Home | History | Annotate | Line # | Download | only in machine
      1 /*
      2  * Copyright (c) 1984 through 2008, William LeFebvre
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *
     16  *     * Neither the name of William LeFebvre nor the names of other
     17  * contributors may be used to endorse or promote products derived from
     18  * this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * top - a top users display for Unix
     35  *
     36  * SYNOPSIS:  any uniprocessor, 32 bit SGI machine running IRIX 5.3
     37  *
     38  * DESCRIPTION:
     39  * This is the machine-dependent module for IRIX 5.3.
     40  * It has been tested on Indys running 5.3 and Indigos running 5.3XFS
     41  *
     42  * LIBS: -lmld
     43  * CFLAGS: -DHAVE_GETOPT
     44  *
     45  * AUTHOR: Sandeep Cariapa <cariapa (at) sgi.com>
     46  * This is not a supported product of Silicon Graphics, Inc.
     47  * Please do not call SGI for support.
     48  *
     49  */
     50 
     51 #define _KMEMUSER
     52 
     53 #include "config.h"
     54 
     55 #include <sys/types.h>
     56 #include <sys/time.h>
     57 #include <sys/stat.h>
     58 #include <sys/swap.h>
     59 #include <sys/proc.h>
     60 #include <sys/procfs.h>
     61 #include <sys/sysinfo.h>
     62 #include <sys/sysmp.h>
     63 #include <paths.h>
     64 #include <dirent.h>
     65 #include <stdio.h>
     66 #include <nlist.h>
     67 #include <unistd.h>
     68 #include <errno.h>
     69 #include <fcntl.h>
     70 #include "top.h"
     71 #include "machine.h"
     72 
     73 #ifdef IRIX64
     74 #define nlist nlist64
     75 #define lseek lseek64
     76 #define off_t off64_t
     77 #endif
     78 
     79 #define UNIX	"/unix"
     80 #define KMEM	"/dev/kmem"
     81 #define CPUSTATES 6
     82 
     83 #ifndef FSCALE
     84 #define FSHIFT  8		/* bits to right of fixed binary point */
     85 #define FSCALE  (1<<FSHIFT)
     86 #endif /* FSCALE */
     87 
     88 #ifdef FIXED_LOADAVG
     89   typedef long load_avg;
     90 # define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
     91 # define intload(i) ((int)((i) * FIXED_LOADAVG))
     92 #else
     93   typedef double load_avg;
     94 # define loaddouble(la) (la)
     95 # define intload(i) ((double)(i))
     96 #endif
     97 
     98 #define percent_cpu(pp) (*(double *)pp->pr_fill)
     99 #define weighted_cpu(pp) (*(double *)&pp->pr_fill[2])
    100 
    101 static int pagesize;
    102 #define pagetok(size) ((size)*pagesize)
    103 
    104 static int numcpus;
    105 
    106 /*
    107  *  These definitions control the format of the per-process area
    108  */
    109 
    110 static char header[] =
    111   "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
    112 /* 0123456   -- field to fill in starts at header+6 */
    113 #define UNAME_START 6
    114 
    115 #define Proc_format \
    116 	"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.16s"
    117 
    118 /* these are for detailing the process states */
    119 char *state_abbrev[] =
    120 {"", "sleep", "run\0\0\0", "zombie", "stop", "idle", "", "swap"};
    121 
    122 int process_states[8];
    123 char *procstatenames[] = {
    124     "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
    125     " idle, ", "", " swapped, ",
    126     NULL
    127 };
    128 
    129 /* these are for detailing the cpu states */
    130 int cpu_states[CPUSTATES];
    131 char *cpustatenames[] = {
    132     "idle", "usr", "ker", "wait", "swp", "intr",
    133     NULL
    134 };
    135 
    136 /* these are for detailing the memory statistics */
    137 
    138 long memory_stats[5];
    139 char *memorynames[] = {
    140     "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
    141 };
    142 
    143 /* useful externals */
    144 extern int errno;
    145 extern char *myname;
    146 extern char *sys_errlist[];
    147 extern char *format_k();
    148 extern char *format_time();
    149 extern long percentages();
    150 
    151 /* forward references */
    152 int proc_compare (void *pp1, void *pp2);
    153 
    154 #define X_AVENRUN	0
    155 #define X_NPROC		1
    156 #define X_FREEMEM	2
    157 #define X_MAXMEM	3
    158 #define X_AVAILRMEM     4
    159 #define X_MPID		5
    160 
    161 static struct nlist nlst[] = {
    162 { "avenrun" },		/* 0. Array containing the 3 load averages. */
    163 { "nproc" },		/* 1. Kernel parameter: Max number of processes. */
    164 { "freemem" },		/* 2. Amount of free memory in system. */
    165 { "maxmem" },		/* 3. Maximum amount of memory usable by system. */
    166 { "availrmem" },        /* 4. Available real memory. */
    167 #ifndef IRIX64
    168 { "mpid" },		/* 5. PID of last process. */
    169 #endif
    170 { 0 }
    171 };
    172 static unsigned long avenrun_offset;
    173 static unsigned long nproc_offset;
    174 static unsigned long freemem_offset;
    175 static unsigned long maxmem_offset;
    176 static unsigned long availrmem_offset;
    177 static unsigned long mpid_offset;
    178 double load[3];
    179 char fmt[MAX_COLS];
    180 static int kmem;
    181 static int nproc;
    182 static int bytes;
    183 static struct prpsinfo *pbase;
    184 static struct prpsinfo **pref;
    185 static DIR *procdir;
    186 
    187 /* get_process_info passes back a handle.  This is what it looks like: */
    188 struct handle  {
    189   struct prpsinfo **next_proc;/* points to next valid proc pointer */
    190   int remaining;	      /* number of pointers remaining */
    191 };
    192 
    193 static struct handle handle;
    194 void getptable();
    195 
    196 /*
    197  * Structure for keeping track of CPU times from last time around
    198  * the program.  We keep these things in a hash table, which is
    199  * recreated at every cycle.
    200  */
    201 struct oldproc
    202   {
    203     pid_t oldpid;
    204     double oldtime;
    205     double oldpct;
    206   };
    207 static int oldprocs;			/* size of table */
    208 static struct oldproc *oldbase;
    209 #define HASH(x) ((x << 1) % oldprocs)
    210 #define PRPSINFOSIZE (sizeof(struct prpsinfo))
    211 
    212 int machine_init(statics)
    213      struct statics *statics;
    214 {
    215   struct oldproc *op, *endbase;
    216 
    217   if ((kmem = open(KMEM, O_RDONLY)) == -1) {
    218     perror(KMEM);
    219     return(-1);
    220   }
    221 
    222   /* get the list of symbols we want to access in the kernel */
    223   (void) nlist(UNIX, nlst);
    224   if (nlst[0].n_type == 0) {
    225     fprintf(stderr, "%s: nlist failed\n", myname);
    226     return(-1);
    227   }
    228 
    229   /* Check if we got all of 'em. */
    230   if (check_nlist(nlst) > 0) {
    231       return(-1);
    232     }
    233   avenrun_offset = nlst[X_AVENRUN].n_value;
    234   nproc_offset = nlst[X_NPROC].n_value;
    235   freemem_offset = nlst[X_FREEMEM].n_value;
    236   maxmem_offset = nlst[X_MAXMEM].n_value;
    237   availrmem_offset = nlst[X_AVAILRMEM].n_value;
    238 #ifndef IRIX64
    239    mpid_offset = nlst[X_MPID].n_value;
    240 #endif
    241 
    242   /* Got to do this first so that we can map real estate for the
    243      process array. */
    244   (void) getkval(nproc_offset, (int *) (&nproc), sizeof(nproc), "nproc");
    245 
    246   /* allocate space for proc structure array and array of pointers */
    247   bytes = nproc * sizeof (struct prpsinfo);
    248   pbase = (struct prpsinfo *) malloc (bytes);
    249   pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *));
    250   oldbase = (struct oldproc *) malloc (2 * nproc * sizeof (struct oldproc));
    251 
    252   /* Just in case ... */
    253   if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL ||
    254       oldbase == (struct oldproc *)NULL) {
    255     (void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
    256     return (-1);
    257   }
    258 
    259   oldprocs = 2 * nproc;
    260   endbase = oldbase + oldprocs;
    261   for (op = oldbase; op < endbase; op++) {
    262     op->oldpid = -1;
    263   }
    264 
    265   if (!(procdir = opendir (_PATH_PROCFSPI))) {
    266     (void) fprintf (stderr, "Unable to open %s\n", _PATH_PROCFSPI);
    267     return (-1);
    268   }
    269 
    270   if (chdir (_PATH_PROCFSPI)) {
    271     /* handy for later on when we're reading it */
    272     (void) fprintf (stderr, "Unable to chdir to %s\n", _PATH_PROCFSPI);
    273     return (-1);
    274   }
    275 
    276   statics->procstate_names = procstatenames;
    277   statics->cpustate_names = cpustatenames;
    278   statics->memory_names = memorynames;
    279 
    280   pagesize = getpagesize()/1024;
    281 
    282   /* all done! */
    283   return(0);
    284 }
    285 
    286 char *format_header(uname_field)
    287      register char *uname_field;
    288 
    289 {
    290   register char *ptr;
    291 
    292   ptr = header + UNAME_START;
    293   while (*uname_field != '\0') {
    294     *ptr++ = *uname_field++;
    295   }
    296 
    297   return(header);
    298 }
    299 
    300 void get_system_info(si)
    301      struct system_info *si;
    302 
    303 {
    304   register int i;
    305   int avenrun[3];
    306   static int freemem;
    307   static int maxmem;
    308   static int availrmem;
    309   struct sysinfo sysinfo;
    310   static long cp_new[CPUSTATES];
    311   static long cp_old[CPUSTATES];
    312   static long cp_diff[CPUSTATES]; /* for cpu state percentages */
    313   off_t  fswap;          /* current free swap in blocks */
    314   off_t  tswap;          /* total swap in blocks */
    315 
    316   (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun), "avenrun");
    317   for (i = 0; i < 3; i++) {
    318     si->load_avg[i] = loaddouble (avenrun[i]);
    319     si->load_avg[i] = si->load_avg[i]/1024.0;
    320   }
    321 
    322   (void) getkval(freemem_offset, (int *) (&freemem), sizeof(freemem),
    323 "freemem");
    324   (void) getkval(maxmem_offset, (int *) (&maxmem), sizeof(maxmem), "maxmem");
    325   (void) getkval(availrmem_offset, (int *) (&availrmem), sizeof(availrmem),
    326 "availrmem");
    327 #ifdef IRIX64
    328   si->last_pid = 0;
    329 #else
    330   (void) getkval(mpid_offset, &(si->last_pid), sizeof (si->last_pid), "mpid");
    331 #endif
    332   swapctl(SC_GETFREESWAP, &fswap);
    333   swapctl(SC_GETSWAPTOT, &tswap);
    334   memory_stats[0] = pagetok(maxmem);
    335   memory_stats[1] = pagetok(availrmem);
    336   memory_stats[2] = pagetok(freemem);
    337   memory_stats[3] = tswap / 2;
    338   memory_stats[4] = fswap / 2;
    339 
    340   /* use sysmp() to get current sysinfo usage. Can run into all kinds of
    341      problems if you try to nlist this kernel variable. */
    342   if (sysmp(MP_SAGET, MPSA_SINFO, &sysinfo, sizeof(struct sysinfo)) == -1) {
    343     perror("sysmp");
    344     return;
    345   }
    346   /* copy sysinfo.cpu to an array of longs, as expected by percentages() */
    347   for (i = 0; i < CPUSTATES; i++) {
    348     cp_new[i] = sysinfo.cpu[i];
    349   }
    350   (void) percentages (CPUSTATES, cpu_states, cp_new, cp_old, cp_diff);
    351 
    352   si->cpustates = cpu_states;
    353   si->memory = memory_stats;
    354 
    355   numcpus = sysmp(MP_NPROCS);
    356 
    357   /* add a slash to the "run" state abbreviation */
    358   if (numcpus > 1) {
    359     state_abbrev[SRUN][3] = '/';
    360   }
    361 
    362   return;
    363 }
    364 
    365 caddr_t get_process_info(si, sel, x)
    366      struct system_info *si;
    367      struct process_select *sel;
    368      int x;
    369 {
    370   register int i;
    371   register int total_procs;
    372   register int active_procs;
    373   register struct prpsinfo **prefp;
    374   register struct prpsinfo *pp;
    375 
    376   /* these are copied out of sel for speed */
    377   int show_idle;
    378   int show_system;
    379   int show_uid;
    380 
    381   /* read all the proc structures */
    382   getptable (pbase);
    383 
    384   /* get a pointer to the states summary array */
    385   si->procstates = process_states;
    386 
    387   /* set up flags which define what we are going to select */
    388   show_idle = sel->idle;
    389   show_system = sel->system;
    390   show_uid = sel->uid != -1;
    391 
    392   /* count up process states and get pointers to interesting procs */
    393   total_procs = 0;
    394   active_procs = 0;
    395   (void) memset (process_states, 0, sizeof (process_states));
    396   prefp = pref;
    397 
    398   for (pp = pbase, i = 0; i < nproc; pp++, i++)    {
    399     /*
    400      *  Place pointers to each valid proc structure in pref[].
    401      *  Process slots that are actually in use have a non-zero
    402      *  status field.  Processes with SSYS set are system
    403      *  processes---these get ignored unless show_system is set.
    404      */
    405     if (pp->pr_state != 0 &&
    406 	(show_system || ((pp->pr_flag & SSYS) == 0))) {
    407       total_procs++;
    408       process_states[pp->pr_state]++;
    409       if ((!pp->pr_zomb) &&
    410 	  (show_idle || (pp->pr_state == SRUN)) &&
    411 	  (!show_uid || pp->pr_uid == (uid_t) sel->uid))  {
    412 	*prefp++ = pp;
    413 	active_procs++;
    414       }
    415     }
    416   }
    417 
    418   /* if requested, sort the "interesting" processes */
    419   if (compare != NULL)
    420     qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), proc_compare);
    421 
    422   /* remember active and total counts */
    423   si->p_total = total_procs;
    424   si->p_active = active_procs;
    425 
    426   /* pass back a handle */
    427   handle.next_proc = pref;
    428   handle.remaining = active_procs;
    429   return((caddr_t)&handle);
    430 }
    431 
    432 char *format_next_process(handle, get_userid)
    433      caddr_t handle;
    434      char *(*get_userid)();
    435 
    436 {
    437   register struct prpsinfo *pp;
    438   struct handle *hp;
    439   register long cputime;
    440   register double pctcpu;
    441 
    442   /* find and remember the next proc structure */
    443   hp = (struct handle *) handle;
    444   pp = *(hp->next_proc++);
    445   hp->remaining--;
    446 
    447   /* get the cpu usage and calculate the cpu percentages */
    448   cputime = pp->pr_time.tv_sec;
    449   pctcpu = percent_cpu (pp);
    450 
    451   if (numcpus > 1) {
    452 	if (pp->pr_sonproc < 0)
    453 		state_abbrev[SRUN][4] = '*';
    454 	else
    455 		state_abbrev[SRUN][4] = pp->pr_sonproc + '0';
    456   }
    457 
    458   /* format this entry */
    459   sprintf (fmt,
    460 	   Proc_format,
    461 	   pp->pr_pid,
    462 	   (*get_userid) (pp->pr_uid),
    463 	   pp->pr_pri - PZERO,
    464 	   pp->pr_nice - NZERO,
    465 	   format_k(pagetok(pp->pr_size)),
    466 	   format_k(pagetok(pp->pr_rssize)),
    467 	   state_abbrev[pp->pr_state],
    468 	   format_time(cputime),
    469 	   weighted_cpu (pp),
    470 	   pctcpu,
    471 	   pp->pr_fname);
    472 
    473   /* return the result */
    474     return(fmt);
    475 }
    476 
    477 /*
    478  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
    479  *	"offset" is the byte offset into the kernel for the desired value,
    480  *  	"ptr" points to a buffer into which the value is retrieved,
    481  *  	"size" is the size of the buffer (and the object to retrieve),
    482  *  	"refstr" is a reference string used when printing error meessages,
    483  *	    if "refstr" starts with a '!', then a failure on read will not
    484  *  	    be fatal (this may seem like a silly way to do things, but I
    485  *  	    really didn't want the overhead of another argument).
    486  *
    487  */
    488 
    489 int getkval(offset, ptr, size, refstr)
    490      off_t offset;
    491      int *ptr;
    492      int size;
    493      char *refstr;
    494 
    495 {
    496   if (lseek(kmem, offset, SEEK_SET) == -1) {
    497     if (*refstr == '!')
    498       refstr++;
    499     (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
    500 		   refstr, strerror(errno));
    501     quit(0);
    502   }
    503   if (read(kmem, (char *) ptr, size) == -1) {
    504     if (*refstr == '!')
    505       return(0);
    506     else {
    507       (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
    508 		     refstr, strerror(errno));
    509       quit(0);
    510     }
    511   }
    512   return(1);
    513 }
    514 
    515 /*
    516  *  proc_compare - comparison function for "qsort"
    517  *	Compares the resource consumption of two processes using five
    518  *  	distinct keys.  The keys (in descending order of importance) are:
    519  *  	percent cpu, cpu ticks, state, resident set size, total virtual
    520  *  	memory usage.  The process states are ordered as follows (from least
    521  *  	to most important):  WAIT, zombie, sleep, stop, idle, run.  The
    522  *  	array declaration below maps a process state index into a number
    523  *  	that reflects this ordering.
    524  */
    525 
    526 
    527 unsigned char sorted_state[] =
    528 {
    529   0,				/* not used		*/
    530   3,				/* sleep		*/
    531   6,				/* run			*/
    532   2,				/* zombie		*/
    533   4,				/* stop			*/
    534   5,				/* idle 		*/
    535   0,				/* not used             */
    536   1				/* being swapped (WAIT)	*/
    537 };
    538 
    539 int proc_compare (pp1, pp2)
    540      void *pp1;
    541      void *pp2;
    542 {
    543   register struct prpsinfo *p1;
    544   register struct prpsinfo *p2;
    545   register long result;
    546 
    547   /* remove one level of indirection */
    548   p1 = *(struct prpsinfo **)pp1;
    549   p2 = *(struct prpsinfo **)pp2;
    550 
    551   /* compare percent cpu (pctcpu) */
    552   if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0) {
    553     /* use cpticks to break the tie */
    554     if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0) {
    555       /* use process state to break the tie */
    556       if ((result = (long) (sorted_state[p2->pr_state] -
    557 			    sorted_state[p1->pr_state])) == 0) {
    558 	/* use priority to break the tie */
    559 	if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)  {
    560 	  /* use resident set size (rssize) to break the tie */
    561 	  if ((result = p2->pr_rssize - p1->pr_rssize) == 0)  {
    562 	    /* use total memory to break the tie */
    563 	    result = (p2->pr_size - p1->pr_size);
    564 	  }
    565 	}
    566       }
    567     }
    568   }
    569   return (result);
    570 }
    571 
    572 /* return the owner of the specified process. */
    573 int proc_owner (pid)
    574      int pid;
    575 {
    576   register struct prpsinfo *p;
    577   int i;
    578 
    579   for (i = 0, p = pbase; i < nproc; i++, p++)
    580     if (p->pr_pid == (oid_t)pid)
    581       return ((int)p->pr_uid);
    582 
    583   return (-1);
    584 }
    585 
    586 /*
    587  * check_nlist(nlst) - checks the nlist to see if any symbols were not
    588  *		found.  For every symbol that was not found, a one-line
    589  *		message is printed to stderr.  The routine returns the
    590  *		number of symbols NOT found.
    591  */
    592 
    593 int check_nlist(nlst)
    594      register struct nlist *nlst;
    595 
    596 {
    597   register int i;
    598 
    599   /* check to see if we got ALL the symbols we requested */
    600   /* this will write one line to stderr for every symbol not found */
    601 
    602   i = 0;
    603   while (nlst->n_name != NULL)   {
    604       if (nlst->n_type == 0) {
    605 	  /* this one wasn't found */
    606 	  fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
    607 	  i = 1;
    608 	}
    609       nlst++;
    610     }
    611 
    612   return(i);
    613 }
    614 
    615 /* get process table */
    616 void getptable (baseptr)
    617      struct prpsinfo *baseptr;
    618 {
    619   struct prpsinfo *currproc;	/* pointer to current proc structure	*/
    620   int numprocs = 0;
    621   int i;
    622   struct dirent *directp;
    623   struct oldproc *op;
    624   static struct timeval lasttime =
    625   {0L, 0L};
    626   struct timeval thistime;
    627   struct timezone thiszone;
    628   double timediff;
    629   double alpha, beta;
    630   struct oldproc *endbase;
    631 
    632   gettimeofday (&thistime, &thiszone);
    633 
    634   /*
    635    * To avoid divides, we keep times in nanoseconds.  This is
    636    * scaled by 1e7 rather than 1e9 so that when we divide we
    637    * get percent.
    638    */
    639   if (lasttime.tv_sec)
    640     timediff = ((double) thistime.tv_sec * 1.0e7 +
    641 		((double) thistime.tv_usec * 10.0)) -
    642       ((double) lasttime.tv_sec * 1.0e7 +
    643        ((double) lasttime.tv_usec * 10.0));
    644   else
    645     timediff = 1.0e7;
    646 
    647   /*
    648      * constants for exponential average.  avg = alpha * new + beta * avg
    649      * The goal is 50% decay in 30 sec.  However if the sample period
    650      * is greater than 30 sec, there's not a lot we can do.
    651      */
    652   if (timediff < 30.0e7)
    653     {
    654       alpha = 0.5 * (timediff / 30.0e7);
    655       beta = 1.0 - alpha;
    656     }
    657   else
    658     {
    659       alpha = 0.5;
    660       beta = 0.5;
    661     }
    662 
    663   endbase = oldbase + oldprocs;
    664   currproc = baseptr;
    665 
    666 
    667   for (rewinddir (procdir); directp = readdir (procdir);)
    668     {
    669       int fd;
    670 
    671       if ((fd = open (directp->d_name, O_RDONLY)) < 0)
    672 	continue;
    673 
    674       currproc = &baseptr[numprocs];
    675       if (ioctl (fd, PIOCPSINFO, currproc) < 0)
    676 	{
    677 	  (void) close (fd);
    678 	  continue;
    679 	}
    680 
    681       /*
    682        * SVr4 doesn't keep track of CPU% in the kernel, so we have
    683        * to do our own.  See if we've heard of this process before.
    684        * If so, compute % based on CPU since last time.
    685        */
    686       op = oldbase + HASH (currproc->pr_pid);
    687       while (1)
    688 	{
    689 	  if (op->oldpid == -1)	/* not there */
    690 	    break;
    691 	  if (op->oldpid == currproc->pr_pid)
    692 	    {			/* found old data */
    693 	      percent_cpu (currproc) =
    694 		((currproc->pr_time.tv_sec * 1.0e9 +
    695 		  currproc->pr_time.tv_nsec)
    696 		 - op->oldtime) / timediff;
    697 	      weighted_cpu (currproc) =
    698 		op->oldpct * beta + percent_cpu (currproc) * alpha;
    699 
    700 	      break;
    701 	    }
    702 	  op++;			/* try next entry in hash table */
    703 	  if (op == endbase)	/* table wrapped around */
    704 	    op = oldbase;
    705 	}
    706 
    707       /* Otherwise, it's new, so use all of its CPU time */
    708       if (op->oldpid == -1)
    709 	{
    710 	  if (lasttime.tv_sec)
    711 	    {
    712 	      percent_cpu (currproc) =
    713 		(currproc->pr_time.tv_sec * 1.0e9 +
    714 		 currproc->pr_time.tv_nsec) / timediff;
    715 	      weighted_cpu (currproc) =
    716 		percent_cpu (currproc);
    717 	    }
    718 	  else
    719 	    {			/* first screen -- no difference is possible */
    720 	      percent_cpu (currproc) = 0.0;
    721 	      weighted_cpu (currproc) = 0.0;
    722 	    }
    723 	}
    724 
    725       numprocs++;
    726       (void) close (fd);
    727     }
    728 
    729   if (nproc != numprocs)
    730     nproc = numprocs;
    731 
    732   /*
    733    * Save current CPU time for next time around
    734    * For the moment recreate the hash table each time, as the code
    735    * is easier that way.
    736    */
    737   oldprocs = 2 * nproc;
    738   endbase = oldbase + oldprocs;
    739   for (op = oldbase; op < endbase; op++)
    740     op->oldpid = -1;
    741   for (i = 0, currproc = baseptr;
    742        i < nproc;
    743      i++, currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE))
    744     {
    745       /* find an empty spot */
    746       op = oldbase + HASH (currproc->pr_pid);
    747       while (1)
    748 	{
    749 	  if (op->oldpid == -1)
    750 	    break;
    751 	  op++;
    752 	  if (op == endbase)
    753 	    op = oldbase;
    754 	}
    755       op->oldpid = currproc->pr_pid;
    756       op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
    757 		     currproc->pr_time.tv_nsec);
    758       op->oldpct = weighted_cpu (currproc);
    759     }
    760   lasttime = thistime;
    761 
    762 }
    763 
    764