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

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