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

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