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

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