source: trunk/third/top/machine/m_irix62.c @ 16185

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