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

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