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

Revision 9084, 18.5 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:  Intel based System V Release 4
5 *
6 * DESCRIPTION:
7 *      System V release 4.0.x for i486
8 *      System V release 4     for Okidata M88100
9 *      System V release 4     for NCR 3000 series OS Rel 1.00 to 2.02
10 *      System V release 4     for NCR 3000 series OS Rel 02.03.00 and above
11 *      and probably other svr4 ports
12 *
13 * LIBS:  -lelf
14 *
15 * AUTHORS:  Andrew Herbert     <andrew@werple.apana.org.au>
16 *           Robert Boucher     <boucher@sofkin.ca>
17 * Ported to System 3000 Release 2.03 by:
18 *           Jeff Janvrin       <jeff.janvrinColumbiaSC.NCR.COM>
19 */
20
21#include "top.h"
22#include "machine.h"
23#include "utils.h"
24#include <stdio.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <dirent.h>
30#include <nlist.h>
31#include <string.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/param.h>
35#include <sys/procfs.h>
36#include <sys/sysinfo.h>
37#include <sys/sysmacros.h>
38#include <sys/vmmeter.h>
39#include <vm/anon.h>
40#include <sys/priocntl.h>
41#include <sys/rtpriocntl.h>
42#include <sys/tspriocntl.h>
43#include <sys/procset.h>
44#include <sys/var.h>
45
46#define UNIX "/stand/unix"
47#define KMEM "/dev/kmem"
48#define PROCFS "/proc"
49#define CPUSTATES       5
50
51#ifndef PRIO_MAX
52#define PRIO_MAX        20
53#endif
54#ifndef PRIO_MIN
55#define PRIO_MIN        -20
56#endif
57
58#ifndef FSCALE
59#define FSHIFT  8               /* bits to right of fixed binary point */
60#define FSCALE  (1<<FSHIFT)
61#endif
62
63#define loaddouble(x) ((double)(x) / FSCALE)
64#define percent_cpu(x) ((double)(x)->pr_cpu / FSCALE)
65#define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
66        ((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
67#define pagetok(size) ctob(size) >> LOG1024
68
69/* definitions for the index in the nlist array */
70#define X_AVENRUN       0
71#define X_MPID          1
72#define X_V             2
73#define X_NPROC         3
74#define X_ANONINFO      4
75#define X_TOTAL         5
76#define X_SYSINFO       6
77
78static struct nlist nlst[] =
79{
80{"avenrun"},                    /* 0 */
81{"mpid"},                       /* 1 */
82{"v"},                  /* 2 */
83{"nproc"},                      /* 3 */
84{"anoninfo"},                   /* 4 */
85{"total"},                      /* 5 */
86{"sysinfo"},                    /* 6 */
87{NULL}
88};
89
90static unsigned long avenrun_offset;
91static unsigned long mpid_offset;
92static unsigned long nproc_offset;
93static unsigned long anoninfo_offset;
94static unsigned long total_offset;
95static unsigned long sysinfo_offset;
96
97/* get_process_info passes back a handle.  This is what it looks like: */
98
99struct handle
100  {
101    struct prpsinfo **next_proc;/* points to next valid proc pointer */
102    int remaining;              /* number of pointers remaining */
103  };
104
105/*
106 *  These definitions control the format of the per-process area
107 */
108
109static char header[] =
110"  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
111/* 0123456   -- field to fill in starts at header+6 */
112#define UNAME_START 6
113#define Proc_format \
114        "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s"
115
116char *state_abbrev[] =
117{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
118
119int process_states[8];
120char *procstatenames[] =
121{
122  "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
123  " starting, ", " on cpu, ", " swapped, ",
124  NULL
125};
126
127int cpu_states[CPUSTATES];
128char *cpustatenames[] =
129{"idle", "user", "kernel", "wait", "swap", NULL};
130
131/* these are for detailing the memory statistics */
132
133int memory_stats[5];
134char *memorynames[] =
135{"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};
136
137static int kmem = -1;
138static int nproc;
139static int bytes;
140static int use_stats = 0;
141static struct prpsinfo *pbase;
142static struct prpsinfo **pref;
143static DIR *procdir;
144
145/* useful externals */
146extern int errno;
147extern char *sys_errlist[];
148extern char *myname;
149extern int check_nlist ();
150extern int getkval ();
151extern void perror ();
152extern void getptable ();
153extern void quit ();
154extern int nlist ();
155
156int
157machine_init (struct statics *statics)
158  {
159    static struct var v;
160
161    /* fill in the statics information */
162    statics->procstate_names = procstatenames;
163    statics->cpustate_names = cpustatenames;
164    statics->memory_names = memorynames;
165
166    /* get the list of symbols we want to access in the kernel */
167    if (nlist (UNIX, nlst))
168      {
169        (void) fprintf (stderr, "Unable to nlist %s\n", UNIX);
170        return (-1);
171      }
172
173    /* make sure they were all found */
174    if (check_nlist (nlst) > 0)
175      return (-1);
176
177    /* open kernel memory */
178    if ((kmem = open (KMEM, O_RDONLY)) == -1)
179      {
180        perror (KMEM);
181        return (-1);
182      }
183
184    /* get the symbol values out of kmem */
185    /* NPROC Tuning parameter for max number of processes */
186    (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name);
187    nproc = v.v_proc;
188
189    /* stash away certain offsets for later use */
190    mpid_offset = nlst[X_MPID].n_value;
191    nproc_offset = nlst[X_NPROC].n_value;
192    avenrun_offset = nlst[X_AVENRUN].n_value;
193    anoninfo_offset = nlst[X_ANONINFO].n_value;
194    total_offset = nlst[X_TOTAL].n_value;
195/* JJ this may need to be changed */
196    sysinfo_offset = nlst[X_SYSINFO].n_value;
197
198    /* allocate space for proc structure array and array of pointers */
199    bytes = nproc * sizeof (struct prpsinfo);
200    pbase = (struct prpsinfo *) malloc (bytes);
201    pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *));
202
203    /* Just in case ... */
204    if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
205      {
206        (void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
207        return (-1);
208      }
209
210    if (!(procdir = opendir (PROCFS)))
211      {
212        (void) fprintf (stderr, "Unable to open %s\n", PROCFS);
213        return (-1);
214      }
215
216    if (chdir (PROCFS))
217      {                         /* handy for later on when we're reading it */
218        (void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
219        return (-1);
220      }
221
222    /* all done! */
223    return (0);
224  }
225
226char *
227format_header (char *uname_field)
228{
229  register char *ptr;
230
231  ptr = header + UNAME_START;
232  while (*uname_field != '\0')
233    *ptr++ = *uname_field++;
234
235  return (header);
236}
237
238void
239get_system_info (struct system_info *si)
240{
241  long avenrun[3];
242  struct sysinfo sysinfo;
243  static struct sysinfo *mpinfo = NULL; /* array, per-processor sysinfo structures. */
244  struct vmtotal total;
245  struct anoninfo anoninfo;
246  static time_t cp_old[CPUSTATES];
247  static time_t cp_diff[CPUSTATES];     /* for cpu state percentages */
248  static int num_cpus;
249  static int fd_cpu = 0;
250  register int i;
251
252  if ( use_stats == 1) {
253    if ( fd_cpu == 0 ) {
254      if ((fd_cpu = open("/stats/cpuinfo", O_RDONLY)) == -1) {
255        (void) fprintf (stderr, "%s: Open of /stats/cpuinfo failed\n", myname);
256        quit(2);
257      }
258      if (read(fd_cpu, &num_cpus, sizeof(int)) != sizeof(int)) {
259        (void) fprintf (stderr, "%s: Read of /stats/cpuinfo failed\n", myname);
260        quit(2);
261      }
262      close(fd_cpu);
263    }
264    if (mpinfo == NULL) {
265      mpinfo = (struct sysinfo *)calloc(num_cpus, sizeof(mpinfo[0]));
266      if (mpinfo == NULL) {
267        (void) fprintf (stderr, "%s: can't allocate space for per-processor sysinfos\n", myname);
268        quit(12);
269      }
270    }
271    /* Read the per cpu sysinfo structures into mpinfo struct. */
272    read_sysinfos(num_cpus, mpinfo);
273    /* Add up all of the percpu sysinfos to get global sysinfo */
274    sysinfo_data(num_cpus, &sysinfo, mpinfo);
275  } else {
276    (void) getkval (sysinfo_offset, &sysinfo, sizeof (struct sysinfo), "sysinfo");
277  }
278
279  /* convert cp_time counts to percentages */
280  (void) percentages (CPUSTATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
281
282  /* get mpid -- process id of last process */
283  (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid),
284                  "mpid");
285
286  /* get load average array */
287  (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun");
288
289  /* convert load averages to doubles */
290  for (i = 0; i < 3; i++)
291    si->load_avg[i] = loaddouble (avenrun[i]);
292
293  /* get total -- systemwide main memory usage structure */
294  (void) getkval (total_offset, (int *) (&total), sizeof (total), "total");
295  /* convert memory stats to Kbytes */
296  memory_stats[0] = pagetok (total.t_rm);
297  memory_stats[1] = pagetok (total.t_arm);
298  memory_stats[2] = pagetok (total.t_free);
299  (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo),
300                  "anoninfo");
301  memory_stats[3] = pagetok (anoninfo.ani_max - anoninfo.ani_free);
302  memory_stats[4] = pagetok (anoninfo.ani_max - anoninfo.ani_resv);
303
304  /* set arrays and strings */
305  si->cpustates = cpu_states;
306  si->memory = memory_stats;
307}
308
309static struct handle handle;
310
311caddr_t
312get_process_info (
313                   struct system_info *si,
314                   struct process_select *sel,
315                   int (*compare) ())
316{
317  register int i;
318  register int total_procs;
319  register int active_procs;
320  register struct prpsinfo **prefp;
321  register struct prpsinfo *pp;
322
323  /* these are copied out of sel for speed */
324  int show_idle;
325  int show_system;
326  int show_uid;
327
328  /* Get current number of processes */
329  (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc");
330
331  /* read all the proc structures */
332  getptable (pbase);
333
334  /* get a pointer to the states summary array */
335  si->procstates = process_states;
336
337  /* set up flags which define what we are going to select */
338  show_idle = sel->idle;
339  show_system = sel->system;
340  show_uid = sel->uid != -1;
341
342  /* count up process states and get pointers to interesting procs */
343  total_procs = 0;
344  active_procs = 0;
345  (void) memset (process_states, 0, sizeof (process_states));
346  prefp = pref;
347
348  for (pp = pbase, i = 0; i < nproc; pp++, i++)
349    {
350      /*
351         *  Place pointers to each valid proc structure in pref[].
352         *  Process slots that are actually in use have a non-zero
353         *  status field.  Processes with SSYS set are system
354         *  processes---these get ignored unless show_sysprocs is set.
355         */
356      if (pp->pr_state != 0 &&
357          (show_system || ((pp->pr_flag & SSYS) == 0)))
358        {
359          total_procs++;
360          process_states[pp->pr_state]++;
361          if ((!pp->pr_zomb) &&
362              (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
363              (!show_uid || pp->pr_uid == (uid_t) sel->uid))
364            {
365              *prefp++ = pp;
366              active_procs++;
367            }
368        }
369    }
370
371  /* if requested, sort the "interesting" processes */
372  if (compare != NULL)
373      qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), compare);
374
375  /* remember active and total counts */
376  si->p_total = total_procs;
377  si->p_active = active_procs;
378
379  /* pass back a handle */
380  handle.next_proc = pref;
381  handle.remaining = active_procs;
382  return ((caddr_t) & handle);
383}
384
385char fmt[MAX_COLS];                     /* static area where result is built */
386
387char *
388format_next_process (
389                      caddr_t handle,
390                      char *(*get_userid) ())
391{
392  register struct prpsinfo *pp;
393  struct handle *hp;
394  register long cputime;
395  register double pctcpu;
396
397  /* find and remember the next proc structure */
398  hp = (struct handle *) handle;
399  pp = *(hp->next_proc++);
400  hp->remaining--;
401
402  /* get the cpu usage and calculate the cpu percentages */
403  cputime = pp->pr_time.tv_sec;
404  pctcpu = percent_cpu (pp);
405
406  /* format this entry */
407  (void) sprintf (fmt,
408                  Proc_format,
409                  pp->pr_pid,
410                  (*get_userid) (pp->pr_uid),
411                  pp->pr_pri - PZERO,
412                  pp->pr_nice - NZERO,
413                  format_k(pagetok (pp->pr_size)),
414                  format_k(pagetok (pp->pr_rssize)),
415                  state_abbrev[pp->pr_state],
416                  format_time(cputime),
417                  (pp->pr_cpu & 0377),
418                  100.0 * pctcpu,
419                  pp->pr_fname);
420
421  /* return the result */
422  return (fmt);
423}
424
425/*
426 * check_nlist(nlst) - checks the nlist to see if any symbols were not
427 *              found.  For every symbol that was not found, a one-line
428 *              message is printed to stderr.  The routine returns the
429 *              number of symbols NOT found.
430 */
431int
432check_nlist (register struct nlist *nlst)
433{
434  register int i;
435  struct stat stat_buf;
436
437  /* check to see if we got ALL the symbols we requested */
438  /* this will write one line to stderr for every symbol not found */
439
440  i = 0;
441  while (nlst->n_name != NULL)
442    {
443      if (nlst->n_type == 0)
444        {
445          if (strcmp("sysinfo", nlst->n_name) == 0)
446            {
447                /* check to see if /stats file system exists. If so,    */
448                /* ignore error.                                        */
449                if ( !((stat("/stats/sysinfo", &stat_buf) == 0) &&
450                  (stat_buf.st_mode & S_IFREG)) )
451                  {
452                    (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
453                    i = 1;
454                  } else {
455                    use_stats = 1;
456                  }
457            } else {
458                       
459              /* this one wasn't found */
460              (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
461              i = 1;
462            }
463        }
464      nlst++;
465    }
466  return (i);
467}
468
469
470/*
471 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
472 *      "offset" is the byte offset into the kernel for the desired value,
473 *      "ptr" points to a buffer into which the value is retrieved,
474 *      "size" is the size of the buffer (and the object to retrieve),
475 *      "refstr" is a reference string used when printing error meessages,
476 *          if "refstr" starts with a '!', then a failure on read will not
477 *          be fatal (this may seem like a silly way to do things, but I
478 *          really didn't want the overhead of another argument).
479 *
480 */
481int
482getkval (
483          unsigned long offset,
484          int *ptr,
485          int size,
486          char *refstr)
487{
488#ifdef MIPS
489  if (lseek (kmem, (long) (offset & 0x7fffffff), 0) == -1)
490#else
491  if (lseek (kmem, (long) offset, 0) == -1)
492#endif
493    {
494      if (*refstr == '!')
495        refstr++;
496      (void) fprintf (stderr, "%s: lseek to %s: %s\n",
497                      myname, refstr, sys_errlist[errno]);
498      quit (22);
499    }
500  if (read (kmem, (char *) ptr, size) == -1)
501    if (*refstr == '!')
502      /* we lost the race with the kernel, process isn't in memory */
503      return (0);
504    else
505      {
506        (void) fprintf (stderr, "%s: reading %s: %s\n",
507                        myname, refstr, sys_errlist[errno]);
508        quit (23);
509      }
510  return (1);
511}
512
513/* comparison routine for qsort */
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, start, 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,                            /* start                */
535  7,                            /* run on a processor   */
536  1                             /* being swapped (WAIT) */
537};
538
539int
540proc_compare (
541               struct prpsinfo **pp1,
542               struct prpsinfo **pp2)
543{
544    register struct prpsinfo *p1;
545    register struct prpsinfo *p2;
546    register long result;
547
548    /* remove one level of indirection */
549    p1 = *pp1;
550    p2 = *pp2;
551
552    /* compare percent cpu (pctcpu) */
553    if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0)
554      {
555        /* use cpticks to break the tie */
556        if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
557          {
558            /* use process state to break the tie */
559            if ((result = (long) (sorted_state[p2->pr_state] -
560                                  sorted_state[p1->pr_state])) == 0)
561              {
562                /* use priority to break the tie */
563                if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
564                  {
565                    /* use resident set size (rssize) to break the tie */
566                    if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
567                      {
568                        /* use total memory to break the tie */
569                        result = (p2->pr_size - p1->pr_size);
570                      }
571                  }
572              }
573          }
574      }
575    return (result);
576  }
577
578/*
579get process table
580*/
581void
582getptable (struct prpsinfo *baseptr)
583{
584  struct prpsinfo *currproc;    /* pointer to current proc structure    */
585  int numprocs = 0;
586  struct dirent *direntp;
587
588  for (rewinddir (procdir); direntp = readdir (procdir);)
589    {
590      int fd;
591
592      if ((fd = open (direntp->d_name, O_RDONLY)) < 0)
593        continue;
594
595      currproc = &baseptr[numprocs];
596      if (ioctl (fd, PIOCPSINFO, currproc) < 0)
597        {
598          (void) close (fd);
599          continue;
600        }
601
602      numprocs++;
603      (void) close (fd);
604    }
605
606  if (nproc != numprocs)
607    nproc = numprocs;
608}
609
610/* return the owner of the specified process, for use in commands.c as we're
611   running setuid root */
612uid_t
613proc_owner (pid_t pid)
614{
615  register struct prpsinfo *p;
616  int i;
617  for (i = 0, p = pbase; i < nproc; i++, p++)
618    if (p->pr_pid == pid)
619      return (p->pr_uid);
620
621  return (-1);
622}
623
624int
625setpriority (int dummy, int who, int niceval)
626{
627  int scale;
628  int prio;
629  pcinfo_t pcinfo;
630  pcparms_t pcparms;
631  tsparms_t *tsparms;
632
633  strcpy (pcinfo.pc_clname, "TS");
634  if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
635    return (-1);
636
637  prio = niceval;
638  if (prio > PRIO_MAX)
639    prio = PRIO_MAX;
640  else if (prio < PRIO_MIN)
641    prio = PRIO_MIN;
642
643  tsparms = (tsparms_t *) pcparms.pc_clparms;
644  scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
645  tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
646  pcparms.pc_cid = pcinfo.pc_cid;
647
648  if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
649    return (-1);
650
651  return (0);
652}
653
654/****************************************************************
655 * read_sysinfos() -                                            *
656 *      Read all of the CPU specific sysinfo sturctures in from *
657 *      the /stats file system.                                 *
658 ****************************************************************/
659read_sysinfos(num_cpus, buf)
660        int num_cpus;
661        struct sysinfo  *buf;
662{
663
664        static int      fd1=0;  /* file descriptor for /stats/sysinfo */
665        int             read_sz;
666
667        /* Open /stats/sysinfo one time only and leave it open */
668        if (fd1==0) {
669                if ((fd1 = open("/stats/sysinfo", O_RDONLY)) == -1)
670                        (void) fprintf (stderr, "%s: Open of /stats/sysinfo failed\n", myname);
671        }
672        /* reset the read pointer to the beginning of the file */
673        if (lseek(fd1, 0L, SEEK_SET) == -1)
674                (void) fprintf (stderr, "%s: lseek to beginning of /stats/sysinfo failed\n", myname);
675        read_sz = num_cpus * sizeof(buf[0]);
676        if (read(fd1, buf, read_sz) != read_sz)
677                (void) fprintf (stderr, "%s: Read of /stats/sysinfo failed\n", myname);
678}
679               
680/****************************************************************
681 * sysinfo_data() -                                             *
682 *      Add up all of the CPU specific sysinfo sturctures to    *
683 *      make the GLOBAL sysinfo.                                *
684 ****************************************************************/
685sysinfo_data(num_cpus, global_si, percpu_si)
686        int num_cpus;
687        struct sysinfo  *global_si;
688        struct sysinfo  *percpu_si;
689{
690        struct sysinfo  *percpu_p;
691        int             cpu, i, *global, *src;
692
693        /* null out the global statistics from last sample */
694        memset(global_si, 0, sizeof(struct sysinfo));
695
696        percpu_p = (struct sysinfo *)percpu_si;
697        for(cpu = 0; cpu < num_cpus; cpu++) {
698                global = (int *)global_si;
699                src = (int *)percpu_p;
700
701                /* assume sysinfo ends on an int boundary */
702                /* Currently, all of the struct sysinfo members are the same
703                 * size as an int. If that changes, we may not be able to
704                 * do this. But this should be safe.
705                 */
706                for(i=0; i<sizeof(struct sysinfo)/sizeof(int); i++) {
707                        *global++ += *src++;
708                }
709                percpu_p++;
710        }
711}
Note: See TracBrowser for help on using the repository browser.