home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / machine / m_svr42.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-03  |  14.8 KB  |  599 lines

  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.  
  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 long percentages ();
  144. extern int check_nlist ();
  145. extern int getkval ();
  146. extern void perror ();
  147. extern void getptable ();
  148. extern void quit ();
  149. extern int nlist ();
  150.  
  151. int
  152. machine_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.  
  220. char *
  221. format_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.  
  232. void
  233. get_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.  
  275. static struct handle handle;
  276.  
  277. caddr_t
  278. get_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.  
  351. char fmt[MAX_COLS];            /* static area where result is built */
  352.  
  353. char *
  354. format_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.  */
  397. int
  398. check_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.  */
  431. int
  432. getkval (
  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.  
  473. unsigned 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.  
  485. int
  486. proc_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. /*
  525. get process table
  526. */
  527. void
  528. getptable (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 */
  558. uid_t
  559. proc_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.  
  570. int
  571. setpriority (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. }
  599.