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

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