source: trunk/third/top/machine/m_bsd44a.c @ 9084

Revision 9084, 14.9 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9083, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  For a pre-release 4.4BSD system
5 *            Note memory statistisc and process sizes could be wrong,
6 *            by ps gets them wrong too...
7 *
8 * DESCRIPTION:
9 * This is the machine-dependent module for BSD4.4 (prerelease alpha)
10 * Works for:
11 *      hp300
12 *
13 * LIBS: -lkvm
14 *
15 * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
16 */
17
18#include <sys/types.h>
19#include <sys/signal.h>
20#include <sys/param.h>
21
22#include "os.h"
23#include <stdio.h>
24#include <nlist.h>
25#include <math.h>
26#include <kvm.h>
27#include <sys/errno.h>
28#include <sys/kinfo.h>
29#include <sys/kinfo_proc.h>
30#ifdef notyet
31#define time __time
32#define hz __hz
33#include <sys/kernel.h>
34#undef time
35#undef hz
36#endif
37#include <sys/dir.h>
38#include <sys/dkstat.h>
39#include <sys/file.h>
40#include <sys/time.h>
41
42
43#define DOSWAP
44
45static int check_nlist __P((struct nlist *));
46static int getkval __P((unsigned long, int *, int, char *));
47
48#include "top.h"
49#include "machine.h"
50#include "utils.h"
51
52#define VMUNIX  "/vmunix"
53#define KMEM    "/dev/kmem"
54#define MEM     "/dev/mem"
55#ifdef DOSWAP
56#define SWAP    "/dev/drum"
57#endif
58
59/* get_process_info passes back a handle.  This is what it looks like: */
60
61struct handle
62{
63    struct kinfo_proc **next_proc;      /* points to next valid proc pointer */
64    int remaining;              /* number of pointers remaining */
65};
66
67/* declarations for load_avg */
68#include "loadavg.h"
69
70#define PP(pp, field) ((pp)->kp_proc . field)
71#define EP(pp, field) ((pp)->kp_eproc . field)
72#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
73
74/* define what weighted cpu is.  */
75#define weighted_cpu(pct, pp) (PP((pp), p_time) == 0 ? 0.0 : \
76                         ((pct) / (1.0 - exp(PP((pp), p_time) * logcpu))))
77
78/* what we consider to be process size: */
79#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
80
81/* definitions for indices in the nlist array */
82#define X_CCPU          0
83#define X_CP_TIME       1
84#define X_HZ            2
85#define X_AVENRUN       3
86
87static struct nlist nlst[] = {
88    { "_ccpu" },                /* 0 */
89    { "_cp_time" },             /* 1 */
90    { "_hz" },                  /* 2 */
91    { "_averunnable" },         /* 3 */
92    { 0 }
93};
94
95/*
96 *  These definitions control the format of the per-process area
97 */
98
99static char header[] =
100  "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
101/* 0123456   -- field to fill in starts at header+6 */
102#define UNAME_START 6
103
104#define Proc_format \
105        "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.16s"
106
107
108/* process state names for the "STATE" column of the display */
109/* the extra nulls in the string "run" are for adding a slash and
110   the processor number when needed */
111
112char *state_abbrev[] =
113{
114    "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
115};
116
117
118static kvm_t *kd;
119
120/* values that we stash away in _init and use in later routines */
121
122static double logcpu;
123
124/* these are retrieved from the kernel in _init */
125
126static          long hz;
127static load_avg  ccpu;
128static          int  ncpu = 0;
129
130/* these are offsets obtained via nlist and used in the get_ functions */
131
132static unsigned long cp_time_offset;
133static unsigned long avenrun_offset;
134
135/* these are for calculating cpu state percentages */
136
137static long cp_time[CPUSTATES];
138static long cp_old[CPUSTATES];
139static long cp_diff[CPUSTATES];
140
141/* these are for detailing the process states */
142
143int process_states[7];
144char *procstatenames[] = {
145    "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
146    " zombie, ", " stopped, ",
147    NULL
148};
149
150/* these are for detailing the cpu states */
151
152int cpu_states[4];
153char *cpustatenames[] = {
154    "user", "nice", "system", "idle", NULL
155};
156
157/* these are for detailing the memory statistics */
158
159int memory_stats[8];
160char *memorynames[] = {
161    "Real: ", "K/", "K ", "Virt: ", "K/",
162    "K ", "Free: ", "K", NULL
163};
164
165/* these are for keeping track of the proc array */
166
167static int bytes;
168static int nproc;
169static int onproc = -1;
170static int pref_len;
171static struct kinfo_proc *pbase;
172static struct kinfo_proc **pref;
173
174/* these are for getting the memory statistics */
175
176static int pageshift;           /* log base 2 of the pagesize */
177
178/* define pagetok in terms of pageshift */
179
180#define pagetok(size) ((size) << pageshift)
181
182machine_init(statics)
183
184struct statics *statics;
185
186{
187    register int i = 0;
188    register int pagesize;
189
190    if ((kd = kvm_open(VMUNIX, MEM, SWAP, O_RDONLY, "kvm_open")) == NULL)
191        return -1;
192
193
194    /* get the list of symbols we want to access in the kernel */
195    (void) kvm_nlist(kd, nlst);
196    if (nlst[0].n_type == 0)
197    {
198        fprintf(stderr, "top: nlist failed\n");
199        return(-1);
200    }
201
202    /* make sure they were all found */
203    if (i > 0 && check_nlist(nlst) > 0)
204    {
205        return(-1);
206    }
207
208    /* get the symbol values out of kmem */
209    (void) getkval(nlst[X_HZ].n_value,     (int *)(&hz),        sizeof(hz),
210            nlst[X_HZ].n_name);
211    (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),      sizeof(ccpu),
212            nlst[X_CCPU].n_name);
213
214    /* stash away certain offsets for later use */
215    cp_time_offset = nlst[X_CP_TIME].n_value;
216    avenrun_offset = nlst[X_AVENRUN].n_value;
217
218    /* this is used in calculating WCPU -- calculate it ahead of time */
219    logcpu = log(loaddouble(ccpu));
220
221    pbase = NULL;
222    pref = NULL;
223    nproc = 0;
224    onproc = -1;
225    /* get the page size with "getpagesize" and calculate pageshift from it */
226    pagesize = getpagesize();
227    pageshift = 0;
228    while (pagesize > 1)
229    {
230        pageshift++;
231        pagesize >>= 1;
232    }
233
234    /* we only need the amount of log(2)1024 for our conversion */
235    pageshift -= LOG1024;
236
237    /* fill in the statics information */
238    statics->procstate_names = procstatenames;
239    statics->cpustate_names = cpustatenames;
240    statics->memory_names = memorynames;
241
242    /* all done! */
243    return(0);
244}
245
246char *format_header(uname_field)
247
248register char *uname_field;
249
250{
251    register char *ptr;
252
253    ptr = header + UNAME_START;
254    while (*uname_field != '\0')
255    {
256        *ptr++ = *uname_field++;
257    }
258
259    return(header);
260}
261
262get_system_info(si)
263
264struct system_info *si;
265
266{
267    long total;
268    load_avg avenrun[3];
269
270    /* get the cp_time array */
271    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
272                   "_cp_time");
273    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
274                   "_avenrun");
275
276    /* convert load averages to doubles */
277    {
278        register int i;
279        register double *infoloadp;
280        load_avg *avenrunp;
281
282#ifdef notyet
283        struct loadavg sysload;
284        int size;
285        getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
286#endif
287
288        infoloadp = si->load_avg;
289        avenrunp = avenrun;
290        for (i = 0; i < 3; i++)
291        {
292#ifdef notyet
293            *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
294#endif
295            *infoloadp++ = loaddouble(*avenrunp++);
296        }
297    }
298
299    /* convert cp_time counts to percentages */
300    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
301
302    /* sum memory statistics */
303    {
304        struct vmtotal total;
305        int size;
306
307        /* get total -- systemwide main memory usage structure */
308        getkerninfo(KINFO_METER, &total, &size, 0);
309        /* convert memory stats to Kbytes */
310        memory_stats[0] = -1;
311        memory_stats[1] = pagetok(total.t_arm);
312        memory_stats[2] = pagetok(total.t_rm);
313        memory_stats[3] = -1;
314        memory_stats[4] = pagetok(total.t_avm);
315        memory_stats[5] = pagetok(total.t_vm);
316        memory_stats[6] = -1;
317        memory_stats[7] = pagetok(total.t_free);
318    }
319
320    /* set arrays and strings */
321    si->cpustates = cpu_states;
322    si->memory = memory_stats;
323    si->last_pid = -1;
324}
325
326static struct handle handle;
327
328caddr_t get_process_info(si, sel, compare)
329
330struct system_info *si;
331struct process_select *sel;
332int (*compare)();
333
334{
335    register int i;
336    register int total_procs;
337    register int active_procs;
338    register struct kinfo_proc **prefp;
339    register struct kinfo_proc *pp;
340
341    /* these are copied out of sel for speed */
342    int show_idle;
343    int show_system;
344    int show_uid;
345    int show_command;
346
347   
348    pbase = kvm_getprocs(kd, KINFO_PROC_ALL, 0, &nproc);
349    if (nproc > onproc)
350        pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
351                * (onproc = nproc));
352    if (pref == NULL || pbase == NULL) {
353        (void) fprintf(stderr, "top: Out of memory.\n");
354        quit(23);
355    }
356    /* get a pointer to the states summary array */
357    si->procstates = process_states;
358
359    /* set up flags which define what we are going to select */
360    show_idle = sel->idle;
361    show_system = sel->system;
362    show_uid = sel->uid != -1;
363    show_command = sel->command != NULL;
364
365    /* count up process states and get pointers to interesting procs */
366    total_procs = 0;
367    active_procs = 0;
368    memset((char *)process_states, 0, sizeof(process_states));
369    prefp = pref;
370    for (pp = pbase, i = 0; i < nproc; pp++, i++)
371    {
372        /*
373         *  Place pointers to each valid proc structure in pref[].
374         *  Process slots that are actually in use have a non-zero
375         *  status field.  Processes with SSYS set are system
376         *  processes---these get ignored unless show_sysprocs is set.
377         */
378        if (PP(pp, p_stat) != 0 &&
379            (show_system || ((PP(pp, p_flag) & SSYS) == 0)))
380        {
381            total_procs++;
382            process_states[PP(pp, p_stat)]++;
383            if ((PP(pp, p_stat) != SZOMB) &&
384                (show_idle || (PP(pp, p_pctcpu) != 0) ||
385                 (PP(pp, p_stat) == SRUN)) &&
386                (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
387            {
388                *prefp++ = pp;
389                active_procs++;
390            }
391        }
392    }
393
394    /* if requested, sort the "interesting" processes */
395    if (compare != NULL)
396    {
397        qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
398    }
399
400    /* remember active and total counts */
401    si->p_total = total_procs;
402    si->p_active = pref_len = active_procs;
403
404    /* pass back a handle */
405    handle.next_proc = pref;
406    handle.remaining = active_procs;
407    return((caddr_t)&handle);
408}
409
410char fmt[MAX_COLS];             /* static area where result is built */
411
412char *format_next_process(handle, get_userid)
413
414caddr_t handle;
415char *(*get_userid)();
416
417{
418    register struct kinfo_proc *pp;
419    register long cputime;
420    register double pct;
421    int where;
422    struct handle *hp;
423
424    /* find and remember the next proc structure */
425    hp = (struct handle *)handle;
426    pp = *(hp->next_proc++);
427    hp->remaining--;
428   
429
430    /* get the process's user struct and set cputime */
431    if ((PP(pp, p_flag) & SLOAD) == 0) {
432        /*
433         * Print swapped processes as <pname>
434         */
435        char *comm = PP(pp, p_comm);
436#define COMSIZ sizeof(PP(pp, p_comm))
437        char buf[COMSIZ];
438        (void) strncpy(buf, comm, COMSIZ);
439        comm[0] = '<';
440        (void) strncpy(&comm[1], buf, COMSIZ - 2);
441        comm[COMSIZ - 2] = '\0';
442        (void) strncat(comm, ">", COMSIZ - 1);
443        comm[COMSIZ - 1] = '\0';
444    }
445
446    cputime = PP(pp, p_utime.tv_sec) + PP(pp, p_stime.tv_sec);
447
448    /* calculate the base for cpu percentages */
449    pct = pctdouble(PP(pp, p_pctcpu));
450
451    /* format this entry */
452    sprintf(fmt,
453            Proc_format,
454            PP(pp, p_pid),
455            (*get_userid)(EP(pp, e_pcred.p_ruid)),
456            PP(pp, p_pri) - PZERO,
457            PP(pp, p_nice) - NZERO,
458            format_k(pagetok(PROCSIZE(pp))),
459            format_k(pagetok(VP(pp, vm_rssize))),
460            state_abbrev[PP(pp, p_stat)],
461            format_time(cputime),
462            100.0 * weighted_cpu(pct, pp),
463            100.0 * pct,
464            printable(PP(pp, p_comm)));
465
466    /* return the result */
467    return(fmt);
468}
469
470
471/*
472 * check_nlist(nlst) - checks the nlist to see if any symbols were not
473 *              found.  For every symbol that was not found, a one-line
474 *              message is printed to stderr.  The routine returns the
475 *              number of symbols NOT found.
476 */
477
478static int check_nlist(nlst)
479
480register struct nlist *nlst;
481
482{
483    register int i;
484
485    /* check to see if we got ALL the symbols we requested */
486    /* this will write one line to stderr for every symbol not found */
487
488    i = 0;
489    while (nlst->n_name != NULL)
490    {
491        if (nlst->n_type == 0)
492        {
493            /* this one wasn't found */
494            fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
495            i = 1;
496        }
497        nlst++;
498    }
499
500    return(i);
501}
502
503
504/*
505 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
506 *      "offset" is the byte offset into the kernel for the desired value,
507 *      "ptr" points to a buffer into which the value is retrieved,
508 *      "size" is the size of the buffer (and the object to retrieve),
509 *      "refstr" is a reference string used when printing error meessages,
510 *          if "refstr" starts with a '!', then a failure on read will not
511 *          be fatal (this may seem like a silly way to do things, but I
512 *          really didn't want the overhead of another argument).
513 *     
514 */
515
516static int getkval(offset, ptr, size, refstr)
517
518unsigned long offset;
519int *ptr;
520int size;
521char *refstr;
522
523{
524    if (kvm_read(kd, offset, (char *) ptr, size) != size)
525    {
526        if (*refstr == '!')
527        {
528            return(0);
529        }
530        else
531        {
532            fprintf(stderr, "top: kvm_read for %s: %s\n",
533                refstr, strerror(errno));
534            quit(23);
535        }
536    }
537    return(1);
538}
539   
540/* comparison routine for qsort */
541
542/*
543 *  proc_compare - comparison function for "qsort"
544 *      Compares the resource consumption of two processes using five
545 *      distinct keys.  The keys (in descending order of importance) are:
546 *      percent cpu, cpu ticks, state, resident set size, total virtual
547 *      memory usage.  The process states are ordered as follows (from least
548 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
549 *      array declaration below maps a process state index into a number
550 *      that reflects this ordering.
551 */
552
553static unsigned char sorted_state[] =
554{
555    0,  /* not used             */
556    3,  /* sleep                */
557    1,  /* ABANDONED (WAIT)     */
558    6,  /* run                  */
559    5,  /* start                */
560    2,  /* zombie               */
561    4   /* stop                 */
562};
563 
564proc_compare(pp1, pp2)
565
566struct kinfo_proc **pp1;
567struct kinfo_proc **pp2;
568
569{
570    register struct kinfo_proc *p1;
571    register struct kinfo_proc *p2;
572    register int result;
573    register pctcpu lresult;
574
575    /* remove one level of indirection */
576    p1 = *pp1;
577    p2 = *pp2;
578
579    /* compare percent cpu (pctcpu) */
580    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
581    {
582        /* use cpticks to break the tie */
583        if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
584        {
585            /* use process state to break the tie */
586            if ((result = sorted_state[PP(p2, p_stat)] -
587                          sorted_state[PP(p1, p_stat)])  == 0)
588            {
589                /* use priority to break the tie */
590                if ((result = PP(p2, p_pri) - PP(p1, p_pri)) == 0)
591                {
592                    /* use resident set size (rssize) to break the tie */
593                    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
594                    {
595                        /* use total memory to break the tie */
596                        result = PROCSIZE(p2) - PROCSIZE(p1);
597                    }
598                }
599            }
600        }
601    }
602    else
603    {
604        result = lresult < 0 ? -1 : 1;
605    }
606
607    return(result);
608}
609
610
611/*
612 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
613 *              the process does not exist.
614 *              It is EXTREMLY IMPORTANT that this function work correctly.
615 *              If top runs setuid root (as in SVR4), then this function
616 *              is the only thing that stands in the way of a serious
617 *              security problem.  It validates requests for the "kill"
618 *              and "renice" commands.
619 */
620
621int proc_owner(pid)
622
623int pid;
624
625{
626    register int cnt;
627    register struct proc **prefp;
628    register struct proc *pp;
629
630    prefp = pref;
631    cnt = pref_len;
632    while (--cnt >= 0)
633    {
634        pp = *prefp++; 
635        if (PP(pp, p_pid) == (pid_t)pid)
636        {
637            return((int)EP(pp, e_pcred.p_ruid));
638        }
639    }
640    return(-1);
641}
Note: See TracBrowser for help on using the repository browser.