home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / machine / m_freebsd20.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-03  |  21.3 KB  |  897 lines

  1. /*
  2.  * top - a top users display for Unix
  3.  *
  4.  * SYNOPSIS:  For a FreeBSD-2.0 (4.4BSD) system
  5.  *          Note process resident sizes could be wrong, but ps shows
  6.  *          zero for them too..
  7.  *
  8.  * DESCRIPTION:
  9.  * Originally written for BSD4.4 system by Christos Zoulas.
  10.  * Ported to FreeBSD 2.0 by Steven Wallace && Wolfram Schneider
  11.  *
  12.  * This is the machine-dependent module for FreeBSD 2.0
  13.  * Works for:
  14.  *    FreeBSD 2.0
  15.  *
  16.  * LIBS: -lkvm
  17.  *
  18.  * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
  19.  *          Steven Wallace  <swallace@freebsd.org>
  20.  *          Wolfram Schneider <wosch@cs.tu-berlin.de>
  21.  *
  22.  * $Id: machine.c,v 1.5 1995/01/06 02:04:39 swallace Exp $
  23.  */
  24.  
  25.  
  26.  
  27. #define LASTPID      /**/  /* use last pid, compiler depended */
  28. /* #define LASTPID_FIXED /**/ 
  29. #define VM_REAL      /**/  /* use the same values as vmstat -s */
  30. #define USE_SWAP     /**/  /* use swap usage (pstat -s), 
  31.                               need to much cpu time */
  32. /* #define DEBUG 1      /**/
  33.  
  34. #include <sys/types.h>
  35. #include <sys/signal.h>
  36. #include <sys/param.h>
  37.  
  38. #include "os.h"
  39. #include <stdio.h>
  40. #include <nlist.h>
  41. #include <math.h>
  42. #include <kvm.h>
  43. #include <sys/errno.h>
  44. #include <sys/sysctl.h>
  45. #include <sys/dir.h>
  46. #include <sys/dkstat.h>
  47. #include <sys/file.h>
  48. #include <sys/time.h>
  49.  
  50. #ifdef USE_SWAP
  51. #include <stdlib.h>
  52. #include <sys/rlist.h>
  53. #include <sys/conf.h>
  54. #endif
  55.  
  56. static int check_nlist __P((struct nlist *));
  57. static int getkval __P((unsigned long, int *, int, char *));
  58. extern char* printable __P((char *));
  59.  
  60. #include "top.h"
  61. #include "machine.h"
  62.  
  63.  
  64. /* get_process_info passes back a handle.  This is what it looks like: */
  65.  
  66. struct handle
  67. {
  68.     struct kinfo_proc **next_proc;    /* points to next valid proc pointer */
  69.     int remaining;        /* number of pointers remaining */
  70. };
  71.  
  72. /* declarations for load_avg */
  73. #include "loadavg.h"
  74.  
  75. #define PP(pp, field) ((pp)->kp_proc . field)
  76. #define EP(pp, field) ((pp)->kp_eproc . field)
  77. #define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
  78.  
  79. /* define what weighted cpu is.  */
  80. #define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \
  81.              ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu))))
  82.  
  83. /* what we consider to be process size: */
  84. #define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
  85.  
  86. /* definitions for indices in the nlist array */
  87.  
  88.  
  89. static struct nlist nlst[] = {
  90. #define X_CCPU        0
  91.     { "_ccpu" },        /* 0 */
  92. #define X_CP_TIME    1
  93.     { "_cp_time" },        /* 1 */
  94. #define X_HZ        2
  95.     { "_hz" },                /* 2 */
  96. #define X_STATHZ    3
  97.     { "_stathz" },        /* 3 */
  98. #define X_AVENRUN    4
  99.     { "_averunnable" },        /* 4 */
  100. #ifdef USE_SWAP
  101. #define VM_SWAPLIST    5
  102.     { "_swaplist" },/* list of free swap areas */
  103. #define VM_SWDEVT    6
  104.     { "_swdevt" },    /* list of swap devices and sizes */
  105. #define VM_NSWAP    7
  106.     { "_nswap" },    /* size of largest swap device */
  107. #define VM_NSWDEV    8
  108.     { "_nswdev" },    /* number of swap devices */
  109. #define VM_DMMAX    9
  110.     { "_dmmax" },    /* maximum size of a swap block */
  111. #endif
  112. #ifdef VM_REAL
  113. #ifdef USE_SWAP
  114. #define X_CNT           10
  115. #else
  116. #define X_CNT           5
  117. #endif
  118.     { "_cnt" },                /* struct vmmeter cnt */
  119. #endif
  120.  
  121. #ifdef LASTPID
  122. #if (defined USE_SWAP && defined VM_REAL)
  123. #define X_LASTPID    11
  124. #elif (defined VM_REAL)
  125. #define X_LASTPID    6
  126. #else
  127. #define X_LASTPID       5
  128. #endif
  129. #ifdef LASTPID_FIXED
  130.     { "_nextpid" },        
  131. #else
  132.     { "_nextpid.178" },        /* lastpid, compiler depended 
  133.                  * should be changed 
  134.                  * in /sys/kern/kern_fork.c */
  135. #endif
  136. #endif
  137.  
  138.     { 0 }
  139. };
  140.  
  141. /*
  142.  *  These definitions control the format of the per-process area
  143.  */
  144.  
  145. static char header[] =
  146.   "  PID X        PRI NICE   SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
  147. /* 0123456   -- field to fill in starts at header+6 */
  148. #define UNAME_START 6
  149.  
  150. #define Proc_format \
  151.     "%5d %-8.8s %3d %4d%7s %5s %-5s%7s %5.2f%% %5.2f%% %.14s"
  152.  
  153.  
  154. /* process state names for the "STATE" column of the display */
  155. /* the extra nulls in the string "run" are for adding a slash and
  156.    the processor number when needed */
  157.  
  158. char *state_abbrev[] =
  159. {
  160.     "", "start", "run\0\0\0", "sleep", "stop", "zomb", "WAIT"
  161. };
  162.  
  163.  
  164. static kvm_t *kd;
  165.  
  166. /* values that we stash away in _init and use in later routines */
  167.  
  168. static double logcpu;
  169.  
  170. /* these are retrieved from the kernel in _init */
  171.  
  172. static          long hz;
  173. static load_avg  ccpu;
  174.  
  175. /* these are offsets obtained via nlist and used in the get_ functions */
  176.  
  177. static unsigned long cp_time_offset;
  178. static unsigned long avenrun_offset;
  179. #ifdef LASTPID
  180. static unsigned long lastpid_offset;
  181. static long lastpid;
  182. #endif
  183. #ifdef VM_REAL
  184. static unsigned long cnt_offset;
  185. static long cnt;
  186. #endif
  187. /* these are for calculating cpu state percentages */
  188.  
  189. static long cp_time[CPUSTATES];
  190. static long cp_old[CPUSTATES];
  191. static long cp_diff[CPUSTATES];
  192.  
  193. /* these are for detailing the process states */
  194.  
  195. int process_states[7];
  196. char *procstatenames[] = {
  197.     "", " starting, ", " running, ", " sleeping, ", " stopped, ",
  198.     " zombie, ", " ABANDONED, ",
  199.     NULL
  200. };
  201.  
  202. /* these are for detailing the cpu states */
  203.  
  204. int cpu_states[CPUSTATES];
  205. char *cpustatenames[] = {
  206.     "user", "nice", "system", "interrupt", "idle", NULL
  207. };
  208.  
  209. /* these are for detailing the memory statistics */
  210.  
  211. int memory_stats[8];
  212. char *memorynames[] = {
  213. #ifndef VM_REAL
  214.     "Real: ", "K/", "K ", "Virt: ", "K/",
  215.     "K ", "Free: ", "K", NULL
  216. #else
  217. #if 0
  218.     "K Act ", "K Inact ", "K Wired ", "K Free ", "% Swap, ",
  219.     "K/", "K SWIO", 
  220. #else
  221.     "K Act ", "K Inact ", "K Wired ", "K Free ", "% Swap, ",
  222.     "Kin ", "Kout", 
  223. #endif
  224.     NULL
  225. #endif
  226. };
  227.  
  228. /* these are for keeping track of the proc array */
  229.  
  230. static int nproc;
  231. static int onproc = -1;
  232. static int pref_len;
  233. static struct kinfo_proc *pbase;
  234. static struct kinfo_proc **pref;
  235.  
  236. /* these are for getting the memory statistics */
  237.  
  238. static int pageshift;        /* log base 2 of the pagesize */
  239.  
  240. /* define pagetok in terms of pageshift */
  241.  
  242. #define pagetok(size) ((size) << pageshift)
  243.  
  244. /* useful externals */
  245. long percentages();
  246.  
  247. int
  248. machine_init(statics)
  249.  
  250. struct statics *statics;
  251.  
  252. {
  253.     register int i = 0;
  254.     register int pagesize;
  255.  
  256.     if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
  257.     return -1;
  258.  
  259.  
  260.     /* get the list of symbols we want to access in the kernel */
  261.     (void) kvm_nlist(kd, nlst);
  262.     if (nlst[0].n_type == 0)
  263.     {
  264.     fprintf(stderr, "top: nlist failed\n");
  265.     return(-1);
  266.     }
  267.  
  268.     /* make sure they were all found */
  269.     if (i > 0 && check_nlist(nlst) > 0)
  270.     {
  271.     return(-1);
  272.     }
  273.  
  274.     /* get the symbol values out of kmem */
  275.     (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!");
  276.     if (!hz) {
  277.     (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
  278.                nlst[X_HZ].n_name);
  279.     }
  280.  
  281.  
  282. #if (defined DEBUG)
  283.     fprintf(stderr, "Hertz: %d\n", hz); 
  284. #endif
  285.  
  286.     (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),    sizeof(ccpu),
  287.         nlst[X_CCPU].n_name);
  288.  
  289.     /* stash away certain offsets for later use */
  290.     cp_time_offset = nlst[X_CP_TIME].n_value;
  291.     avenrun_offset = nlst[X_AVENRUN].n_value;
  292. #ifdef LASTPID
  293.     lastpid_offset =  nlst[X_LASTPID].n_value;
  294. #endif
  295. #ifdef VM_REAL
  296.     cnt_offset = nlst[X_CNT].n_value;
  297. #endif
  298.  
  299.     /* this is used in calculating WCPU -- calculate it ahead of time */
  300.     logcpu = log(loaddouble(ccpu));
  301.  
  302.     pbase = NULL;
  303.     pref = NULL;
  304.     nproc = 0;
  305.     onproc = -1;
  306.     /* get the page size with "getpagesize" and calculate pageshift from it */
  307.     pagesize = getpagesize();
  308.     pageshift = 0;
  309.     while (pagesize > 1)
  310.     {
  311.     pageshift++;
  312.     pagesize >>= 1;
  313.     }
  314.  
  315.     /* we only need the amount of log(2)1024 for our conversion */
  316.     pageshift -= LOG1024;
  317.  
  318.     /* fill in the statics information */
  319.     statics->procstate_names = procstatenames;
  320.     statics->cpustate_names = cpustatenames;
  321.     statics->memory_names = memorynames;
  322.  
  323.     /* all done! */
  324.     return(0);
  325. }
  326.  
  327. char *format_header(uname_field)
  328.  
  329. register char *uname_field;
  330.  
  331. {
  332.     register char *ptr;
  333.  
  334.     ptr = header + UNAME_START;
  335.     while (*uname_field != '\0')
  336.     {
  337.     *ptr++ = *uname_field++;
  338.     }
  339.  
  340.     return(header);
  341. }
  342.  
  343. static int swappgsin = -1;
  344. static int swappgsout = -1;
  345. extern struct timeval timeout;
  346.  
  347. void
  348. get_system_info(si)
  349.  
  350. struct system_info *si;
  351.  
  352. {
  353.     long total;
  354.     load_avg avenrun[3];
  355.  
  356.     /* get the cp_time array */
  357.     (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
  358.            nlst[X_CP_TIME].n_name);
  359.     (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
  360.            nlst[X_AVENRUN].n_name);
  361.  
  362. #ifdef LASTPID
  363.     (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid),
  364.            "!");
  365. #endif
  366.  
  367.     /* convert load averages to doubles */
  368.     {
  369.     register int i;
  370.     register double *infoloadp;
  371.     load_avg *avenrunp;
  372.  
  373. #ifdef notyet
  374.     struct loadavg sysload;
  375.     int size;
  376.     getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
  377. #endif
  378.  
  379.     infoloadp = si->load_avg;
  380.     avenrunp = avenrun;
  381.     for (i = 0; i < 3; i++)
  382.     {
  383. #ifdef notyet
  384.         *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
  385. #endif
  386.         *infoloadp++ = loaddouble(*avenrunp++);
  387.     }
  388.     }
  389.  
  390.     /* convert cp_time counts to percentages */
  391.     total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
  392.  
  393.     /* sum memory statistics */
  394.     {
  395.  
  396. #ifndef VM_REAL
  397.     struct vmtotal total;
  398.     int size = sizeof(total);
  399.     static int mib[] = { CTL_VM, VM_METER };
  400.  
  401.     /* get total -- systemwide main memory usage structure */
  402.     if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
  403.         (void) fprintf(stderr, "top: sysctl failed: %s\n", strerror(errno));
  404.         bzero(&total, sizeof(total));
  405.     }
  406.     /* convert memory stats to Kbytes */
  407.     memory_stats[0] = -1;
  408.     memory_stats[1] = pagetok(total.t_arm);
  409.     memory_stats[2] = pagetok(total.t_rm);
  410.     memory_stats[3] = -1;
  411.     memory_stats[4] = pagetok(total.t_avm);
  412.     memory_stats[5] = pagetok(total.t_vm);
  413.     memory_stats[6] = -1;
  414.     memory_stats[7] = pagetok(total.t_free);
  415.     }
  416. #else
  417.     struct vmmeter sum;
  418.     static unsigned int swap_delay = 0;
  419.  
  420.         (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum),
  421.            "_cnt");
  422.  
  423.     /* convert memory stats to Kbytes */
  424.     memory_stats[0] = pagetok(sum.v_active_count);
  425.     memory_stats[1] = pagetok(sum.v_inactive_count);
  426.     memory_stats[2] = pagetok(sum.v_wire_count);
  427.     memory_stats[3] = pagetok(sum.v_free_count);
  428.  
  429.         if (swappgsin < 0) {
  430.         memory_stats[5] = 0;
  431.         memory_stats[6] = 0;
  432.     } else {
  433.         memory_stats[5] = pagetok(((sum.v_swappgsin - swappgsin)));
  434.         memory_stats[6] = pagetok(((sum.v_swappgsout - swappgsout)));
  435.     }
  436.         swappgsin = sum.v_swappgsin;
  437.     swappgsout = sum.v_swappgsout;
  438.  
  439. #ifdef USE_SWAP
  440.         if ((memory_stats[5] > 0 || memory_stats[6]) > 0 || swap_delay == 0) {
  441.         memory_stats[4] = swapmode();
  442.     }
  443.         swap_delay++;
  444. #else
  445.         memory_stats[4] = 0;
  446. #endif
  447.  
  448.  
  449.     memory_stats[7] = -1;
  450.     }
  451. #endif
  452.     /* set arrays and strings */
  453.     si->cpustates = cpu_states;
  454.     si->memory = memory_stats;
  455. #ifdef LASTPID
  456.     if(lastpid > 0) {
  457.     si->last_pid = lastpid;
  458.     } else {
  459.     si->last_pid = -1;
  460.     }
  461. #else
  462.     si->last_pid = -1;
  463. #endif
  464.  
  465. }
  466.  
  467. static struct handle handle;
  468.  
  469. caddr_t get_process_info(si, sel, compare)
  470.  
  471. struct system_info *si;
  472. struct process_select *sel;
  473. int (*compare)();
  474.  
  475. {
  476.     register int i;
  477.     register int total_procs;
  478.     register int active_procs;
  479.     register struct kinfo_proc **prefp;
  480.     register struct kinfo_proc *pp;
  481.  
  482.     /* these are copied out of sel for speed */
  483.     int show_idle;
  484.     int show_system;
  485.     int show_uid;
  486.     int show_command;
  487.  
  488.     
  489.     pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
  490.     if (nproc > onproc)
  491.     pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
  492.         * (onproc = nproc));
  493.     if (pref == NULL || pbase == NULL) {
  494.     (void) fprintf(stderr, "top: Out of memory.\n");
  495.     quit(23);
  496.     }
  497.     /* get a pointer to the states summary array */
  498.     si->procstates = process_states;
  499.  
  500.     /* set up flags which define what we are going to select */
  501.     show_idle = sel->idle;
  502.     show_system = sel->system;
  503.     show_uid = sel->uid != -1;
  504.     show_command = sel->command != NULL;
  505.  
  506.     /* count up process states and get pointers to interesting procs */
  507.     total_procs = 0;
  508.     active_procs = 0;
  509.     memset((char *)process_states, 0, sizeof(process_states));
  510.     prefp = pref;
  511.     for (pp = pbase, i = 0; i < nproc; pp++, i++)
  512.     {
  513.     /*
  514.      *  Place pointers to each valid proc structure in pref[].
  515.      *  Process slots that are actually in use have a non-zero
  516.      *  status field.  Processes with P_SYSTEM set are system
  517.      *  processes---these get ignored unless show_sysprocs is set.
  518.      */
  519.     if (PP(pp, p_stat) != 0 &&
  520.         (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
  521.     {
  522.         total_procs++;
  523.         process_states[(unsigned char) PP(pp, p_stat)]++;
  524.         if ((PP(pp, p_stat) != SZOMB) &&
  525.         (show_idle || (PP(pp, p_pctcpu) != 0) || 
  526.          (PP(pp, p_stat) == SRUN)) &&
  527.         (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
  528.         {
  529.         *prefp++ = pp;
  530.         active_procs++;
  531.         }
  532.     }
  533.     }
  534.  
  535.     /* if requested, sort the "interesting" processes */
  536.     if (compare != NULL)
  537.     {
  538.     qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
  539.     }
  540.  
  541.     /* remember active and total counts */
  542.     si->p_total = total_procs;
  543.     si->p_active = pref_len = active_procs;
  544.  
  545.     /* pass back a handle */
  546.     handle.next_proc = pref;
  547.     handle.remaining = active_procs;
  548.     return((caddr_t)&handle);
  549. }
  550.  
  551. char fmt[128];        /* static area where result is built */
  552.  
  553. char *format_next_process(handle, get_userid)
  554.  
  555. caddr_t handle;
  556. char *(*get_userid)();
  557.  
  558. {
  559.     register struct kinfo_proc *pp;
  560.     register long cputime;
  561.     register double pct;
  562.     struct handle *hp;
  563.  
  564.     /* find and remember the next proc structure */
  565.     hp = (struct handle *)handle;
  566.     pp = *(hp->next_proc++);
  567.     hp->remaining--;
  568.     
  569.  
  570.     /* get the process's user struct and set cputime */
  571.     if ((PP(pp, p_flag) & P_INMEM) == 0) {
  572.     /*
  573.      * Print swapped processes as <pname>
  574.      */
  575.     char *comm = PP(pp, p_comm);
  576. #define COMSIZ sizeof(PP(pp, p_comm))
  577.     char buf[COMSIZ];
  578.     (void) strncpy(buf, comm, COMSIZ);
  579.     comm[0] = '<';
  580.     (void) strncpy(&comm[1], buf, COMSIZ - 2);
  581.     comm[COMSIZ - 2] = '\0';
  582.     (void) strncat(comm, ">", COMSIZ - 1);
  583.     comm[COMSIZ - 1] = '\0';
  584.     }
  585.  
  586. #if 0
  587.     /* This does not produce the correct results */
  588.     cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks);
  589. #endif
  590.     cputime = PP(pp, p_rtime).tv_sec;    /* This does not count interrupts */
  591.  
  592.     /* calculate the base for cpu percentages */
  593.     pct = pctdouble(PP(pp, p_pctcpu));
  594.  
  595.     /* format this entry */
  596.     sprintf(fmt,
  597.         Proc_format,
  598.         PP(pp, p_pid),
  599.         (*get_userid)(EP(pp, e_pcred.p_ruid)),
  600.         PP(pp, p_priority) - PZERO,
  601.         PP(pp, p_nice) - NZERO,
  602.         format_k(pagetok(PROCSIZE(pp))),
  603.         format_k(pagetok(VP(pp, vm_rssize))),
  604.         state_abbrev[(unsigned char) PP(pp, p_stat)],
  605.         format_time(cputime),
  606.         10000.0 * weighted_cpu(pct, pp) / hz,
  607.         10000.0 * pct / hz,
  608.         printable(PP(pp, p_comm)));
  609.  
  610.     /* return the result */
  611.     return(fmt);
  612. }
  613.  
  614.  
  615. /*
  616.  * check_nlist(nlst) - checks the nlist to see if any symbols were not
  617.  *        found.  For every symbol that was not found, a one-line
  618.  *        message is printed to stderr.  The routine returns the
  619.  *        number of symbols NOT found.
  620.  */
  621.  
  622. static int check_nlist(nlst)
  623.  
  624. register struct nlist *nlst;
  625.  
  626. {
  627.     register int i;
  628.  
  629.     /* check to see if we got ALL the symbols we requested */
  630.     /* this will write one line to stderr for every symbol not found */
  631.  
  632.     i = 0;
  633.     while (nlst->n_name != NULL)
  634.     {
  635.     if (nlst->n_type == 0)
  636.     {
  637.         /* this one wasn't found */
  638.         (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
  639.                nlst->n_name);
  640.         i = 1;
  641.     }
  642.     nlst++;
  643.     }
  644.  
  645.     return(i);
  646. }
  647.  
  648.  
  649. /*
  650.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  651.  *    "offset" is the byte offset into the kernel for the desired value,
  652.  *      "ptr" points to a buffer into which the value is retrieved,
  653.  *      "size" is the size of the buffer (and the object to retrieve),
  654.  *      "refstr" is a reference string used when printing error meessages,
  655.  *        if "refstr" starts with a '!', then a failure on read will not
  656.  *          be fatal (this may seem like a silly way to do things, but I
  657.  *          really didn't want the overhead of another argument).
  658.  *      
  659.  */
  660.  
  661. static int getkval(offset, ptr, size, refstr)
  662.  
  663. unsigned long offset;
  664. int *ptr;
  665. int size;
  666. char *refstr;
  667.  
  668. {
  669.     if (kvm_read(kd, offset, (char *) ptr, size) != size)
  670.     {
  671.     if (*refstr == '!')
  672.     {
  673.         return(0);
  674.     }
  675.     else
  676.     {
  677.         fprintf(stderr, "top: kvm_read for %s: %s\n",
  678.         refstr, strerror(errno));
  679.         quit(23);
  680.     }
  681.     }
  682.     return(1);
  683. }
  684.     
  685. /* comparison routine for qsort */
  686.  
  687. /*
  688.  *  proc_compare - comparison function for "qsort"
  689.  *    Compares the resource consumption of two processes using five
  690.  *      distinct keys.  The keys (in descending order of importance) are:
  691.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  692.  *      memory usage.  The process states are ordered as follows (from least
  693.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  694.  *      array declaration below maps a process state index into a number
  695.  *      that reflects this ordering.
  696.  */
  697.  
  698. static unsigned char sorted_state[] =
  699. {
  700.     0,    /* not used        */
  701.     3,    /* sleep        */
  702.     1,    /* ABANDONED (WAIT)    */
  703.     6,    /* run            */
  704.     5,    /* start        */
  705.     2,    /* zombie        */
  706.     4    /* stop            */
  707. };
  708.  
  709. int
  710. proc_compare(pp1, pp2)
  711.  
  712. struct proc **pp1;
  713. struct proc **pp2;
  714.  
  715. {
  716.     register struct kinfo_proc *p1;
  717.     register struct kinfo_proc *p2;
  718.     register int result;
  719.     register pctcpu lresult;
  720.  
  721.     /* remove one level of indirection */
  722.     p1 = *(struct kinfo_proc **) pp1;
  723.     p2 = *(struct kinfo_proc **) pp2;
  724.  
  725.     /* compare percent cpu (pctcpu) */
  726.     if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
  727.     {
  728.     /* use cpticks to break the tie */
  729.     if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
  730.     {
  731.         /* use process state to break the tie */
  732.         if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
  733.               sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
  734.         {
  735.         /* use priority to break the tie */
  736.         if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
  737.         {
  738.             /* use resident set size (rssize) to break the tie */
  739.             if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
  740.             {
  741.             /* use total memory to break the tie */
  742.             result = PROCSIZE(p2) - PROCSIZE(p1);
  743.             }
  744.         }
  745.         }
  746.     }
  747.     }
  748.     else
  749.     {
  750.     result = lresult < 0 ? -1 : 1;
  751.     }
  752.  
  753.     return(result);
  754. }
  755.  
  756.  
  757. /*
  758.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  759.  *        the process does not exist.
  760.  *        It is EXTREMLY IMPORTANT that this function work correctly.
  761.  *        If top runs setuid root (as in SVR4), then this function
  762.  *        is the only thing that stands in the way of a serious
  763.  *        security problem.  It validates requests for the "kill"
  764.  *        and "renice" commands.
  765.  */
  766.  
  767. int proc_owner(pid)
  768.  
  769. int pid;
  770.  
  771. {
  772.     register int cnt;
  773.     register struct kinfo_proc **prefp;
  774.     register struct kinfo_proc *pp;
  775.  
  776.     prefp = pref;
  777.     cnt = pref_len;
  778.     while (--cnt >= 0)
  779.     {
  780.     pp = *prefp++;    
  781.     if (PP(pp, p_pid) == (pid_t)pid)
  782.     {
  783.         return((int)EP(pp, e_pcred.p_ruid));
  784.     }
  785.     }
  786.     return(-1);
  787. }
  788.  
  789.  
  790. #ifdef USE_SWAP
  791. /*
  792.  * swapmode is based on a program called swapinfo written
  793.  * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
  794.  */
  795.  
  796. #define    SVAR(var) __STRING(var)    /* to force expansion */
  797. #define    KGET(idx, var)                            \
  798.     KGET1(idx, &var, sizeof(var), SVAR(var))
  799. #define    KGET1(idx, p, s, msg)                        \
  800.     KGET2(nlst[idx].n_value, p, s, msg)
  801. #define    KGET2(addr, p, s, msg)                        \
  802.     if (kvm_read(kd, (u_long)(addr), p, s) != s)            \
  803.         warnx("cannot read %s: %s", msg, kvm_geterr(kd))
  804. #define    KGETRET(addr, p, s, msg)                    \
  805.     if (kvm_read(kd, (u_long)(addr), p, s) != s) {            \
  806.         warnx("cannot read %s: %s", msg, kvm_geterr(kd));    \
  807.         return (0);                        \
  808.     }
  809.  
  810.  
  811. int
  812. swapmode()
  813. {
  814.     char *header;
  815.     int hlen, nswap, nswdev, dmmax;
  816.     int i, div, avail, nfree, npfree, used;
  817.     struct swdevt *sw;
  818.     long blocksize, *perdev;
  819.     struct rlist head;
  820.     struct rlist *swaplist;
  821.  
  822.     KGET(VM_NSWAP, nswap);
  823.     KGET(VM_NSWDEV, nswdev);
  824.     KGET(VM_DMMAX, dmmax);
  825.     KGET(VM_SWAPLIST, swaplist);
  826.     if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL ||
  827.         (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL)
  828.         err(1, "malloc");
  829.     KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
  830.  
  831.     /* Count up swap space. */
  832.     nfree = 0;
  833.     memset(perdev, 0, nswdev * sizeof(*perdev));
  834.     while (swaplist) {
  835.         int    top, bottom, next_block;
  836.  
  837.         KGET2(swaplist, &head, sizeof(struct rlist), "swaplist");
  838.  
  839.         top = head.rl_end;
  840.         bottom = head.rl_start;
  841.  
  842.         nfree += top - bottom + 1;
  843.  
  844.         /*
  845.          * Swap space is split up among the configured disks.
  846.          *
  847.          * For interleaved swap devices, the first dmmax blocks
  848.          * of swap space some from the first disk, the next dmmax
  849.          * blocks from the next, and so on up to nswap blocks.
  850.          *
  851.          * The list of free space joins adjacent free blocks,
  852.          * ignoring device boundries.  If we want to keep track
  853.          * of this information per device, we'll just have to
  854.          * extract it ourselves.
  855.          */
  856.         while (top / dmmax != bottom / dmmax) {
  857.             next_block = ((bottom + dmmax) / dmmax);
  858.             perdev[(bottom / dmmax) % nswdev] +=
  859.                 next_block * dmmax - bottom;
  860.             bottom = next_block * dmmax;
  861.         }
  862.         perdev[(bottom / dmmax) % nswdev] +=
  863.             top - bottom + 1;
  864.  
  865.         swaplist = head.rl_next;
  866.     }
  867.  
  868.     header = getbsize(&hlen, &blocksize);
  869.     div = blocksize / 512;
  870.     avail = npfree = 0;
  871.     for (i = 0; i < nswdev; i++) {
  872.         int xsize, xfree;
  873.  
  874.         /*
  875.          * Don't report statistics for partitions which have not
  876.          * yet been activated via swapon(8).
  877.          */
  878.  
  879.         xsize = sw[i].sw_nblks;
  880.         xfree = perdev[i];
  881.         used = xsize - xfree;
  882.         npfree++;
  883.         avail += xsize;
  884.     }
  885.  
  886.     /* 
  887.      * If only one partition has been set up via swapon(8), we don't
  888.      * need to bother with totals.
  889.      */
  890.     used = avail - nfree;
  891.     free(sw); free(perdev);
  892.     return  (int)(((double)used / (double)avail * 100.0) + 0.5);
  893. }
  894.  
  895. #endif
  896.  
  897.