home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / machine / m_next32.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-23  |  28.5 KB  |  1,054 lines

  1. /*
  2.  * top - a top users display for Unix
  3.  * NEXTSTEP v.0.5  11/26/1996 tpugh
  4.  *
  5.  * SYNOPSIS:  any m68k or intel NEXTSTEP v3.x system
  6.  *
  7.  * DESCRIPTION:
  8.  *    This is the machine-dependent module for NEXTSTEP v3.x
  9.  *    Reported to work for NEXTSTEP v3.0, v3.2, and v3.3 Mach OS:
  10.  *        NEXTSTEP v3.0 on Motorola machines.
  11.  *        NEXTSTEP v3.2 on Intel and Motorola machines.
  12.  *        NEXTSTEP v3.3 on Intel and Motorola machines.
  13.  *    Problem with command column for: (Choose next33 for fix)
  14.  *        NEXTSTEP v3.2 on HP machines.
  15.  *        NEXTSTEP v3.3 on HP and Sparc machines.
  16.  *    Has not been tested for NEXTSTEP v2.x machines, although it should work.
  17.  *    Has not been tested for NEXTSTEP v3.1 machines, although it should work.
  18.  *    Install "top" with the permissions 4755.
  19.  *        hostname# chmod 4755 top
  20.  *        hostname# ls -lg top
  21.  *        -rwsr-xr-x  1 root     kmem      121408 Sep  1 10:14 top*
  22.  *    With the kmem group sticky bit set, we can read kernal memory without problems,
  23.  *    but to communicate with the Mach kernal for task and thread info, it requires
  24.  *    root privileges. Therefore, "top" must be setuid 4755 with the owner as root.
  25.  *
  26.  * LIBS: 
  27.  *
  28.  * Need the compiler flag, "-DSHOW_UTT", to see the user task and thread task
  29.  * data structures to report process info.
  30.  *
  31.  * CFLAGS: -DSHOW_UTT
  32.  *
  33.  *
  34.  * AUTHORS:        Tim Pugh <tpugh@oce.orst.edu>
  35.  */
  36.  
  37. #include <sys/types.h>
  38. #include <sys/signal.h>
  39. #include <sys/param.h>
  40.  
  41. #include <stdio.h>
  42. #include <nlist.h>
  43. #include <math.h>
  44. #include <sys/dir.h>
  45. #include <sys/user.h>
  46. #include <sys/proc.h>
  47. #include <sys/dk.h>
  48. #include <sys/vm.h>
  49. #include <sys/file.h>
  50. #include <sys/time.h>
  51. #import <mach/mach.h>
  52. #include <sys/vmmeter.h>
  53. #import <mach/vm_statistics.h>
  54.  
  55. #include "top.h"
  56. #include "utils.h"
  57. #include "machine.h"
  58.  
  59. #ifdef NEXTSTEP40
  60. #import "machine/m_next40_task.h"
  61. #else NEXTSTEP40
  62. #import "machine/m_next_task.h"
  63. #endif NEXTSTEP40
  64.  
  65. /*  Problems on NS/HPPA machines.  Also, not currently used by source code.
  66.  *#define DOSWAP
  67.  */
  68.  
  69. extern int errno, sys_nerr;
  70. extern char *sys_errlist[];
  71. #define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
  72.  
  73. #define VMUNIX  "/mach"
  74. #define KMEM    "/dev/kmem"
  75. #define MEM     "/dev/mem"
  76. #ifdef DOSWAP
  77. #define SWAP    "/dev/drum"
  78. #endif
  79.  
  80. /* NeXT BSD process structure does not contain locations to hold info such as
  81.  * cpu usage, memory usage, resident core memory, or cpu time data.  So I've made
  82.  * a new process structure which composites the NeXT structure and the missing
  83.  * system info.
  84.  */
  85. struct proc_unix {
  86.     struct proc *p_self;        /* Each p_self points to a element in pbase. */
  87.     int p_pctcpu;                /* Scaled percent cpu usage. */
  88.     int p_vsize;                /* Total VM memory usage. */
  89.     int p_rsize;                /* Resident core memory usage. */
  90.     int p_cptime;                /* scaled CPU Time */
  91.     int run_state;                /* Task run state. */
  92.     int flags;                    /* Task state flags. */
  93.     int nthreads;                /* Number of threads per Task. */
  94.     int cur_priority;            /* Current main thread priority */
  95. };
  96.  
  97. /* Contains the list of processes. */
  98. struct handle
  99. {
  100.     struct proc_unix *list;        /* points to list of valid proc pointer */
  101.     int count;                    /* number of pointers */
  102.     int current;                /* Index of the current process formatting */
  103. };
  104.  
  105. /* declarations for load_avg */
  106. #include "loadavg.h"
  107. #define LSCALE    1000    /* scaling for "fixed point" arithmetic - <sys/kernel.h> */
  108.  
  109. /* define what weighted cpu is. */
  110. /*
  111.  *#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
  112.  *             ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
  113.  */
  114.  
  115. /*  The following three variables are not defined in NeXT's process structure.
  116.  *    So they are zeroed until other ways of obtaining the info are found.
  117.  */
  118. /* what we consider to be process size: */
  119. /* #define PROCSIZE(pp)    ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) */
  120. #define PROCSIZE(pp)    (0)
  121.  
  122. /* #define P_RSSIZE(pp)    ((pp)->p_rssize) */
  123. #define    P_RSSIZE(pp)    (0)
  124.  
  125. /* #define P_CPTICKS(pp)    ((pp)->p_cpticks) */
  126. #define P_CPTICKS(pp)    (0)
  127.  
  128.  
  129. extern int thread_stats(int p_pid, struct thread_basic_info *info, int *count);
  130. extern int mach_load_avg(void);
  131. extern kern_return_t task_stats(int p_pid, struct task_basic_info *info);
  132.  
  133. /* definitions for indices in the nlist array */
  134. #define X_AVENRUN    0
  135. #define X_CCPU        1
  136. #define X_NPROC        2
  137. #define X_PROC        3
  138. #define X_TOTAL        4
  139. #define X_CP_TIME    5
  140. #define X_MPID        6
  141. #define X_HZ        7
  142.  
  143. static struct nlist nlst[] = {
  144.     { "_avenrun" },        /* 0 */
  145.     { "_cpu_clk" },        /* 1 */
  146.     { "_max_proc" },    /* 2 */
  147.     { "_allproc" },        /* 3 */
  148.     { "_total" },        /* 4 */
  149.     { "_cp_time" },        /* 5 */
  150.     { "_mpid" },        /* 6 */
  151.     { "_hz" },            /* 7 */
  152.     { 0 }
  153. };
  154.  
  155. /*
  156.  *  These definitions control the format of the per-process area
  157.  */
  158.  
  159. static char header[] =
  160.   "  PID X        STATE PRI NICE  THR VSIZE RSIZE   %MEM   %CPU   TIME COMMAND";
  161. /* static char header[] =
  162.  * "  PID X        STATE PRI NICE  THR VSIZE RSIZE   %MEM  %WCPU   %CPU   TIME COMMAND"; 
  163.  */
  164.  
  165. /* 0123456   -- field to fill in starts at header+6 */
  166. #define UNAME_START 6
  167.  
  168. #define Proc_format \
  169.     "%5d %-8.8s %-5s %3d %4d %4d %5s %5s %6.2f %6.2f %6s %.14s"
  170. /* #define Proc_format \
  171.  *    "%5d %-8.8s %-5s %3d %4d %4d %5s %5s %6.2f %6.2f %6.2f %6s %.14s"
  172.  */
  173.  
  174.  
  175. /* process state names for the "STATE" column of the display */
  176. /* the extra nulls in the string "run" are for adding a slash and
  177.    the processor number when needed */
  178. char *state_abbrev[] =
  179. {
  180.     "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
  181. };
  182. char *mach_state[] =
  183. {
  184.     "", "R", "T", "S", "U", "H"
  185. };
  186. char *flags_state[] =
  187. {
  188.     "", "W", "I"
  189. };
  190.  
  191. /* these are for detailing the process states */
  192. int process_states[7];
  193. /* char *procstatenames[] = {
  194.  *    "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
  195.  *    " zombie, ", " stopped, ",
  196.  *    NULL
  197.  *};
  198.  */
  199. char *procstatenames[] = {
  200.     "", " running, ", " stopped, ", " sleeping, ", " uninterruptable, ",
  201.     " halted, ", " zombie ", NULL
  202. };
  203.  
  204.  
  205. static int kmem, mem;
  206. #ifdef DOSWAP
  207. static int swap;
  208. #endif
  209.  
  210. /* values that we stash away in _init and use in later routines */
  211.  
  212. /* static double logcpu; */
  213.  
  214. /* these are retrieved from the kernel in _init */
  215.  
  216. static unsigned long proc;
  217. static          int  nproc;
  218. static          long hz;
  219. static load_avg  ccpu;
  220. static          int  ncpu = 0;
  221.  
  222. /* these are offsets obtained via nlist and used in the get_ functions */
  223.  
  224. static unsigned long avenrun_offset;
  225. static unsigned long mpid_offset;
  226. static unsigned long total_offset;
  227. static unsigned long cp_time_offset;
  228.  
  229. /* these are for calculating cpu state percentages */
  230.  
  231. static long cp_time[CPUSTATES];
  232. static long cp_old[CPUSTATES];
  233. static long cp_diff[CPUSTATES];
  234.  
  235. /* these are for detailing the cpu states */
  236.  
  237. int cpu_states[4];
  238. char *cpustatenames[] = {
  239.     "user", "nice", "system", "idle", NULL
  240. };
  241.  
  242. /* these are for detailing the memory statistics */
  243. int memory_stats[7];
  244. /* char *memorynames[] = {
  245.  *   "Real: ", "K/", "K act/tot  ", "Virtual: ", "K/",
  246.  *    "K act/tot  ", "Free: ", "K", NULL
  247.  * };
  248.  */
  249. char *memorynames[] = {
  250.     "K Tot, ", "K Act, ", "K Inact, ", "K Wired, ", "K Free, ", "K in, ", "K out ", NULL
  251. };
  252.  
  253. /* these are for keeping track of the proc array */
  254. static int bytes;
  255. static int pref_count;
  256. static struct proc *pbase;
  257. static struct proc_unix *pref;
  258.  
  259. /* these are for getting the memory statistics */
  260.  
  261. static int pageshift;        /* log base 2 of the pagesize */
  262.  
  263. /* define pagetok in terms of pageshift */
  264. #define pagetok(size) ((size) << pageshift)
  265.  
  266. /* useful externals */
  267. extern int errno;
  268. extern char *sys_errlist[];
  269.  
  270. long lseek();
  271. long time();
  272.  
  273. machine_init(struct statics *statics)
  274. {
  275.     register int i = 0;
  276.     register int pagesize;
  277.     
  278.     if ((kmem = open(KMEM, O_RDONLY)) == -1) {
  279.     perror(KMEM);
  280.     return(-1);
  281.     }
  282.     if ((mem = open(MEM, O_RDONLY)) == -1) {
  283.     perror(MEM);
  284.     return(-1);
  285.     }
  286.  
  287. #ifdef DOSWAP
  288.     if ((swap = open(SWAP, O_RDONLY)) == -1) {
  289.     perror(SWAP);
  290.     return(-1);
  291.     }
  292. #endif
  293.  
  294.     /* get the list of symbols we want to access in the kernel */
  295.     (void) nlist(VMUNIX, nlst);
  296.     if (nlst[0].n_type == 0)
  297.     {
  298.         fprintf(stderr, "top: nlist failed\n");
  299.         return(-1);
  300.     }
  301.  
  302.     /* make sure they were all found */
  303.     if (i > 0 && check_nlist(nlst) > 0)
  304.     {
  305.         return(-1);
  306.     }
  307.  
  308.     /* get the symbol values out of kmem */
  309.     (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
  310.                     nlst[X_PROC].n_un.n_name);
  311.     (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc),
  312.                     nlst[X_NPROC].n_un.n_name);
  313.     (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
  314.                     nlst[X_HZ].n_un.n_name);
  315. /*    (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu),
  316.  *                    nlst[X_CCPU].n_un.n_name);
  317.  */
  318.  
  319.     /* stash away certain offsets for later use */
  320.     mpid_offset = nlst[X_MPID].n_value;
  321.     avenrun_offset = nlst[X_AVENRUN].n_value;
  322.     total_offset = nlst[X_TOTAL].n_value;
  323.     cp_time_offset = nlst[X_CP_TIME].n_value;
  324.     
  325.  
  326.     /* this is used in calculating WCPU -- calculate it ahead of time */
  327. /*    ccpu = mach_load_avg();
  328.  *   logcpu = log((double)(ccpu)/LOAD_SCALE);
  329.  */
  330.  
  331.     /* allocate space for proc structure array and array of pointers */
  332.     bytes = nproc * sizeof(struct proc);
  333.     pbase = (struct proc *)malloc(bytes);
  334.     pref  = (struct proc_unix *)malloc((nproc+1) * sizeof(struct proc_unix *));
  335.  
  336.     /* Just in case ... */
  337.     if (pbase == (struct proc *)NULL || pref == (struct proc_unix *)NULL)
  338.     {
  339.     fprintf(stderr, "top: can't allocate sufficient memory\n");
  340.     return(-1);
  341.     }
  342.  
  343.     /* get the page size with "getpagesize" and calculate pageshift from it */
  344.     pagesize = getpagesize();
  345.     pageshift = ceil(log(pagesize)/log(2.0));
  346.  
  347.     /* we only need the amount of log(2)1024 for our conversion */
  348.     pageshift -= LOG1024;
  349.  
  350.     /* fill in the statics information */
  351.     statics->procstate_names = procstatenames;
  352.     statics->cpustate_names = cpustatenames;
  353.     statics->memory_names = memorynames;
  354.  
  355.     /* all done! */
  356.     return(0);
  357. }
  358.  
  359. char *format_header(register char *uname_field)
  360. {
  361.     register char *ptr;
  362.  
  363.     ptr = header + UNAME_START;
  364.     while (*uname_field != '\0')
  365.     {
  366.     *ptr++ = *uname_field++;
  367.     }
  368.  
  369.     return(header);
  370. }
  371.  
  372. static int swappgsin = -1;
  373. static int swappgsout = -1;
  374. static vm_statistics_data_t vm_stats;
  375. static host_basic_info_data_t  host_stats;
  376.  
  377. get_system_info(struct system_info *si)
  378. {
  379.     long avenrun[3];
  380.     long total;
  381.  
  382.     /* get the cp_time array */
  383.     (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
  384.            "_cp_time");
  385.  
  386.     /* get load average array */
  387.     (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
  388.            "_avenrun");
  389.  
  390.     /* get mpid -- process id of last process */
  391.     (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
  392.            "_mpid");
  393.  
  394.     /* convert load averages to doubles */
  395.     {
  396.     register int i;
  397.     for(i=0; i<3; i++)
  398.         si->load_avg[i] = ((double)avenrun[i])/LSCALE;
  399.     }
  400.  
  401.     /* convert cp_time counts to percentages */
  402.     total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
  403.  
  404.     /* sum memory statistics */
  405.     {
  406.         /* get total -- systemwide main memory usage structure */
  407.         /* Does not work on NeXT system.  Use vm_statistics() for paging info. */
  408.         /* struct vmtotal total;
  409.          * (void) getkval(total_offset, (int *)(&total), sizeof(total),
  410.          *           "_total");
  411.          */
  412.         /* convert memory stats to Kbytes */
  413.         /* memory_stats[0] = -1;
  414.          * memory_stats[1] = pagetok(total.t_arm);
  415.          * memory_stats[2] = pagetok(total.t_rm);
  416.          * memory_stats[3] = -1;
  417.          * memory_stats[4] = pagetok(total.t_avm);
  418.          * memory_stats[5] = pagetok(total.t_vm);
  419.          * memory_stats[6] = -1;
  420.          * memory_stats[7] = pagetok(total.t_free);
  421.          */
  422.         kern_return_t status;
  423.         unsigned int count=HOST_BASIC_INFO_COUNT;
  424.         status = vm_statistics(task_self(), &vm_stats);
  425. #ifdef DEBUG
  426.         if(status != KERN_SUCCESS)
  427.             mach_error("An error calling vm_statistics()!", status);
  428. #endif
  429.         status = host_info(host_self(), HOST_BASIC_INFO, (host_info_t)&host_stats, &count);
  430. #ifdef DEBUG
  431.         if(status != KERN_SUCCESS)
  432.             mach_error("An error calling host_info()!", status);
  433. #endif
  434.         /* convert memory stats to Kbytes */
  435.         memory_stats[0] = pagetok(host_stats.memory_size / vm_stats.pagesize);
  436.         memory_stats[1] = pagetok(vm_stats.active_count);
  437.         memory_stats[2] = pagetok(vm_stats.inactive_count);
  438.         memory_stats[3] = pagetok(vm_stats.wire_count);
  439.         memory_stats[4] = pagetok(vm_stats.free_count);
  440.         if (swappgsin < 0)
  441.         {
  442.             memory_stats[5] = 1;
  443.             memory_stats[6] = 1;
  444.         } else {
  445.             memory_stats[5] = pagetok(((vm_stats.pageins - swappgsin)));
  446.             memory_stats[6] = pagetok(((vm_stats.pageouts - swappgsout)));
  447.         }
  448.         swappgsin = vm_stats.pageins;
  449.         swappgsout = vm_stats.pageouts;
  450.     }
  451.  
  452.     /* set arrays and strings */
  453.     si->cpustates = cpu_states;
  454.     si->memory = memory_stats;
  455. }
  456.  
  457. static struct handle handle;
  458.  
  459. caddr_t get_process_info(struct system_info *si, 
  460.                          struct process_select *sel, 
  461.                          int (*compare)())
  462. {
  463.     int i, j;
  464.     int total_procs;
  465.     int active_procs;
  466.     struct proc *pp;
  467.     struct task_basic_info taskInfo;
  468.     struct thread_basic_info threadInfo;
  469.     kern_return_t thread_status;
  470.     kern_return_t task_status;
  471.     int threadCount;
  472.  
  473.     /* these are copied out of sel for speed */
  474.     int show_idle;
  475.     int show_system;
  476.     int show_uid;
  477.     int show_command;
  478.  
  479.     /* get a pointer to the states summary array */
  480.     si->procstates = process_states;
  481.  
  482.     /* set up flags which define what we are going to select */
  483.     show_idle = sel->idle;
  484.     show_system = sel->system;
  485.     show_uid = sel->uid != -1;
  486.     show_command = sel->command != NULL;
  487.  
  488.     (void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
  489.                     nlst[X_PROC].n_un.n_name);
  490.  
  491.     /* count up process states and get pointers to interesting procs */
  492.     total_procs = 0;
  493.     active_procs = 0;
  494.     memset((char *)process_states, 0, sizeof(process_states));
  495.     i = 0;
  496.     j = 0;
  497.     do {
  498.         if(i == 0) {
  499.                /* read first proc structure */
  500.             (void) getkval(proc, (int *)&pbase[i], sizeof(struct proc), "first proc");
  501.          } else {
  502.             (void) getkval(pp->p_nxt, (int *)&pbase[i], sizeof(struct proc), "nxt proc");
  503.         }
  504.         pp = &pbase[i];
  505.  
  506.         thread_status = thread_stats(pp->p_pid, &threadInfo, &threadCount);
  507.         task_status = task_stats(pp->p_pid, &taskInfo);
  508.     /*
  509.      *  Process slots that are actually in use have a non-zero
  510.      *  status field.  Processes with SSYS set are system
  511.      *  processes---these get ignored unless show_sysprocs is set.
  512.      */
  513.         if (pp->p_stat != 0 &&
  514.             (show_system || ((pp->p_flag & SSYS) == 0)))
  515.         {
  516.             total_procs++;
  517. /* Using thread info for process states. */
  518. /*            process_states[pp->p_stat]++; */
  519.             if(thread_status==KERN_SUCCESS)
  520.                 process_states[threadInfo.run_state]++;
  521.             if ((pp->p_stat != SZOMB) &&
  522.                 (show_idle || (pp->p_stat == SRUN)) &&
  523.                 (!show_uid || pp->p_uid == (uid_t)sel->uid))
  524.             {
  525.                 pref[j].p_self = pp;
  526.                 if(thread_status==KERN_SUCCESS)
  527.                 {
  528.                     pref[j].run_state = threadInfo.run_state;
  529.                     pref[j].flags = threadInfo.flags;
  530.                     pref[j].p_pctcpu = threadInfo.cpu_usage;
  531.                     pref[j].p_cptime = threadInfo.user_time.seconds + 
  532.                                          threadInfo.system_time.seconds;
  533.                     pref[j].cur_priority = threadInfo.cur_priority;
  534.                     pref[j].nthreads = threadCount;
  535.                 } else {
  536.                     pref[j].run_state = 0;
  537.                     pref[j].flags = 0;
  538.                     pref[j].p_pctcpu = 0;
  539.                     pref[j].p_cptime = 0;
  540.                 }
  541.                 /* Get processes memory usage and cputime */
  542.                 if(task_status==KERN_SUCCESS)
  543.                 {
  544.                     pref[j].p_rsize = taskInfo.resident_size/1024;
  545.                     pref[j].p_vsize = taskInfo.virtual_size/1024;
  546.                 } else {
  547.                     pref[j].p_rsize = 0;
  548.                     pref[j].p_vsize = 0;
  549.                 }
  550.                 active_procs++;
  551.                 j++;
  552.             }
  553.         }
  554.         i++;
  555.     } while(pp->p_nxt != 0);
  556.     pref[j].p_self = NULL;  /*  End list of processes with NULL */
  557.  
  558.     /* if requested, sort the "interesting" processes */
  559.      if (compare != NULL)
  560.     {
  561.         qsort((char *)pref, active_procs, sizeof(struct proc_unix), compare);
  562.     }
  563.  
  564.     /* remember active and total counts */
  565.     si->p_total = total_procs;
  566.     si->p_active = pref_count = active_procs;
  567.  
  568.     /* pass back a handle */
  569.     handle.list = pref;
  570.     handle.count = active_procs;
  571.     handle.current = 0;
  572.     return((caddr_t)&handle);
  573. }
  574.  
  575. char fmt[MAX_COLS];        /* static area where result is built */
  576.  
  577. char *format_next_process(caddr_t handle, char *(*get_userid)())
  578. {
  579.     register struct proc *pp;
  580.     register long cputime;
  581.     register double pct, wcpu, pctmem;
  582.     int where;
  583.     struct user u;
  584.     struct handle *hp;
  585.     register int p_pctcpu;
  586.     register int rm_size;
  587.     register int vm_size;
  588.     register int run_state;
  589.     register int flags;
  590.     register int nthreads;
  591.     register int cur_priority;
  592.     char state_str[10];
  593.  
  594.     /* find and remember the next proc structure */
  595.     hp = (struct handle *)handle;
  596.     pp = hp->list[hp->current].p_self;
  597.     p_pctcpu = hp->list[hp->current].p_pctcpu;
  598.     cputime = hp->list[hp->current].p_cptime;
  599.     rm_size = hp->list[hp->current].p_rsize;
  600.     vm_size = hp->list[hp->current].p_vsize;
  601.     run_state = hp->list[hp->current].run_state;
  602.     flags = hp->list[hp->current].flags;
  603.     nthreads = hp->list[hp->current].nthreads;
  604.     cur_priority = hp->list[hp->current].cur_priority;
  605.     hp->current++;
  606.     hp->count--;
  607.  
  608.     /* get the process's user struct and set cputime */
  609.     where = getu(pp, &u);
  610.     if (where == -1)
  611.     {
  612.         (void) strcpy(u.u_comm, "<swapped>");
  613.         cputime = 0;
  614.     }
  615.     else
  616.     {
  617.         /* set u_comm for system processes */
  618.         if (u.u_comm[0] == '\0')
  619.         {
  620.             if (pp->p_pid == 0)
  621.             {
  622.                 (void) strcpy(u.u_comm, "Swapper");
  623.             }
  624.             else if (pp->p_pid == 2)
  625.             {
  626.                 (void) strcpy(u.u_comm, "Pager");
  627.             }
  628.             }
  629.         if (where == 1) {
  630.             /*
  631.              * Print swapped processes as <pname>
  632.              */
  633.             char buf[sizeof(u.u_comm)];
  634.             (void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
  635.             u.u_comm[0] = '<';
  636.             (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2);
  637.             u.u_comm[sizeof(u.u_comm) - 2] = '\0';
  638.             (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1);
  639.             u.u_comm[sizeof(u.u_comm) - 1] = '\0';
  640.         }
  641. /*    User structure does not work.  Use Thread Info to get cputime for process. */
  642. /*        cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; */
  643.     }
  644.  
  645.  
  646.     /* calculate the base for cpu percentages */
  647.     pct = (double)(p_pctcpu)/TH_USAGE_SCALE;
  648. /*    wcpu = weighted_cpu(pct, pp);
  649.  */
  650.     pctmem = (double)(rm_size*1024.) / (double)(host_stats.memory_size);
  651.     
  652.     /* Get process state description */
  653.     if(run_state)
  654.     {
  655.         strcpy(state_str, mach_state[run_state]);
  656.         strcat(state_str, flags_state[flags]);
  657.     } else {
  658.         strcpy(state_str, state_abbrev[pp->p_stat]);
  659.     }
  660.     
  661.     /* format this entry */
  662.     sprintf(fmt,
  663.         Proc_format,
  664.         pp->p_pid,
  665.         (*get_userid)(pp->p_uid),
  666.         state_str,
  667.         cur_priority,
  668. /*        pp->p_pri - PZERO, */
  669.         pp->p_nice - NZERO,
  670.         nthreads,
  671.         format_k(vm_size),
  672.         format_k(rm_size),
  673.         100.0 * pctmem,
  674. /*        100.0 * wcpu, */
  675.         100.0 * pct,
  676.         format_time(cputime),
  677.         printable(u.u_comm));
  678.  
  679.     /* return the result */
  680.     return(fmt);
  681. }
  682.  
  683. /*
  684.  *  getu(p, u) - get the user structure for the process whose proc structure
  685.  *    is pointed to by p.  The user structure is put in the buffer pointed
  686.  *    to by u.  Return 0 if successful, -1 on failure (such as the process
  687.  *    being swapped out).
  688.  */
  689.  
  690. getu(register struct proc *p, struct user *u)
  691. {
  692. /*    int i; */
  693. /*   int uutask[40]; */
  694.  
  695.     register int nbytes, n;
  696.     struct task task;
  697.     struct utask utask;
  698.     struct uthread thread;
  699.  
  700.     /*
  701.      *  Check if the process is currently loaded or swapped out.  The way we
  702.      *  get the u area is totally different for the two cases.  For this
  703.      *  application, we just don't bother if the process is swapped out.
  704.      */
  705.     /* NEXTSTEP proc.h
  706.      * One structure allocated per active
  707.      * process. It contains all data needed
  708.      * about the process while the
  709.      * process may be swapped out.
  710.      * Other per process data (user.h)
  711.      * is swapped with the process.
  712.      */
  713.  
  714.     if ((p->p_flag & SLOAD) == 0) {
  715. /* User info is always in core.
  716.  * #ifdef DOSWAP
  717.  *         if (lseek(swap, (long)dtob(p->p_swaddr), 0) == -1) {
  718.  *             perror("lseek(swap)");
  719.  *             return(-1);
  720.  *         }
  721.  *         if (read(swap, (char *) u, sizeof(struct user)) != sizeof(struct user))  {
  722.  *             perror("read(swap)");
  723.  *             return(-1);
  724.  *         }
  725.  *         return (1);
  726.  * #else
  727.  */
  728.         return(-1);
  729. /*#endif
  730.  */
  731.     }
  732.  
  733.     /*
  734.      *  Process is currently in memory, we hope!
  735.      */
  736. /*    getkval(p->task, (int *)&uutask, 40*sizeof(int), "task");
  737.     for(i=0; i<40; i++) printf("task[%i]=%i\n", i, uutask[i]);
  738.     getkval(task.u_address, (int *)&uutask, 40*sizeof(int), "task.u_address");
  739.     for(i=0; i<40; i++) printf("utask[%i]=%i, %s\n", i, uutask[i], &uutask[i]);
  740. */
  741.  
  742.  
  743.     if(!getkval(p->task, (int *)&task, sizeof(struct task), "task")) {
  744. #ifdef DEBUG
  745.         perror("getkval(p->task)");
  746. #endif
  747.         /* we can't seem to get to it, so pretend it's swapped out */
  748.         return(-1);
  749.     }
  750.  
  751.  
  752.     if(!getkval(task.u_address, (int *)&utask, sizeof(struct utask), "task.u_address")) {
  753. #ifdef DEBUG
  754.         perror("getkval(task->utask)");
  755. #endif
  756.         /* we can't seem to get to it, so pretend it's swapped out */
  757.         return(-1);
  758.     }
  759.  
  760.     /* Copy utask and uthread info into struct user *u */
  761.     /*  This is incomplete.  Only copied info needed. */
  762.  
  763.     u->u_procp = utask.uu_procp;
  764.     u->u_ar0 = utask.uu_ar0;
  765.     u->u_ru = utask.uu_ru;
  766.     strcpy(u->u_comm, utask.uu_comm);
  767.     nbytes = strlen(u->u_comm);
  768.     for(n=nbytes; n<MAXCOMLEN; n++)
  769.         u->u_comm[n] = ' ';
  770.     u->u_comm[MAXCOMLEN] = '\0';
  771.     return(0);
  772. }
  773.  
  774. /*
  775.  * check_nlist(nlst) - checks the nlist to see if any symbols were not
  776.  *        found.  For every symbol that was not found, a one-line
  777.  *        message is printed to stderr.  The routine returns the
  778.  *        number of symbols NOT found.
  779.  */
  780.  
  781. int check_nlist(register struct nlist *nlst)
  782. {
  783.     register int i;
  784.  
  785.     /* check to see if we got ALL the symbols we requested */
  786.     /* this will write one line to stderr for every symbol not found */
  787.  
  788.     i = 0;
  789.     while (nlst->n_un.n_name != NULL)
  790.     {
  791.     if (nlst->n_type == 0 && nlst->n_value == 0)
  792.     {
  793.         /* this one wasn't found */
  794.         fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_un.n_name);
  795.         i = 1;
  796.     }
  797.     nlst++;
  798.     }
  799.  
  800.     return(i);
  801. }
  802.  
  803.  
  804. /*
  805.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  806.  *    "offset" is the byte offset into the kernel for the desired value,
  807.  *      "ptr" points to a buffer into which the value is retrieved,
  808.  *      "size" is the size of the buffer (and the object to retrieve),
  809.  *      "refstr" is a reference string used when printing error meessages,
  810.  *        if "refstr" starts with a '!', then a failure on read will not
  811.  *          be fatal (this may seem like a silly way to do things, but I
  812.  *          really didn't want the overhead of another argument).
  813.  *      
  814.  */
  815.  
  816. getkval(unsigned long offset, int *ptr, int size, char *refstr)
  817. {
  818.     if (lseek(kmem, (long)offset, L_SET) == -1) {
  819.         if (*refstr == '!')
  820.             refstr++;
  821.         (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, 
  822.                refstr, strerror(errno));
  823.         quit(23);
  824.     }
  825.     if (read(kmem, (char *) ptr, size) == -1) {
  826.         if (*refstr == '!') 
  827.             return(0);
  828.         else {
  829.             (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, 
  830.                refstr, strerror(errno));
  831.             quit(23);
  832.         }
  833.     }
  834.     return(1);
  835. }
  836.     
  837. /* comparison routine for qsort */
  838.  
  839. /*
  840.  *  proc_compare - comparison function for "qsort"
  841.  *    Compares the resource consumption of two processes using five
  842.  *      distinct keys.  The keys (in descending order of importance) are:
  843.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  844.  *      memory usage.  The process states are ordered as follows (from least
  845.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  846.  *      array declaration below maps a process state index into a number
  847.  *      that reflects this ordering.
  848.  */
  849.  
  850. static unsigned char sorted_state[] =
  851. {
  852.     0,    /* not used        */
  853.     3,    /* sleep        */
  854.     1,    /* ABANDONED (WAIT)    */
  855.     6,    /* run            */
  856.     5,    /* start        */
  857.     2,    /* zombie        */
  858.     4    /* stop            */
  859. };
  860.  
  861. proc_compare(struct proc_unix *pp1, struct proc_unix *pp2)
  862. {
  863.     register struct proc *p1 = pp1->p_self;
  864.     register struct proc *p2 = pp2->p_self;
  865.     register int result;
  866.     register pctcpu lresult;
  867.  
  868.     /* compare percent cpu (pctcpu) */
  869.     if ((lresult = pp2->p_pctcpu - pp1->p_pctcpu) == 0)
  870.     {
  871.     /* use cpticks to break the tie */
  872.     if ((result = P_CPTICKS(p2) - P_CPTICKS(p1)) == 0)
  873.     {
  874.         /* use process state to break the tie */
  875.         if ((result = sorted_state[p2->p_stat] - sorted_state[p1->p_stat])  == 0)
  876.         {
  877.         /* use priority to break the tie */
  878.         if ((result = p2->p_pri - p1->p_pri) == 0)
  879.         {
  880.             /* use resident set size (rssize) to break the tie */
  881.             if ((result = pp2->p_rsize - pp1->p_rsize) == 0)
  882.             {
  883.             /* use total memory to break the tie */
  884.             result = pp2->p_vsize - pp1->p_vsize;
  885.             }
  886.         }
  887.         }
  888.     }
  889.     }
  890.     else
  891.     {
  892.     result = lresult < 0 ? -1 : 1;
  893.     }
  894.  
  895.     return(result);
  896. }
  897.  
  898. /*
  899.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  900.  *        the process does not exist.
  901.  *        It is EXTREMLY IMPORTANT that this function work correctly.
  902.  *        If top runs setuid root (as in SVR4), then this function
  903.  *        is the only thing that stands in the way of a serious
  904.  *        security problem.  It validates requests for the "kill"
  905.  *        and "renice" commands.
  906.  */
  907.  
  908. int proc_owner(int pid)
  909. {
  910.     register int cnt;
  911.     register struct proc *pp;
  912.  
  913.     cnt = pref_count;
  914.     while (--cnt >= 0)
  915.     {
  916.         pp = pref[cnt].p_self;
  917.         if( pp->p_pid == pid )     /* Modified (pid_t)pid to pid, compiler error. */
  918.         {
  919.             return((int)pp->p_uid);
  920.         }
  921.     }
  922.     return(-1);
  923. }
  924.  
  925. int thread_stats(int pid, struct thread_basic_info *info, int *thread_count)
  926. {
  927.     int                       i;
  928.     kern_return_t             status;
  929.     kern_return_t              status_dealloc;
  930.     task_t                      p_task;
  931.     thread_array_t              thread_list, list;
  932.     struct thread_basic_info  threadInfo;
  933.     unsigned int              info_count = THREAD_BASIC_INFO_COUNT;
  934.  
  935.     /* Get the task pointer for the process. */
  936.     status = task_by_unix_pid( task_self(), pid, &p_task);
  937.     if (status!=KERN_SUCCESS)
  938.     {
  939. #ifdef DEBUG
  940.         printf("pid = %i\n", pid);
  941.         mach_error("Error calling task_by_unix_pid()", status);
  942. #endif
  943.         return status;
  944.     }
  945.     
  946.     /* Get the list of threads for the task. */
  947.     status = task_threads(p_task, &thread_list, thread_count);
  948.     if (status!=KERN_SUCCESS)
  949.     {
  950. #ifdef DEBUG
  951.         mach_error("Error calling task_threads()", status);
  952. #endif
  953.         return status;
  954.     }
  955.  
  956.     /* Get the pctcpu value for each thread and sum the values */
  957.     info->user_time.seconds = 0;
  958.     info->user_time.microseconds = 0;
  959.     info->system_time.seconds = 0;
  960.     info->system_time.microseconds = 0;
  961.     info->cpu_usage = 0;
  962.     info->sleep_time = 0;
  963.  
  964.     for(i=0; i<*thread_count; i++)
  965.     {
  966.         status = thread_info(thread_list[i], THREAD_BASIC_INFO, 
  967.                         (thread_info_t)&threadInfo, &info_count);
  968.         if (status!=KERN_SUCCESS)
  969.         {
  970. #ifdef DEBUG
  971.             mach_error("Error calling thread_info()", status);
  972. #endif
  973.             break; 
  974.         } else {
  975.             if(i==0)
  976.             {
  977.                 info->base_priority = threadInfo.base_priority;
  978.                 info->cur_priority = threadInfo.cur_priority;
  979.                 info->run_state = threadInfo.run_state;
  980.                 info->flags = threadInfo.flags;
  981.                 info->suspend_count = threadInfo.suspend_count;
  982.                 info->sleep_time += threadInfo.sleep_time;
  983.             }
  984.             info->user_time.seconds += threadInfo.user_time.seconds;
  985.             info->user_time.microseconds += threadInfo.user_time.microseconds;
  986.             info->system_time.seconds += threadInfo.system_time.seconds;
  987.             info->system_time.microseconds += threadInfo.system_time.microseconds;
  988.             info->cpu_usage += threadInfo.cpu_usage;
  989.         }
  990.     }
  991.  
  992.     /* Deallocate the list of threads. */
  993.     status_dealloc = vm_deallocate(task_self(), (vm_address_t)thread_list,
  994.                            sizeof(thread_list)*(*thread_count));
  995.     if (status_dealloc != KERN_SUCCESS)
  996.     {
  997. #ifdef DEBUG
  998.         mach_error("Trouble freeing thread_list", status_dealloc);
  999. #endif
  1000.         status = status_dealloc;
  1001.     }
  1002.     return status;
  1003. }
  1004.  
  1005. int mach_load_avg(void)
  1006. {
  1007.     kern_return_t                    status;
  1008.     host_t                           host;
  1009.     unsigned int                     info_count;
  1010.     struct processor_set_basic_info  info;
  1011.     processor_set_t                  default_set;
  1012.  
  1013.     status=processor_set_default(host_self(), &default_set);
  1014.     if (status!=KERN_SUCCESS){
  1015.         mach_error("Error calling processor_set_default", status);
  1016.         exit(1);
  1017.     }
  1018.  
  1019.     info_count=PROCESSOR_SET_BASIC_INFO_COUNT;
  1020.     status=processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
  1021.                                &host, (processor_set_info_t)&info, &info_count);
  1022. #ifdef DEBUG
  1023.     if (status != KERN_SUCCESS)
  1024.         mach_error("Error calling processor_set_info", status);
  1025. #endif
  1026.     return info.load_average;
  1027. }
  1028.  
  1029. kern_return_t task_stats(int pid, struct task_basic_info *info)
  1030. {
  1031.     kern_return_t             status;
  1032.     task_t                      p_task;
  1033.     unsigned int              info_count=TASK_BASIC_INFO_COUNT;
  1034.  
  1035.     /* Get the task pointer for the process. */
  1036.     status = task_by_unix_pid( task_self(), pid, &p_task);
  1037.     if (status!=KERN_SUCCESS) {
  1038. #ifdef DEBUG
  1039.         printf("pid = %i\n", pid);
  1040.         mach_error("Error calling task_by_unix_pid()", status);
  1041. #endif
  1042.         return(status);
  1043.     }
  1044.  
  1045.     status=task_info(p_task, TASK_BASIC_INFO, (task_info_t)info, &info_count);
  1046.     if (status!=KERN_SUCCESS) {
  1047. #ifdef DEBUG
  1048.         mach_error("Error calling task_info()", status);
  1049. #endif
  1050.         return(status);
  1051.     }        
  1052.     return(KERN_SUCCESS);
  1053. }
  1054.