home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / machine / m_svr4.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-06  |  14.9 KB  |  602 lines

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