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

Revision 16185, 16.6 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 hp9000 running hpux version 10.x
5 *
6 * DESCRIPTION:
7 * This is the machine-dependent module for HPUX 10/11 that uses pstat.
8 * It has been tested on HP/UX 10.01, 10.20, and 11.00.  It is presumed
9 * to work also on 10.10.
10 * Idle processes are marked by being either runnable or having a %CPU
11 * of at least 0.1%.  This fraction is defined by CPU_IDLE_THRESH and
12 * can be adjusted at compile time.
13 *
14 * CFLAGS: -DHAVE_GETOPT
15 *
16 * LIBS:
17 *
18 * AUTHOR: John Haxby <john_haxby@hp.com>
19 * AUTHOR: adapted from Rich Holland <holland@synopsys.com>
20 * AUTHOR: adapted from Kevin Schmidt <kevin@mcl.ucsb.edu>
21 */
22
23#include <stdio.h>
24#include <errno.h>
25#include <unistd.h>
26#include <ctype.h>
27#include <signal.h>
28#include <nlist.h>
29#include <fcntl.h>
30#include <stdlib.h>
31
32#include <sys/types.h>
33#include <sys/param.h>
34#include <sys/pstat.h>
35#include <sys/dk.h>
36#include <sys/stat.h>
37#include <sys/dirent.h>
38
39#include "top.h"
40#include "machine.h"
41#include "utils.h"
42
43/*
44 * The idle threshold (CPU_IDLE_THRESH) is an extension to the normal
45 * idle process check.  Basically, we regard a process as idle if it is
46 * both asleep and using less that CPU_IDLE_THRESH percent cpu time.  I
47 * believe this makes the "i" option more useful, but if you don't, add
48 * "-DCPU_IDLE_THRESH=0.0" to the CFLAGS.
49 */
50#ifndef CPU_IDLE_THRESH
51#define CPU_IDLE_THRESH 0.1
52#endif
53
54# define P_RSSIZE(p) (p)->pst_rssize
55# define P_TSIZE(p) (p)->pst_tsize
56# define P_DSIZE(p) (p)->pst_dsize
57# define P_SSIZE(p) (p)->pst_ssize
58
59#define VMUNIX  "/stand/vmunix"
60#define KMEM    "/dev/kmem"
61#define MEM     "/dev/mem"
62#ifdef DOSWAP
63#define SWAP    "/dev/dmem"
64#endif
65
66/* what we consider to be process size: */
67#define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp))
68
69/* definitions for indices in the nlist array */
70#define X_MPID          0
71
72static struct nlist nlst[] = {
73    { "mpid" },
74    { 0 }
75};
76
77/*
78 *  These definitions control the format of the per-process area
79 */
80
81static char header[] =
82  "     TTY   PID X         PRI NICE  SIZE   RES STATE   TIME    CPU COMMAND";
83/* 0123456789.12345 -- field to fill in starts at header+6 */
84#define UNAME_START 15
85
86#define Proc_format \
87        "%8.8s %5d %-8.8s %4d %4d %5s %5s %-5s %6s %5.2f%% %s"
88
89/* process state names for the "STATE" column of the display */
90
91char *state_abbrev[] =
92{
93    "", "sleep", "run", "stop", "zomb", "trans", "start"
94};
95
96
97/* values that we stash away in _init and use in later routines */
98static int kmem;
99static struct pst_status *pst;
100
101/* these are retrieved from the OS in _init */
102static int nproc;
103static int ncpu = 0;
104
105/* these are offsets obtained via nlist and used in the get_ functions */
106static unsigned long mpid_offset;
107
108/* these are for calculating cpu state percentages */
109static long cp_time[PST_MAX_CPUSTATES];
110static long cp_old[PST_MAX_CPUSTATES];
111static long cp_diff[PST_MAX_CPUSTATES];
112
113/* these are for detailing the process states */
114int process_states[7];
115char *procstatenames[] = {
116    "", " sleeping, ", " running, ", " stopped, ", " zombie, ",
117    " trans, ", " starting, ",
118    NULL
119};
120
121/* these are for detailing the cpu states */
122int cpu_states[PST_MAX_CPUSTATES];
123char *cpustatenames[] = {
124    /* roll "swait" into "block" and "ssys" into "sys" */
125    "usr", "nice", "sys", "idle", "", "block", "\0swait", "intr", "\0ssys",
126    NULL
127};
128
129/* these are for detailing the memory statistics */
130int memory_stats[8];
131char *memorynames[] = {
132    "Real: ", "K/", "K act/tot  ", "Virtual: ", "K/",
133    "K act/tot  ", "Free: ", "K", NULL
134};
135
136/* these are for getting the memory statistics */
137static int pageshift;           /* log base 2 of the pagesize */
138
139/* define pagetok in terms of pageshift */
140#define pagetok(size) ((size) << pageshift)
141
142/* Mapping TTY major/minor numbers is done through this structure */
143struct ttymap {
144    dev_t dev;
145    char name [9];
146};
147static struct ttymap *ttynames = NULL;
148static int nttys = 0;
149static get_tty_names ();
150
151
152machine_init(statics)
153
154struct statics *statics;
155
156{
157    struct pst_static info;
158    int i = 0;
159    int pagesize;
160
161    /* If we can get mpid from the kernel, we'll use it, otherwise    */
162    /* we'll guess from the most recently started proces              */
163    if ((kmem = open (KMEM, O_RDONLY)) < 0 ||
164        (nlist (VMUNIX, nlst)) < 0 ||
165        (nlst[X_MPID].n_type) == 0)
166        mpid_offset = 0;
167    else
168        mpid_offset = nlst[X_MPID].n_value;
169
170    if (pstat_getstatic (&info, sizeof (info), 1, 0) < 0)
171    {
172        perror ("pstat_getstatic");
173        return -1;
174    }
175
176    /*
177     * Allocate space for the per-process structures (pst_status).  To
178     * make life easier, simply allocate enough storage to hold all the
179     * process information at once.  This won't normally be a problem
180     * since machines with lots of processes configured will also have
181     * lots of memory.
182     */
183    nproc = info.max_proc;
184    pst = (struct pst_status *) malloc (nproc * sizeof (struct pst_status));
185    if (pst == NULL)
186    {
187        fprintf (stderr, "out of memory\n");
188        return -1;
189    }
190
191    /*
192     * Calculate pageshift -- the value needed to convert pages to Kbytes.
193     * This will usually be 2.
194     */
195    pageshift = 0;
196    for (pagesize = info.page_size; pagesize > 1; pagesize >>= 1)
197        pageshift += 1;
198    pageshift -= LOG1024;
199
200    /* get tty name information */
201    i = 0;
202    get_tty_names ("/dev", &i);
203
204    /* fill in the statics information */
205    statics->procstate_names = procstatenames;
206    statics->cpustate_names = cpustatenames;
207    statics->memory_names = memorynames;
208
209    /* all done! */
210    return(0);
211}
212
213char *format_header(uname_field)
214char *uname_field;
215{
216    char *ptr = header + UNAME_START;
217    while (*uname_field != '\0')
218        *ptr++ = *uname_field++;
219
220    return header;
221}
222
223get_system_info(si)
224
225struct system_info *si;
226
227{
228    static struct pst_dynamic dynamic;
229    int i, n;
230    long total;
231
232    pstat_getdynamic (&dynamic, sizeof (dynamic), 1, 0);
233    ncpu = dynamic.psd_proc_cnt;  /* need this later */
234
235    /* Load average */
236    si->load_avg[0] = dynamic.psd_avg_1_min;
237    si->load_avg[1] = dynamic.psd_avg_5_min;
238    si->load_avg[2] = dynamic.psd_avg_15_min;
239
240    /*
241     * CPU times
242     * to avoid space problems, we roll SWAIT (kernel semaphore block)
243     * into BLOCK (spin lock block) and SSYS (kernel process) into SYS
244     * (system time) Ideally, all screens would be wider :-)
245     */
246    dynamic.psd_cpu_time [CP_BLOCK] += dynamic.psd_cpu_time [CP_SWAIT];
247    dynamic.psd_cpu_time [CP_SWAIT] = 0;
248    dynamic.psd_cpu_time [CP_SYS] += dynamic.psd_cpu_time [CP_SSYS];
249    dynamic.psd_cpu_time [CP_SSYS] = 0;
250    for (i = 0; i < PST_MAX_CPUSTATES; i++)
251        cp_time [i] = dynamic.psd_cpu_time [i];
252    percentages(PST_MAX_CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
253    si->cpustates = cpu_states;
254
255    /*
256     * VM statistics
257     */
258    memory_stats[0] = -1;
259    memory_stats[1] = pagetok (dynamic.psd_arm);
260    memory_stats[2] = pagetok (dynamic.psd_rm);
261    memory_stats[3] = -1;
262    memory_stats[4] = pagetok (dynamic.psd_avm);
263    memory_stats[5] = pagetok (dynamic.psd_vm);
264    memory_stats[6] = -1;
265    memory_stats[7] = pagetok (dynamic.psd_free);
266    si->memory = memory_stats;
267
268    /*
269     * If we can get mpid from the kernel, then we will do so now.
270     * Otherwise we'll guess at mpid from the most recently started
271     * process time.  Note that this requires us to get the pst array
272     * now rather than in get_process_info().  We rely on
273     * get_system_info() being called before get_system_info() for this
274     * to work reliably.
275     */
276    for (i = 0; i < nproc; i++)
277        pst[i].pst_pid = -1;
278    n = pstat_getproc (pst, sizeof (*pst), nproc, 0);
279
280    if (kmem >= 0 && mpid_offset > 0)
281        (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid");
282    else
283    {
284        static int last_start_time = 0;
285        int pid = 0;
286
287        for (i = 0; i < n; i++)
288        {
289            if (last_start_time <= pst[i].pst_start)
290            {
291                last_start_time = pst[i].pst_start;
292                if (pid <= pst[i].pst_pid)
293                    pid = pst[i].pst_pid;
294            }
295        }
296        if (pid != 0)
297            si->last_pid = pid;
298    }
299}
300
301caddr_t get_process_info(si, sel, compare)
302
303struct system_info *si;
304struct process_select *sel;
305int (*compare)();
306
307{
308    static int handle;
309    int i, active, total;
310
311    /*
312     * Eliminate unwanted processes
313     * and tot up all the wanted processes by state
314     */
315    for (i = 0; i < sizeof (process_states)/sizeof (process_states[0]); i++)
316        process_states [i] = 0;
317
318    for (total = 0, active = 0, i = 0; pst[i].pst_pid >= 0; i++)
319    {
320        int state = pst[i].pst_stat;
321
322        process_states [state] += 1;
323        total += 1;
324
325        if (!sel->system && (pst[i].pst_flag & PS_SYS))
326        {
327            pst[i].pst_stat = -1;
328            continue;
329        }
330
331        /*
332         * If we are eliminating idle processes, then a process is regarded
333         * as idle if it is in a short term sleep and not using much
334         * CPU, or stopped, or simple dead.
335         */
336        if (!sel->idle
337            && (state == PS_SLEEP || state == PS_STOP || state == PS_ZOMBIE)
338            && (state != PS_SLEEP && pst[i].pst_pctcpu < CPU_IDLE_THRESH/100.0))
339            pst[i].pst_stat = -1;
340               
341        if (sel->uid > 0 && sel->uid != pst[i].pst_uid)
342            pst[i].pst_stat = -1;
343
344        if (sel->command != NULL &&
345            strncmp (sel->command, pst[i].pst_ucomm, strlen (pst[i].pst_ucomm)) != 0)
346            pst[i].pst_stat = -1;
347
348        if (pst[i].pst_stat >= 0)
349            active += 1;
350    }
351    si->procstates = process_states;
352    si->p_total = total;
353    si->p_active = active;
354
355    if (compare != NULL)
356        qsort ((char *)pst, i, sizeof(*pst), compare);
357
358    /* handle is simply an index into the process structures */
359    handle = 0;
360    return (caddr_t) &handle;
361}
362
363/*
364 * Find the terminal name associated with a particular
365 * major/minor number pair
366 */
367static char *term_name (term)
368struct psdev *term;
369{
370    dev_t dev;
371    int i;
372
373    if (term->psd_major == -1 && term->psd_minor == -1)
374        return "?";
375
376    dev = makedev (term->psd_major, term->psd_minor);
377    for (i = 0; i < nttys && ttynames[i].name[0] != '\0'; i++)
378    {
379        if (dev == ttynames[i].dev)
380            return ttynames[i].name;
381    }
382    return "<unk>";
383}
384
385char *format_next_process(handle, get_userid)
386
387caddr_t handle;
388char *(*get_userid)();
389
390{
391    static char fmt[MAX_COLS];  /* static area where result is built */
392    char run [sizeof ("runNN")];
393    int idx;
394    struct pst_status *proc;
395    char *state;
396    int size;
397
398    register long cputime;
399    register double pct;
400    int where;
401    struct handle *hp;
402    struct timeval time;
403    struct timezone timezone;
404
405    /* sanity check */
406    if (handle == NULL)
407        return "";
408
409    idx = *((int *) handle);
410    while (idx < nproc && pst[idx].pst_stat < 0)
411        idx += 1;
412    if (idx >= nproc || pst[idx].pst_stat < 0)
413        return "";
414    proc = &pst[idx];
415    *((int *) handle) = idx+1;
416
417    /* set ucomm for system processes, although we shouldn't need to */
418    if (proc->pst_ucomm[0] == '\0')
419    {
420        if (proc->pst_pid == 0)
421            strcpy (proc->pst_ucomm, "Swapper");
422        else if (proc->pst_pid == 2)
423            strcpy (proc->pst_ucomm, "Pager");
424    }
425
426    size = proc->pst_tsize + proc->pst_dsize + proc->pst_ssize;
427
428    if (ncpu > 1 && proc->pst_stat == PS_RUN)
429    {
430        sprintf (run, "run%02d", proc->pst_procnum);
431        state = run;
432    }
433    else if (proc->pst_stat == PS_SLEEP)
434    {
435        switch (proc->pst_pri+PTIMESHARE) {
436        case PSWP:      state = "SWP"; break; /* also PMEM */
437        case PRIRWLOCK: state = "RWLOCK"; break;
438        case PRIBETA:   state = "BETA"; break;
439        case PRIALPHA:  state = "ALPHA"; break;
440        case PRISYNC:   state = "SYNC"; break;
441        case PINOD:     state = "INOD"; break;
442        case PRIBIO:    state = "BIO"; break;
443        case PLLIO:     state = "LLIO"; break; /* also PRIUBA  */
444        case PZERO:     state = "ZERO"; break;
445        case PPIPE:     state = "pipe"; break;
446        case PVFS:      state = "vfs"; break;
447        case PWAIT:     state = "wait"; break;
448        case PLOCK:     state = "lock"; break;
449        case PSLEP:     state = "slep"; break;
450        case PUSER:     state = "user"; break;
451        default:
452            if (proc->pst_pri < PZERO-PTIMESHARE)
453                state = "SLEEP";
454            else
455                state = "sleep";
456        }
457    }
458    else
459        state = state_abbrev [proc->pst_stat];
460
461    /* format this entry */
462    sprintf(fmt,
463            Proc_format,
464            term_name (&proc->pst_term),
465            proc->pst_pid,
466            (*get_userid)(proc->pst_uid),
467            proc->pst_pri,
468            proc->pst_nice - NZERO,
469            format_k(size),
470            format_k(proc->pst_rssize),
471            state,
472            format_time(proc->pst_utime + proc->pst_stime),
473            100.0 * proc->pst_pctcpu,
474            printable(proc->pst_ucomm));
475
476    /* return the result */
477    return(fmt);
478}
479
480
481
482/*
483 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
484 *      "offset" is the byte offset into the kernel for the desired value,
485 *      "ptr" points to a buffer into which the value is retrieved,
486 *      "size" is the size of the buffer (and the object to retrieve),
487 *      "refstr" is a reference string used when printing error meessages,
488 *          if "refstr" starts with a '!', then a failure on read will not
489 *          be fatal (this may seem like a silly way to do things, but I
490 *          really didn't want the overhead of another argument).
491 *     
492 */
493
494getkval(offset, ptr, size, refstr)
495
496unsigned long offset;
497int *ptr;
498int size;
499char *refstr;
500
501{
502    if (lseek(kmem, (long)offset, SEEK_SET) == -1) {
503        if (*refstr == '!')
504            refstr++;
505        (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
506                       refstr, strerror(errno));
507        quit(23);
508    }
509    if (read(kmem, (char *) ptr, size) == -1) {
510        if (*refstr == '!')
511            return(0);
512        else {
513            (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
514                           refstr, strerror(errno));
515            quit(23);
516        }
517    }
518    return(1);
519}
520   
521/* comparison routine for qsort */
522
523/*
524 *  proc_compare - comparison function for "qsort"
525 *      Compares the resource consumption of two processes using five
526 *      distinct keys.  The keys (in descending order of importance) are:
527 *      percent cpu, cpu ticks, state, resident set size, total virtual
528 *      memory usage.  The process states are ordered as follows (from least
529 *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
530 *      array declaration below maps a process state index into a number
531 *      that reflects this ordering.
532 */
533
534static unsigned char sorted_state[] =
535{
536    0,  /* not used             */
537    3,  /* sleep                */
538    6,  /* run                  */
539    4,  /* stop                 */
540    2,  /* zombie               */
541    5,  /* start                */
542    1,  /* other                */
543};
544 
545proc_compare(p1, p2)
546struct pst_status *p1;
547struct pst_status *p2;
548
549{
550    int result;
551    float lresult;
552
553    /* compare percent cpu (pctcpu) */
554    if ((lresult = p2->pst_pctcpu - p1->pst_pctcpu) == 0)
555    {
556        /* use cpticks to break the tie */
557        if ((result = p2->pst_cpticks - p1->pst_cpticks) == 0)
558        {
559            /* use process state to break the tie */
560            if ((result = sorted_state[p2->pst_stat] -
561                          sorted_state[p1->pst_stat])  == 0)
562            {
563                /* use priority to break the tie */
564                if ((result = p2->pst_pri - p1->pst_pri) == 0)
565                {
566                    /* use resident set size (rssize) to break the tie */
567                    if ((result = P_RSSIZE(p2) - P_RSSIZE(p1)) == 0)
568                    {
569                        /* use total memory to break the tie */
570                        result = PROCSIZE(p2) - PROCSIZE(p1);
571                    }
572                }
573            }
574        }
575    }
576    else
577    {
578        result = lresult < 0 ? -1 : 1;
579    }
580
581    return(result);
582}
583
584
585void (*signal(sig, func))()
586    int sig;
587    void (*func)();
588{
589    struct sigaction act;
590    struct sigaction oact;
591
592    memset (&act, 0, sizeof (act));
593    act.sa_handler = func;
594
595    if (sigaction (sig, &act, &oact) < 0)
596        return BADSIG;
597    return oact.sa_handler;
598}
599
600/*
601 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
602 *              the process does not exist.
603 *              It is EXTREMLY IMPORTANT that this function work correctly.
604 *              If top runs setuid root (as in SVR4), then this function
605 *              is the only thing that stands in the way of a serious
606 *              security problem.  It validates requests for the "kill"
607 *              and "renice" commands.
608 */
609int proc_owner(pid)
610int pid;
611{
612    int i;
613
614    for (i = 0;  i < nproc; i++)
615    {
616        if (pst[i].pst_pid == pid)
617            return pst[i].pst_uid;
618    }
619    return -1;
620}
621
622
623static get_tty_names (dir, m)
624char *dir;
625int *m;
626{
627    char name [MAXPATHLEN+1];
628    struct dirent **namelist;
629    int i, n;
630
631    if ((n = scandir (dir, &namelist, NULL, NULL)) < 0)
632        return;
633
634    if (ttynames == NULL)
635    {
636        nttys = n;
637        ttynames = malloc (n*sizeof (*ttynames));
638    }
639    else
640    {
641        nttys += n;
642        ttynames = realloc (ttynames, nttys*sizeof (*ttynames));
643    }
644
645    for (i = 0; i < n; i++)
646    {
647        struct stat statbuf;
648        char *str = namelist[i]->d_name;
649        if (*str == '.')
650            continue;
651        sprintf (name, "%s/%s", dir, str);
652        if (stat (name, &statbuf) < 0)
653            continue;
654       
655        if (!isalpha (*str))
656            str = name + sizeof ("/dev");
657        if (S_ISCHR (statbuf.st_mode))
658        {
659            ttynames [*m].dev = statbuf.st_rdev;
660            strncpy (ttynames[*m].name, str, 8);
661            ttynames[*m].name[9] = '\0';
662            *m += 1;
663        }
664        else if (S_ISDIR (statbuf.st_mode))
665            get_tty_names (name, m);
666    }
667    if (*m < nttys)
668        ttynames[*m].name[0] = '\0';
669    free (namelist);
670}
671
Note: See TracBrowser for help on using the repository browser.