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

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