xref: /netbsd/external/bsd/top/dist/machine/m_irix5.c (revision 6550d01e)
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@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