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

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