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

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