source: trunk/third/top/machine/m_umax.c @ 16185

Revision 16185, 15.8 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16184, which included commits to RCS files with non-trunk default branches.
Line 
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 <wnl@groupsys.com>
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
21struct 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
40struct 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
69static 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
79char *state_abbrev[] =
80{
81    "", "", "wait", "run", "start", "stop", "exec", "event"
82};
83
84/* these are for detailing the process states */
85
86int process_states[5];
87char *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
103int cpu_states[4];
104char *cpustatenames[] = {
105    "user", "nice", "system", "idle", NULL
106};
107
108/* these are for detailing the memory statistics */
109
110int memory_stats[4];
111char *memorynames[] = {
112    "K available, ", "K free, ", "K locked, ", "K virtual", NULL
113};
114
115/* these detail per-process information */
116
117static int nprocs;
118static int pref_len;
119static struct proc_detail *pd;
120static struct proc_detail **pref;
121
122/* inq_stats structures and the STAT_DESCRs that use them */
123
124static struct proc_config stat_pc;
125static struct vm_config stat_vm;
126static struct class_stats stat_class;
127static struct proc_summary stat_ps;
128static struct cpu_stats stat_cpu;
129
130static 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
143static 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
156static 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
169static 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
182static 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
195static 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 */
209static int numcpus;
210
211machine_init(statics)
212
213struct 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
257char *format_header(uname_field)
258
259register 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
273get_system_info(si)
274
275struct 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
293static struct handle handle;
294
295caddr_t get_process_info(si, sel, compare)
296
297struct system_info *si;
298struct process_select *sel;
299int (*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
473char fmt[MAX_COLS];             /* static area where result is built */
474
475char *format_next_process(handle, get_userid)
476
477caddr_t handle;
478char *(*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
538static 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 
550proc_compare(pp1, pp2)
551
552struct proc **pp1;
553struct 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
597int proc_owner(pid)
598
599int 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}
Note: See TracBrowser for help on using the repository browser.