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

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