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

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