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

  1. /*
  2.  * top - a top users display for Unix
  3.  *
  4.  * SYNOPSIS:  Encore Multimax running any release of UMAX 4.3
  5.  *
  6.  * DESCRIPTION:
  7.  * This module makes top work on the following systems:
  8.  *    Encore Multimax running UMAX 4.3 release 4.0 and later
  9.  *
  10.  * AUTHOR:  William LeFebvre <phil@eecs.nwu.edu>
  11.  */
  12.  
  13. /*
  14.  * The winner of the "wow what a hack" award:
  15.  * We don't really need the proc structure out of sys/proc.h, but we do
  16.  * need many of the #defines.  So, we define a bogus "queue" structure
  17.  * so that we don't have to include that mess of stuff in machine/*.h
  18.  * just so that the proc struct will get defined cleanly.
  19.  */
  20.  
  21. struct queue { int x };
  22.  
  23. #include <stdio.h>
  24. #include <sys/types.h>
  25. #include <sys/param.h>
  26. #include <sys/time.h>
  27. #include <sys/resource.h>
  28. #include <sys/proc.h>
  29. #include <machine/cpu.h>
  30. #include <inq_stats/statistics.h>
  31. #include <inq_stats/cpustats.h>
  32. #include <inq_stats/procstats.h>
  33. #include <inq_stats/vmstats.h>
  34.  
  35. #include "top.h"
  36. #include "display.h"
  37. #include "machine.h"
  38. #include "utils.h"
  39.  
  40. struct handle
  41. {
  42.     struct proc **next_proc;    /* points to next valid proc pointer */
  43.     int remaining;        /* number of pointers remaining */
  44. };
  45.  
  46. /* Log base 2 of 1024 is 10 (2^10 == 1024) */
  47. #define LOG1024        10
  48.  
  49. /* Convert clicks (kernel pages) to kbytes ... */
  50. #if PGSHIFT>10
  51. #define pagetok(size)    ((size) << (PGSHIFT - LOG1024))
  52. #else
  53. #define pagetok(size)    ((size) >> (LOG1024 - PGSHIFT))
  54. #endif
  55.  
  56. /* what we consider to be process size: */
  57. #define PROCSIZE(pp) ((pp)->pd_tsize + (pp)->pd_dsize + (pp)->pd_ssize)
  58.  
  59. /* the ps_nrun array index is incremented every 12th of a minute */
  60. #define    MINUTES(x)    ((x) * 12)
  61.  
  62. /* convert a tv structure (seconds, microseconds) to a double */
  63. #define TVTODOUBLE(tv) ((double)(tv).tv_sec + ((double)(tv).tv_usec / 1000000))
  64.  
  65. /*
  66.  *  These definitions control the format of the per-process area
  67.  */
  68.  
  69. static char header[] =
  70.   "  PID X        PRI NICE  SIZE   RES STATE    TIME    %CPU COMMAND";
  71. /* 0123456   -- field to fill in starts at header+6 */
  72. #define UNAME_START 6
  73.  
  74. #define Proc_format \
  75.     "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %6.2f%% %s"
  76.  
  77. /* process state names for the "STATE" column of the display */
  78.  
  79. char *state_abbrev[] =
  80. {
  81.     "", "", "wait", "run", "start", "stop", "exec", "event"
  82. };
  83.  
  84. /* these are for detailing the process states */
  85.  
  86. int process_states[5];
  87. char *procstatenames[] = {
  88.     " waiting, ",
  89. #define P_SLEEP  0
  90.     " running, ",
  91. #define P_RUN    1
  92.     " zombie, ",
  93. #define P_ZOMBIE 2
  94.     " stopped, ",
  95. #define P_STOP   3
  96.     " free slots",
  97. #define P_FREE   4
  98.     NULL
  99. };
  100.  
  101. /* these are for detailing the cpu states */
  102.  
  103. int cpu_states[4];
  104. char *cpustatenames[] = {
  105.     "user", "nice", "system", "idle", NULL
  106. };
  107.  
  108. /* these are for detailing the memory statistics */
  109.  
  110. int memory_stats[4];
  111. char *memorynames[] = {
  112.     "K available, ", "K free, ", "K locked, ", "K virtual", NULL
  113. };
  114.  
  115. /* these detail per-process information */
  116.  
  117. static int nprocs;
  118. static int pref_len;
  119. static struct proc_detail *pd;
  120. static struct proc_detail **pref;
  121.  
  122. /* inq_stats structures and the STAT_DESCRs that use them */
  123.  
  124. static struct proc_config stat_pc;
  125. static struct vm_config stat_vm;
  126. static struct class_stats stat_class;
  127. static struct proc_summary stat_ps;
  128. static struct cpu_stats stat_cpu;
  129.  
  130. static struct stat_descr sd_procconfig = {
  131.     NULL,        /* sd_next */
  132.     SUBSYS_PROC,    /* sd_subsys */
  133.     PROCTYPE_CONFIG,    /* sd_type */
  134.     0,            /* sd_options */
  135.     0,            /* sd_objid */
  136.     &stat_pc,        /* sd_addr */
  137.     sizeof(stat_pc),    /* sd_size */
  138.     0,            /* sd_status */
  139.     0,            /* sd_sizeused */
  140.     0            /* sd_time */
  141. };
  142.  
  143. static struct stat_descr sd_memory = {
  144.     NULL,        /* sd_next */
  145.     SUBSYS_VM,        /* sd_subsys */
  146.     VMTYPE_SYSTEM,    /* sd_type */
  147.     0,            /* sd_options */
  148.     0,            /* sd_objid */
  149.     &stat_vm,        /* sd_addr */
  150.     sizeof(stat_vm),    /* sd_size */
  151.     0,            /* sd_status */
  152.     0,            /* sd_sizeused */
  153.     0            /* sd_time */
  154. };
  155.  
  156. static struct stat_descr sd_class = {
  157.     NULL,        /* sd_next */
  158.     SUBSYS_CPU,        /* sd_subsys */
  159.     CPUTYPE_CLASS,    /* sd_type */
  160.     0,            /* sd_options */
  161.     UMAXCLASS,        /* sd_objid */
  162.     &stat_class,    /* sd_addr */
  163.     sizeof(stat_class),    /* sd_size */
  164.     0,            /* sd_status */
  165.     0,            /* sd_sizeused */
  166.     0            /* sd_time */
  167. };
  168.  
  169. static struct stat_descr sd_procsummary = {
  170.     NULL,        /* sd_next */
  171.     SUBSYS_PROC,    /* sd_subsys */
  172.     PROCTYPE_SUMMARY,    /* sd_type */
  173.     0,            /* sd_options */
  174.     0,            /* sd_objid */
  175.     &stat_ps,        /* sd_addr */
  176.     sizeof(stat_ps),    /* sd_size */
  177.     0,            /* sd_status */
  178.     0,            /* sd_sizeused */
  179.     0            /* sd_time */
  180. };
  181.  
  182. static struct stat_descr sd_procdetail = {
  183.     NULL,        /* sd_next */
  184.     SUBSYS_PROC,    /* sd_subsys */
  185.     PROCTYPE_DETAIL,    /* sd_type */
  186.     PROC_DETAIL_ALL | PROC_DETAIL_ALLPROC,    /* sd_options */
  187.     0,            /* sd_objid */
  188.     NULL,        /* sd_addr */
  189.     0,            /* sd_size */
  190.     0,            /* sd_status */
  191.     0,            /* sd_sizeused */
  192.     0            /* sd_time */
  193. };
  194.  
  195. static struct stat_descr sd_cpu = {
  196.     NULL,        /* sd_next */
  197.     SUBSYS_CPU,        /* sd_subsys */
  198.     CPUTYPE_CPU,    /* sd_type */
  199.     0,            /* sd_options */
  200.     0,            /* sd_objid */
  201.     &stat_cpu,        /* sd_addr */
  202.     sizeof(stat_cpu),    /* sd_size */
  203.     0,            /* sd_status */
  204.     0,            /* sd_sizeused */
  205.     0            /* sd_time */
  206. };
  207.  
  208. /* precomputed values */
  209. static int numcpus;
  210.  
  211. machine_init(statics)
  212.  
  213. struct statics *statics;
  214.  
  215. {
  216.     if (inq_stats(2, &sd_procconfig, &sd_class) == -1)
  217.     {
  218.     perror("proc config");
  219.     return(-1);
  220.     }
  221.  
  222.     if (sd_procconfig.sd_status != 0)
  223.     {
  224.     fprintf(stderr, "stats status %d\n", sd_procconfig.sd_status);
  225.     }
  226.     
  227. #ifdef DEBUG
  228.     printf("pc_nprocs = %d\n", stat_pc.pc_nprocs);
  229.     printf("class_numcpus = %d\n", stat_class.class_numcpus);
  230. #endif
  231.  
  232.     /* things to remember */
  233.     numcpus = stat_class.class_numcpus;
  234.  
  235.     /* space to allocate */
  236.     nprocs = stat_pc.pc_nprocs;
  237.     pd = (struct proc_detail *)malloc(nprocs * sizeof(struct proc_detail));
  238.     pref = (struct proc_detail **)malloc(nprocs * sizeof(struct proc_detail *));
  239.     if (pd == NULL || pref == NULL)
  240.     {
  241.     fprintf(stderr, "top: can't allocate sufficient memory\n");
  242.     return(-1);
  243.     }
  244.  
  245.     /* pointers to assign */
  246.     sd_procdetail.sd_addr = pd;
  247.     sd_procdetail.sd_size = nprocs * sizeof(struct proc_detail);
  248.  
  249.     /* fill in the statics stuff */
  250.     statics->procstate_names = procstatenames;
  251.     statics->cpustate_names = cpustatenames;
  252.     statics->memory_names = memorynames;
  253.  
  254.     return(0);
  255. }
  256.  
  257. char *format_header(uname_field)
  258.  
  259. register char *uname_field;
  260.  
  261. {
  262.     register char *ptr;
  263.  
  264.     ptr = header + UNAME_START;
  265.     while (*uname_field != '\0')
  266.     {
  267.     *ptr++ = *uname_field++;
  268.     }
  269.  
  270.     return(header);
  271. }
  272.  
  273. get_system_info(si)
  274.  
  275. struct system_info *si;
  276.  
  277. {
  278.     /* get all status information at once */
  279.     inq_stats(1, &sd_memory);
  280.  
  281.  
  282.     /* fill in the memory statistics, converting to K */
  283.     memory_stats[0] = pagetok(stat_vm.vm_availmem);
  284.     memory_stats[1] = pagetok(stat_vm.vm_freemem);
  285.     memory_stats[2] = pagetok(stat_vm.vm_physmem - stat_vm.vm_availmem);
  286.     memory_stats[3] = 0;   /* ??? */
  287.  
  288.     /* set array pointers */
  289.     si->cpustates = cpu_states;
  290.     si->memory = memory_stats;
  291. }
  292.  
  293. static struct handle handle;
  294.  
  295. caddr_t get_process_info(si, sel, compare)
  296.  
  297. struct system_info *si;
  298. struct process_select *sel;
  299. int (*compare)();
  300.  
  301. {
  302.     register int i;
  303.     register int index;
  304.     register int total;
  305.     int active_procs;
  306.     char show_idle;
  307.     char show_system;
  308.     char show_uid;
  309.     char show_command;
  310.  
  311.     if (inq_stats(3, &sd_procsummary, &sd_cpu, &sd_procdetail) == -1)
  312.     {
  313.     perror("proc summary");
  314.     return(NULL);
  315.     }
  316.  
  317.     if (sd_procsummary.sd_status != 0)
  318.     {
  319.     fprintf(stderr, "stats status %d\n", sd_procsummary.sd_status);
  320.     }
  321.  
  322. #ifdef DEBUG
  323.     printf("nfree = %d\n", stat_ps.ps_nfree);
  324.     printf("nzombies = %d\n", stat_ps.ps_nzombies);
  325.     printf("nnrunnable = %d\n", stat_ps.ps_nrunnable);
  326.     printf("nwaiting = %d\n", stat_ps.ps_nwaiting);
  327.     printf("nstopped = %d\n", stat_ps.ps_nstopped);
  328.     printf("curtime0 = %d.%d\n", stat_cpu.cpu_curtime.tv_sec, stat_cpu.cpu_curtime.tv_usec);
  329.     printf("starttime0 = %d.%d\n", stat_cpu.cpu_starttime.tv_sec, stat_cpu.cpu_starttime.tv_usec);
  330.     printf("usertime0 = %d.%d\n", stat_cpu.cpu_usertime.tv_sec, stat_cpu.cpu_usertime.tv_usec);
  331.     printf("systime0 = %d.%d\n", stat_cpu.cpu_systime.tv_sec, stat_cpu.cpu_systime.tv_usec);
  332.     printf("idletime0 = %d.%d\n", stat_cpu.cpu_idletime.tv_sec, stat_cpu.cpu_idletime.tv_usec);
  333.     printf("intrtime0 = %d.%d\n", stat_cpu.cpu_intrtime.tv_sec, stat_cpu.cpu_intrtime.tv_usec);
  334. #endif
  335.  
  336.     /* fill in the process related counts */
  337.     process_states[P_SLEEP]  = stat_ps.ps_nwaiting;
  338.     process_states[P_RUN]    = stat_ps.ps_nrunnable;
  339.     process_states[P_ZOMBIE] = stat_ps.ps_nzombies;
  340.     process_states[P_STOP]   = stat_ps.ps_nstopped;
  341.     process_states[P_FREE]   = stat_ps.ps_nfree;
  342.     si->procstates = process_states;
  343.     si->p_total = stat_ps.ps_nzombies +
  344.                   stat_ps.ps_nrunnable +
  345.                   stat_ps.ps_nwaiting +
  346.                   stat_ps.ps_nstopped;
  347.     si->p_active = 0;
  348.     si->last_pid = -1;
  349.  
  350.     /* calculate load averages, the ENCORE way! */
  351.     /* this code was inspiried by the program cpumeter */
  352.     i = total = 0;
  353.     index = stat_ps.ps_nrunidx;
  354.  
  355.     /* we go in three cumulative steps:  one for each avenrun measure */
  356.     /* we are (once again) sacrificing code size for speed */
  357.     while (i < MINUTES(1))
  358.     {
  359.     if (index < 0)
  360.     {
  361.         index = PS_NRUNSIZE - 1;
  362.     }
  363.     total += stat_ps.ps_nrun[index--];
  364.     i++;
  365.     }
  366.     si->load_avg[0] = (double)total / MINUTES(1);
  367.     while (i < MINUTES(5))
  368.     {
  369.     if (index < 0)
  370.     {
  371.         index = PS_NRUNSIZE - 1;
  372.     }
  373.     total += stat_ps.ps_nrun[index--];
  374.     i++;
  375.     }
  376.     si->load_avg[1] = (double)total / MINUTES(5);
  377.     while (i < MINUTES(15))
  378.     {
  379.     if (index < 0)
  380.     {
  381.         index = PS_NRUNSIZE - 1;
  382.     }
  383.     total += stat_ps.ps_nrun[index--];
  384.     i++;
  385.     }
  386.     si->load_avg[2] = (double)total / (double)MINUTES(15);
  387.  
  388.     /* grab flags out of process_select for speed */
  389.     show_idle = sel->idle;
  390.     show_system = sel->system;
  391.     show_uid = sel->uid != -1;
  392.     show_command = sel->command != NULL;
  393.  
  394.     /*
  395.      *  Build a list of pointers to interesting proc_detail structures.
  396.      *  inq_stats will return a proc_detail structure for every currently
  397.      *  existing process.
  398.      */
  399.     {
  400.     register struct proc_detail *pp;
  401.     register struct proc_detail **prefp;
  402.     register double virttime;
  403.     register double now;
  404.  
  405.     /* pointer to destination array */
  406.     prefp = pref;
  407.     active_procs = 0;
  408.  
  409.     /* calculate "now" based on inq_stats retrieval time */
  410.     now = TVTODOUBLE(sd_procdetail.sd_time);
  411.  
  412.     /*
  413.      * Note: we will calculate the number of processes from
  414.      * procdetail.sd_sizeused just in case there is an inconsistency
  415.      * between it and the procsummary information.
  416.      */
  417.     total = sd_procdetail.sd_sizeused / sizeof(struct proc_detail);
  418.     for (pp = pd, i = 0; i < total; pp++, i++)
  419.     {
  420.         /*
  421.          *  Place pointers to each interesting structure in pref[]
  422.          *  and compute something akin to %cpu usage.  Computing %cpu
  423.          *  is really hard with the information that inq_stats gives
  424.          *  us, so we do the best we can based on the "virtual time"
  425.          *  and cpu time fields.  We also need a place to store this
  426.          *  computation so that we only have to do it once.  So we will
  427.          *  borrow one of the int fields in the proc_detail, and set a
  428.          *  #define accordingly.
  429.          *
  430.          *  We currently have no good way to determine if a process is
  431.          *  "idle", so we ignore the sel->idle flag.
  432.          */
  433. #define pd_pctcpu pd_swrss
  434.  
  435.         if ((show_system || ((pp->pd_flag & SSYS) == 0)) &&
  436.         ((pp->pd_flag & SZOMBIE) == 0) &&
  437.         (!show_uid || pp->pd_uid == (uid_t)sel->uid) &&
  438.         (!show_command || strcmp(sel->command, pp->pd_command) == 0))
  439.         {
  440.         /* calculate %cpu as best we can */
  441.         /* first, calculate total "virtual" cputime */
  442.         pp->pd_virttime = virttime = TVTODOUBLE(pp->pd_utime) +
  443.                          TVTODOUBLE(pp->pd_stime);
  444.  
  445.         /* %cpu is total cpu time over total wall time */
  446.         /* we express this as a percentage * 10 */
  447.         pp->pd_pctcpu = (int)(1000 * (virttime /
  448.                       (now - TVTODOUBLE(pp->pd_starttime))));
  449.  
  450.         /* store pointer to this record and move on */
  451.         *prefp++ = pp;
  452.         active_procs++;
  453.         }
  454.     }
  455.     }
  456.  
  457.     /* if requested, sort the "interesting" processes */
  458.     if (compare != NULL)
  459.     {
  460.     qsort((char *)pref, active_procs,
  461.           sizeof(struct proc_detail *),
  462.           compare);
  463.     }
  464.  
  465.     si->p_active = pref_len = active_procs;
  466.  
  467.     /* pass back a handle */
  468.     handle.next_proc = pref;
  469.     handle.remaining = active_procs;
  470.     return((caddr_t)&handle);
  471. }
  472.  
  473. char fmt[MAX_COLS];        /* static area where result is built */
  474.  
  475. char *format_next_process(handle, get_userid)
  476.  
  477. caddr_t handle;
  478. char *(*get_userid)();
  479.  
  480. {
  481.     register struct proc_detail *pp;
  482.     register long cputime;
  483.     struct handle *hp;
  484.  
  485.     /* find and remember the next proc structure */
  486.     hp = (struct handle *)handle;
  487.     pp = *(hp->next_proc++);
  488.     hp->remaining--;
  489.     
  490.  
  491.     /* set the cputime */
  492.     cputime = pp->pd_utime.tv_sec + pp->pd_stime.tv_sec;
  493.  
  494.     /* calculate the base for cpu percentages */
  495.  
  496. #ifdef notyet
  497.     /*
  498.      *  If there is more than one cpu then add the processor number to
  499.      *  the "run/" string.  Note that this will only show up if the
  500.      *  process is in the run state.  Also note:  this will break for
  501.      *  systems with more than 9 processors since the string will then
  502.      *  be more than 5 characters.  I'm still thinking about that one.
  503.      */
  504.     if (numcpus > 1)
  505.     {
  506. ???    state_abbrev[SRUN][4] = (pp->p_cpuid & 0xf) + '0';
  507.     }
  508. #endif
  509.  
  510.     /* format this entry */
  511.     sprintf(fmt,
  512.         Proc_format,
  513.         pp->pd_pid,
  514.         (*get_userid)(pp->pd_uid),
  515.         pp->pd_pri,            /* PZERO ??? */
  516.         pp->pd_nice,        /* NZERO ??? */
  517.         format_k(pagetok(PROCSIZE(pp))),
  518.         format_k(pagetok(pp->pd_rssize)),
  519.         state_abbrev[pp->pd_state],
  520.         format_time((long)(pp->pd_virttime)),
  521.         (double)pp->pd_pctcpu / 10.,
  522.         printable(pp->pd_command));
  523.  
  524.     /* return the result */
  525.     return(fmt);
  526. }
  527.  
  528. /*
  529.  *  proc_compare - comparison function for "qsort"
  530.  *    Compares the resource consumption of two processes using five
  531.  *      distinct keys.  The keys (in descending order of importance) are:
  532.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  533.  *      memory usage.  The process states are ordered according to the
  534.  *    premutation array "sorted_state" with higher numbers being sorted
  535.  *    before lower numbers.
  536.  */
  537.  
  538. static unsigned char sorted_state[] =
  539. {
  540.     0,    /* not used        */
  541.     0,    /* not used        */
  542.     1,    /* wait            */
  543.     6,    /* run            */
  544.     3,    /* start        */
  545.     4,    /* stop         */
  546.     5,    /* exec            */
  547.     2    /* event        */
  548. };
  549.  
  550. proc_compare(pp1, pp2)
  551.  
  552. struct proc **pp1;
  553. struct proc **pp2;
  554.  
  555. {
  556.     register struct proc_detail *p1;
  557.     register struct proc_detail *p2;
  558.     register int result;
  559.  
  560.     /* remove one level of indirection */
  561.     p1 = *pp1;
  562.     p2 = *pp2;
  563.  
  564.     /* compare percent cpu (pctcpu) */
  565.     if ((result = p2->pd_pctcpu - p1->pd_pctcpu) == 0)
  566.     {
  567.     /* use process state to break the tie */
  568.     if ((result = sorted_state[p2->pd_state] -
  569.               sorted_state[p1->pd_state])  == 0)
  570.     {
  571.         /* use priority to break the tie */
  572.         if ((result = p2->pd_pri - p1->pd_pri) == 0)
  573.         {
  574.         /* use resident set size (rssize) to break the tie */
  575.         if ((result = p2->pd_rssize - p1->pd_rssize) == 0)
  576.         {
  577.             /* use total memory to break the tie */
  578.             result = PROCSIZE(p2) - PROCSIZE(p1);
  579.         }
  580.         }
  581.     }
  582.     }
  583.  
  584.     return(result);
  585. }
  586.  
  587. /*
  588.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  589.  *        the process does not exist.
  590.  *        It is EXTREMLY IMPORTANT that this function work correctly.
  591.  *        If top runs setuid root (as in SVR4), then this function
  592.  *        is the only thing that stands in the way of a serious
  593.  *        security problem.  It validates requests for the "kill"
  594.  *        and "renice" commands.
  595.  */
  596.  
  597. int proc_owner(pid)
  598.  
  599. int pid;
  600.  
  601. {
  602.     register int cnt;
  603.     register struct proc_detail **prefp;
  604.     register struct proc_detail *pp;
  605.  
  606.     prefp = pref;
  607.     cnt = pref_len;
  608.     while (--cnt >= 0)
  609.     {
  610.     if ((pp = *prefp++)->pd_pid == pid)
  611.     {
  612.         return(pp->pd_uid);
  613.     }
  614.     }
  615.     return(-1);
  616. }
  617.