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

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