
/*
 *	topcpu.c
 *
 *	QNX 4
 *
 *	(C) Copyright 1991, 1992, 1996 by Robert Krten, all rights reserved.
 *
 *	This module is responsible for the TOP CPU display
 *
 *	1991 09 20	R. Krten		created
 *	1991 09 22	R. Krten		added histogram mode
 *	1992 01 05	R. Krten		released for free
 *	1996 11 01	R. Krten		changed "min idle" priority to 5 for screen savers under Photon
*/

#include <stdio.h>
#include <time.h>
#include "sysmon.h"

static	int		compareTOPCPU (TopcpuT **, TopcpuT **);
static	void	fetchSortTOPCPU (void);

#define	MaxHashEntries			16		/* hash is lowest four bits */

extern struct _osinfo osData;
static struct _psinfo psData;

static char procname [17];
static PrioTotalsT prioTotals;

static	int processCount;
static	int last80 [80];
static	long currentHistogramValue;
static	long histogramTotal;
static	long histogramSamples;

static	TopcpuT	topcpuBackbone [MaxHashEntries];
static	FILE *File;
static TopcpuT **processList;


void
doTOPCPU (int pass)
{
  cpuBackbone (pass);
  removeDeadCpuEntries (pass);
  fetchSortTOPCPU ();
  calcDeltaTOPCPU ();
}


int
getProcessCpu(char *name)
{
  int i;

  if (!processList || processCount == 0) return -1;

  if (!name) return -1;

  if (!strcmp(name, "idle"))
    return prioTotals.cpuprio_totals[0];

  for (i = 0; i < processCount; i++) {
    if (!strcmp(name, processList[i]->name)) {
      printf("Found %s! Percentage = %d\n", name, processList[i]->percentage);
      return processList[i]->percentage;
    }
  }

  return -1;
}

void
initializeCpuData()
{
  int i;

  /*
   *	clear out the top CPU backbone for the hash table, and initialize
   */
  processList = NULL;
  for (i = 0; i < MaxHashEntries; i++) {
    topcpuBackbone [i].next = NULL;
  }
  processCount = 0;
  for (i = 0; i < 80; i++) {
    last80 [i] = 0;
  }
}

void
cpuBackbone (pass)
int		pass;
{
	pid_t	currentPID;
	TopcpuT	*ptr;
	TopcpuT	*prev;

	currentPID = 1;
	for (;;) {
		currentPID = qnx_psinfo (PROC_PID, currentPID, &psData, 0, NULL);
		if (currentPID == -1) {
			if (errno == ESRCH || errno == EINVAL) {
				return;				/* done, go away */
			}
		}
		if ((psData.flags & (_PPF_MID | _PPF_VID)) == 0) {
			prev = &topcpuBackbone [currentPID & 0x0f];
			for (ptr = prev -> next; ptr != NULL; ptr = (prev = ptr) -> next) {
				if (ptr -> pid == currentPID) {
					ptr -> prio = psData.priority;
					ptr -> delta = ptr -> total;
					ptr -> total = psData.un.proc.times.tms_cutime;
					ptr -> total+= psData.un.proc.times.tms_cstime;
					ptr -> total+= psData.un.proc.times.tms_stime;
					ptr -> total+= psData.un.proc.times.tms_utime;
					ptr -> delta = ptr -> total - ptr -> delta;
					break;
				}
			}
			if (ptr == NULL) {
				if ((prev -> next = (TopcpuT *) malloc (TopcpuS)) == NULL) {
					fatal ("Couldn't allocate memory for new topcpu hash entry");
				}
				ptr = prev -> next;		/* get to new entry */
				ptr -> pid = currentPID;
				ptr -> prio = psData.priority;
				fixName (ptr -> name, psData.un.proc.name);
				ptr -> total = psData.un.proc.times.tms_cutime;
				ptr -> total+= psData.un.proc.times.tms_cstime;
				ptr -> total+= psData.un.proc.times.tms_stime;
				ptr -> total+= psData.un.proc.times.tms_utime;
				ptr -> delta = 0;	/* new processes don't have any time! */
				ptr -> percentage = 0;
				ptr -> next = NULL;
				processCount++;
#if 0
				printf("adding pid %d, count %d\n",
				       currentPID, processCount);
#endif
			}
			ptr -> pass = pass;
		}
		currentPID++;
	}
}

void
removeDeadCpuEntries (pass)
int		pass;
{
	int		i;
	TopcpuT	*ptr;
	TopcpuT	*prev;

	for (i = 0; i < MaxHashEntries; i++) {
		prev = &topcpuBackbone [i];
		ptr = prev -> next;
		while (ptr != NULL) {
			if (!*ptr -> name) {
				getName (ptr -> name, ptr -> pid);
			}
			if (ptr -> pass != pass) {
#if 0
			  printf("removing %d\n", ptr->pid);
#endif
				prev -> next = ptr -> next;
				free (ptr);
				processCount--;
			} else {
				prev = ptr;
			}
			ptr = prev -> next;
		}
	}
}

static int
compareTOPCPU (a, b)
TopcpuT **a;
TopcpuT	**b;
{
	if ((*a) -> delta > (*b) -> delta) {
		return (-1);
	} else if ((*a) -> delta < (*b) -> delta) {
		return (1);
	} else {
		return (0);
	}
}

static void
fetchSortTOPCPU ()
{
	int		i;
	int		p;
	TopcpuT *ptr;

	if (processList) free(processList);
	if ((processList = (TopcpuT **) malloc (sizeof (TopcpuT *) * processCount)) == NULL) {
		fatal ("Couldn't allocate memory for new processList");
	}
	p = 0;
	for (i = 0; i < MaxHashEntries; i++) {
		ptr = topcpuBackbone [i].next;
		while (ptr != NULL) {
			processList [p++] = ptr;
			ptr = ptr -> next;
		}
	}
	qsort (processList, p, sizeof (TopcpuT *), compareTOPCPU);
}

/* Return a copy of the latest priority-based CPU usage in the
   provided PrioTotalsT object */
void
cpuPrioTotals(PrioTotalsT *pt)
{
  memcpy(pt->cpuprio_totals, &prioTotals.cpuprio_totals, sizeof(prioTotals.cpuprio_totals));
}


void
calcDeltaTOPCPU ()
{
 	int		i;
	clock_t	grandTotal;
	int		percentage;
	struct timespec now;
	double timenow;
	PrioTotalsT pt;

	fetchSortTOPCPU ();
	grandTotal = 0;
	for (i = 0; i < processCount; i++) {
		grandTotal += processList [i] -> delta;
	}

	/* Re-initialize the priority totals object */
	for (i = 0; i < NumberOfPriorities; i++) {
	  prioTotals.cpuprio_totals[i] = 0;
	}

	/* For every process in the list, grab the most recent CPU usage data */
	for (i = 0; (i < processCount); i++) {
	  if (!processList [i] -> delta) {
	    break;
	  }
	  percentage = (int) ((float) processList [i] -> delta * 100. / (float) grandTotal);
	  processList[i]->percentage = percentage;
	  /* Keep a running total for the priority */
	  prioTotals.cpuprio_totals[processList[i]->prio] += percentage;
	  
	}
#if 0
	free(processList);
	printf("topcpu: %d\n", prioTotals.prio_totals[0]);
#endif
}
