source: trunk/third/top/machine/m_irix5.c @ 9084

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