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
101static int pagesize;
102#define pagetok(size) ((size)*pagesize)
103
104static int numcpus;
105
106/*
107 *  These definitions control the format of the per-process area
108 */
109
110static 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 */
119char *state_abbrev[] =
120{"", "sleep", "run\0\0\0", "zombie", "stop", "idle", "", "swap"};
121
122int process_states[8];
123char *procstatenames[] = {
124    "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
125    " idle, ", "", " swapped, ",
126    NULL
127};
128
129/* these are for detailing the cpu states */
130int cpu_states[CPUSTATES];
131char *cpustatenames[] = {
132    "idle", "usr", "ker", "wait", "swp", "intr",
133    NULL
134};
135
136/* these are for detailing the memory statistics */
137
138long memory_stats[5];
139char *memorynames[] = {
140    "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
141};
142
143/* useful externals */
144extern int errno;
145extern char *myname;
146extern char *sys_errlist[];
147extern char *format_k();
148extern char *format_time();
149extern long percentages();
150
151/* forward references */
152int 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
161static 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};
172static unsigned long avenrun_offset;
173static unsigned long nproc_offset;
174static unsigned long freemem_offset;
175static unsigned long maxmem_offset;
176static unsigned long availrmem_offset;
177static unsigned long mpid_offset;
178double load[3];
179char fmt[MAX_COLS];
180static int kmem;
181static int nproc;
182static int bytes;
183static struct prpsinfo *pbase;
184static struct prpsinfo **pref;
185static DIR *procdir;
186
187/* get_process_info passes back a handle.  This is what it looks like: */
188struct handle  {
189  struct prpsinfo **next_proc;/* points to next valid proc pointer */
190  int remaining;	      /* number of pointers remaining */
191};
192
193static struct handle handle;
194void 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 */
201struct oldproc
202  {
203    pid_t oldpid;
204    double oldtime;
205    double oldpct;
206  };
207static int oldprocs;			/* size of table */
208static struct oldproc *oldbase;
209#define HASH(x) ((x << 1) % oldprocs)
210#define PRPSINFOSIZE (sizeof(struct prpsinfo))
211
212int 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
286char *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
300void 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
365caddr_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
432char *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
489int 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
527unsigned 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
539int 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. */
573int 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
593int 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 */
616void 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