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

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