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

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