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

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