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

Revision 16185, 14.0 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 Convex OS 11.X
3 *
4 * SYNOPSIS:  any C2XX running Convex OS 11.X.
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for Convex OS.11.X
8 * Most of it was stolen from m_sunos4.c which was written by
9 * William LeFebvre <wnl@groupsys.com>
10 * Works for:
11 *      Convex OS 11.1
12 *
13 * CFLAGS: -DHAVE_GETOPT
14 * MODE: 2111
15 * UID: root
16 * GID: kmem
17 * INSTALL: /usr/bin/install
18 *
19 * AUTHOR: William L. Jones jones@chpc.utexas.edu
20 *         minor format changes Warren Vosper <warrenv@convex.com>
21 */
22
23#include <sys/types.h>
24#include <sys/proc.h>
25#include <sys/user.h>
26#include <sys/fcntl.h>
27#include <sys/file.h>
28#include <nlist.h>
29#include <stdio.h>
30#include <sys/dk.h>
31#include <sys/vmmeter.h>
32
33#include "top.h"
34#include "machine.h"
35
36/*
37 * Defines.
38 */
39#define KMEM    "/dev/kmem"
40#define VMUNIX  "/vmunix"
41#define pagetok(size) ((size) << 2)
42
43struct _oldproc {
44        pid_t  p_pid;
45        float  p_pctcpu;
46};
47
48/*
49 * Globals.
50 */
51static int kmem;
52static unsigned long    proc_addr;
53static          int     nproc;
54static unsigned long    avenrun_offset;
55static unsigned long    total_offset;
56static unsigned long    cp_time_offset;
57static struct   proc    *proc;
58static struct   _oldproc *oldproc;
59static          long    cp_time[MAXCPUS][CPUSTATES];
60static          long    cp_old[CPUSTATES];
61static          long    cp_diff[CPUSTATES];
62static          double  avenrun[3];
63static struct   proc    **pref;
64static          int     pref_len = 0;
65static double           logcpu;
66static int              ccpu;
67static struct   vmtotal vmtotal;
68
69/*
70 * Defines
71 */
72
73
74/*
75 * Extenrals.
76 */
77extern long percentages();
78
79/*
80 * nlist arrary.
81 */
82#define X_AVENRUN       0
83#define X_NPROC         1
84#define X_PROC          2
85#define X_TOTAL         3
86#define X_CP_TIME       4
87#define X_CCPU          5
88
89static struct nlist nlst[] = {
90    { "_avenrun" },              /* 0 */
91    { "_nproc" },                /* 1 */
92    { "_proc" },                 /* 2 */
93    { "_total"},                 /* 3 */
94    { "_cp_time"},               /* 4 */
95    { "_ccpu"},                  /* 5 */
96    { "" },
97};
98
99/* declarations for load_avg */
100#include "loadavg.h"
101
102/* define what weighted cpu is.  */
103#define weighted_cpu(pp) (*(float *)&pp->p_genid)
104
105/* get_process_info passes back a handle.  This is what it looks like: */
106
107struct handle
108{
109    struct proc **next_proc;    /* points to next valid proc pointer */
110    int remaining;              /* number of pointers remaining */
111};
112
113
114
115/*
116 *  These definitions control the format of the per-process area
117 */
118
119static char header[] =
120  "  PID X           PRI  NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
121/* 0123456   -- field to fill in starts at header+6 */
122#define UNAME_START 6
123
124#define Proc_format \
125        "%5d %-8.8s %7.2g %4d %5s %5s %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"
126
127
128int process_states[SSTOP+1];
129char *procstatenames[] = {
130    "", " running, ", " idle, ", " zombie, ", " sleeping, ",
131    " stopped, ",
132    NULL
133};
134
135char *state_abbrev[] =
136{
137    "init", "run", "idl", "zomb", "sleep", "stop"
138};
139
140
141/* these are for detailing the cpu states */
142
143int cpu_states[5];
144char *cpustatenames[] = {
145    "user", "nice", "system", "idle", NULL
146};
147
148/* these are for detailing the memory statistics */
149
150int memory_stats[4];
151char *memorynames[] = {
152    "M virt, ", "M real, ", "M free, ", NULL
153};
154
155/* useful externals */
156extern int errno;
157extern char *sys_errlist[];
158
159long lseek();
160long time();
161long percentages();
162
163machine_init(statics)
164
165struct statics *statics;
166
167{
168   
169    if ((kmem = open(KMEM, O_RDONLY)) == -1) {
170        perror(KMEM);
171        return(-1);
172    }
173   
174    (void)nlist(VMUNIX, nlst);
175   
176    if (nlst[0].n_type == 0) {
177        fprintf(stderr, "top: nlist failed\n");
178        return -1;
179    }
180    /*
181     * Get the sysmbol value of of kmem
182     */
183    (void) getkval(nlst[X_PROC].n_value, (int *)(&proc_addr), sizeof(proc_addr),
184            nlst[X_PROC].n_un.n_name);
185    (void) getkval(nlst[X_NPROC].n_value,&nproc,              sizeof(nproc),
186            nlst[X_NPROC].n_un.n_name);
187    (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu),      sizeof(ccpu),
188            nlst[X_CCPU].n_un.n_name);
189
190    /* this is used in calculating WCPU -- calculate it ahead of time */
191    logcpu = log(loaddouble(ccpu));
192
193    /*
194     * Allocate storage.
195     */
196    proc = (struct proc *)malloc(nproc * sizeof(struct proc));
197    oldproc = (struct _oldproc *)malloc(nproc * sizeof(struct _oldproc));
198    memset((char *)oldproc, 0, nproc*sizeof(struct _oldproc));
199    pref = (struct proc **)malloc(nproc * sizeof(struct proc *));
200
201    /*
202     * stash away certain offsets for later us
203     */
204    avenrun_offset = nlst[X_AVENRUN].n_value;
205    total_offset = nlst[X_TOTAL].n_value;
206    cp_time_offset = nlst[X_CP_TIME].n_value;
207
208    /*
209     * fill in the statics information
210     */
211    statics->procstate_names = procstatenames;
212    statics->cpustate_names = cpustatenames;
213    statics->memory_names = memorynames;
214
215
216    return(0);
217}
218
219char *format_header(uname_field)
220
221register char *uname_field;
222
223{
224    register char *ptr;
225
226    ptr = header + UNAME_START;
227    while (*uname_field != '\0')
228    {
229        *ptr++ = *uname_field++;
230    }
231
232    return(header);
233}
234
235get_system_info(si)
236
237struct system_info *si;
238
239{
240    long cpu[CPUSTATES];
241    int i,j;
242    int total;
243
244    /* get the cp_time array */
245    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
246                   "_cp_time");
247
248    /* get load average array */
249    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
250                   "_avenrun");
251   
252    /* get memory stats */
253    (void) getkval(total_offset,   (int *)&vmtotal,    sizeof(vmtotal),
254                   "_total");
255
256    for (i=0; i<3; i++) {
257        si->load_avg[i] = avenrun[i];
258    }
259
260    for (i=0; i<CPUSTATES; i++) {
261        cpu[i] = 0;
262        for (j=0; j<MAXCPUS; j++) {
263           cpu[i] += cp_time[j][i];
264        }
265    }
266    total = percentages(CPUSTATES, cpu_states, cpu, cp_old, cp_diff);
267    si->cpustates = cpu_states;
268    memory_stats[0] = pagetok(vmtotal.t_vm)/1024.0;
269    memory_stats[1] = pagetok(vmtotal.t_rm)/1024.0;
270    memory_stats[2] = pagetok(vmtotal.t_free)/1024.0;
271    memory_stats[3] = -1;
272    si->memory = memory_stats;
273    si->procstates = process_states;
274    si->p_total = 0;
275    si->p_active = 0;
276    si->last_pid = -1;
277}
278   
279
280static struct handle handle;
281
282caddr_t get_process_info(si, sel, compare)
283
284struct system_info *si;
285struct process_select *sel;
286int (*compare)();
287
288{
289    register int i;
290    register int total_procs;
291    register int active_procs;
292    register struct proc **prefp;
293    register struct proc *pp;
294    /* these are copied out of sel for speed */
295    int show_idle;
296    int show_system;
297    int show_uid;
298    int show_command;
299
300    static struct timeval lasttime = {0, 0};
301    struct timeval thistime;
302    struct timezone tzp;
303    double timediff;
304    double alpha, beta;
305
306    gettimeofday(&thistime,&tzp);
307    /*
308     * To avoid divides, we keep times in nanoseconds.  This is
309     * scaled by 1e7 rather than 1e9 so that when we divide we
310     * get percent.
311     */
312    if (lasttime.tv_sec)
313        timediff = ((double) thistime.tv_sec - lasttime.tv_sec);
314    else
315        timediff = 1;
316    /*
317     * constants for exponential average.  avg = alpha * new + beta * avg
318     * The goal is 50% decay in 30 sec.  However if the sample period
319     * is greater than 30 sec, there's not a lot we can do.
320     */
321    if (timediff < 30) {
322        alpha = 0.5 * (timediff / 30.0);
323        beta = 1.0 - alpha;
324    } else {
325        alpha = 0.5;
326        beta = 0.5;
327    }
328
329    /* set up flags which define what we are going to select */
330    show_idle = sel->idle;
331    show_system = sel->system;
332    show_uid = sel->uid != -1;
333    show_command = sel->command != NULL;
334
335    /* read all the proc structures in one fell swoop */
336
337    (void) getkval(proc_addr, (int *)proc, nproc * sizeof(struct proc),
338                "proc array");
339
340    /* count up process states and get pointers to interesting procs */
341
342    total_procs = 0;
343    active_procs = 0;
344    bzero((char *)process_states, sizeof(process_states));
345    prefp = pref;     
346    for (pp = proc, i = 0; i < nproc; pp++, i++)
347    {
348        if (oldproc[i].p_pid == pp->p_pid) {
349            weighted_cpu(pp) = pctdouble(oldproc[i].p_pctcpu)*beta +
350                               pctdouble(pp->p_pctcpu)*alpha;
351        } else {
352            weighted_cpu(pp) = pctdouble(oldproc[i].p_pctcpu);
353        }
354        oldproc[i].p_pid = pp->p_pid;
355        oldproc[i].p_pctcpu = pp->p_pctcpu;
356
357        if (pp->p_stat != 0 &&
358            (show_system || ((pp->p_flag & SSYS) == 0)))
359        {
360            total_procs++;
361            process_states[pp->p_stat]++;
362            if ((pp->p_stat != SZOMB) &&
363                (show_idle || (pp->p_stat == SRUN)) &&
364                (!show_uid || pp->p_uid == (uid_t)sel->uid))
365            {
366                *prefp++ = pp;
367                active_procs++;
368            }
369        }
370    }
371
372    /* if requested, sort the "interesting" processes */
373    if (compare != NULL)
374    {
375        qsort((char *)pref, active_procs, sizeof(struct proc *), compare);
376    }
377
378
379    lasttime = thistime;
380
381    si->p_total = pref_len = total_procs;
382    si->p_active = active_procs;
383
384    /* pass back a handle */
385    handle.next_proc = pref;
386    handle.remaining = active_procs;
387    return((caddr_t)&handle);
388}
389
390char fmt[128];          /* static area where result is built */
391
392
393char *format_next_process(handle, get_userid)
394
395caddr_t handle;
396char *(*get_userid)();
397
398{
399    register struct proc *pp;
400    register long cputime;
401    register double pct;
402    struct user u;
403    struct handle *hp;
404    long rrsize = 0;
405    long size = 0;
406
407    /* find and remember the next proc structure */
408    hp = (struct handle *)handle;
409    pp = *(hp->next_proc++);
410    hp->remaining--;
411   
412    /* get the process's user struct and set cputime */
413    if (getu(pp, &u) == -1)
414    {
415        (void) strcpy(u.u_comm, "<swapped>");
416        cputime = 0;
417        size = rrsize = 0;
418    }
419    else
420    {
421        /* set u_comm for system processes */
422        if (u.u_comm[0] == '\0')
423        {
424            if (pp->p_pid == 0)
425            {
426                (void) strcpy(u.u_comm, "swappout");
427            }
428            else if (pp->p_pid == 1)
429            {
430                (void) strcpy(u.u_comm, "init");
431            }
432            else if (pp->p_pid == 2)
433            {
434                (void) strcpy(u.u_comm, "pageout");
435            }
436            else if (pp->p_pid == 3)
437            {
438                (void) strcpy(u.u_comm, "swapin");
439            }
440            else if (pp->p_pid == 4)
441            {
442                (void) strcpy(u.u_comm, "scheduler");
443            }
444            else if (pp->p_pid == 5)
445            {
446                (void) strcpy(u.u_comm, "interrupt");
447            }
448        }
449        cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
450        size = u.u_tsize + u.u_dsize + u.u_ssize;
451        rrsize = u.u_ru.ru_maxrss;
452    }
453
454    /* calculate the base for cpu percentages */
455    pct = pctdouble(pp->p_pctcpu);
456
457   
458    /* format this entry */
459    sprintf(fmt,
460            Proc_format,
461            pp->p_pid,
462            (*get_userid)(pp->p_uid),
463            pp->p_pri,
464            pp->p_nice - NZERO,
465            format_k(pagetok(size)),
466            format_k(pagetok(rrsize)),
467            state_abbrev[pp->p_stat],
468            cputime / 60l,
469            cputime % 60l,
470            100.0 * weighted_cpu(pp),
471            100.0 * pct,
472            printable(u.u_comm));
473
474    /* return the result */
475    return(fmt);
476}
477
478getu(p, u)
479register struct proc *p;
480struct user *u;
481{
482    if (p->p_uaddr) {
483       
484        if (lseek(kmem, (long)p->p_uaddr, L_SET) != -1) {
485            if (read(kmem, (char *)u, sizeof(struct user)) ==
486                sizeof(struct user)) {
487                return 0;
488            }
489        }
490    }
491    return -1;
492}
493
494/*
495 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
496 *      "offset" is the byte offset into the kernel for the desired value,
497 *      "ptr" points to a buffer into which the value is retrieved,
498 *      "size" is the size of the buffer (and the object to retrieve),
499 *      "refstr" is a reference string used when printing error meessages,
500 *          if "refstr" starts with a '!', then a failure on read will not
501 *          be fatal (this may seem like a silly way to do things, but I
502 *          really didn't want the overhead of another argument).
503 *     
504 */
505
506getkval(offset, ptr, size, refstr)
507
508unsigned long offset;
509int *ptr;
510int size;
511char *refstr;
512
513{
514    if (lseek(kmem, (long)offset, L_SET) == -1) {
515        if (*refstr == '!')
516            refstr++;
517        (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
518                       refstr, strerror(errno));
519        quit(23);
520    }
521
522    if (read(kmem, (char *)ptr, size) != size)
523    {
524        if (*refstr == '!')
525        {
526            return(0);
527        }
528        else
529        {
530            fprintf(stderr, "top: kvm_read for %s: %s\n",
531                refstr, sys_errlist[errno]);
532            quit(23);
533        }
534    }
535    return(1);
536}
537   
538/* comparison routine for qsort */
539/* NOTE: this is specific to the BSD proc structure, but it should
540   give you a good place to start. */
541
542/*
543 *  proc_compare - comparison function for "qsort"
544 *      Compares the resource consumption of two processes using five
545 *      distinct keys.  The keys (in descending order of importance) are:
546 *      percent cpu, cpu ticks, state, resident set size, total virtual
547 *      memory usage.  The process states are ordered as follows (from least
548 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
549 *      array declaration below maps a process state index into a number
550 *      that reflects this ordering.
551 */
552
553static unsigned char sorted_state[] =
554{
555    0,  /* not used             */
556    3,  /* sleep                */
557    1,  /* ABANDONED (WAIT)     */
558    6,  /* run                  */
559    5,  /* start                */
560    2,  /* zombie               */
561    4   /* stop                 */
562};
563 
564proc_compare(pp1, pp2)
565
566struct proc **pp1;
567struct proc **pp2;
568
569{
570    register struct proc *p1;
571    register struct proc *p2;
572    register int result;
573    register pctcpu lresult;
574
575    /* remove one level of indirection */
576    p1 = *pp1;
577    p2 = *pp2;
578
579    /* compare percent cpu (pctcpu) */
580    if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
581    {
582        /* use cpticks to break the tie */
583        if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
584        {
585            /* use process state to break the tie */
586            if ((result = sorted_state[p2->p_stat] -
587                          sorted_state[p1->p_stat])  == 0)
588            {
589                /* use priority to break the tie */
590                if ((result = p2->p_pri - p1->p_pri) == 0)
591                {
592                    result = 0;
593                }
594            }
595        }
596    }
597    else
598    {
599        result = lresult < 0 ? -1 : 1;
600    }
601
602    return(result);
603}
604
605int proc_owner(pid)
606
607int pid;
608
609{
610    register int cnt;
611    register struct proc **prefp;
612    register struct proc *pp;
613
614    prefp = pref;
615    cnt =  pref_len;
616    while (--cnt >= 0)
617    {
618        if ((pp = *prefp++)->p_pid == (pid_t)pid)
619        {
620            return((int)pp->p_uid);
621        }
622    }
623    return(-1);
624}
Note: See TracBrowser for help on using the repository browser.