Rename top(1) to mtop(1), import NetBSD top(1)
[minix3.git] / external / bsd / top / dist / machine / m_hpux10.c
blob1bb8e4a1a6a90a45a5bc700f007af17a47542657
1 /*
2 * Copyright (c) 1984 through 2008, William LeFebvre
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * top - a top users display for Unix
36 * SYNOPSIS: any hp9000 running hpux version 10.x
38 * DESCRIPTION:
39 * This is the machine-dependent module for HPUX 10/11 that uses pstat.
40 * It has been tested on HP/UX 10.01, 10.20, and 11.00. It is presumed
41 * to work also on 10.10.
42 * Idle processes are marked by being either runnable or having a %CPU
43 * of at least 0.1%. This fraction is defined by CPU_IDLE_THRESH and
44 * can be adjusted at compile time.
46 * CFLAGS: -DHAVE_GETOPT
48 * LIBS:
50 * AUTHOR: John Haxby <john_haxby@hp.com>
51 * AUTHOR: adapted from Rich Holland <holland@synopsys.com>
52 * AUTHOR: adapted from Kevin Schmidt <kevin@mcl.ucsb.edu>
55 #include "config.h"
56 #include <stdio.h>
57 #include <errno.h>
58 #include <unistd.h>
59 #include <ctype.h>
60 #include <signal.h>
61 #include <nlist.h>
62 #include <fcntl.h>
63 #include <stdlib.h>
65 #include <sys/types.h>
66 #include <sys/param.h>
67 #include <sys/pstat.h>
68 #include <sys/dk.h>
69 #include <sys/stat.h>
70 #include <sys/dirent.h>
72 #include "top.h"
73 #include "machine.h"
74 #include "utils.h"
77 * The idle threshold (CPU_IDLE_THRESH) is an extension to the normal
78 * idle process check. Basically, we regard a process as idle if it is
79 * both asleep and using less that CPU_IDLE_THRESH percent cpu time. I
80 * believe this makes the "i" option more useful, but if you don't, add
81 * "-DCPU_IDLE_THRESH=0.0" to the CFLAGS.
83 #ifndef CPU_IDLE_THRESH
84 #define CPU_IDLE_THRESH 0.1
85 #endif
87 # define P_RSSIZE(p) (p)->pst_rssize
88 # define P_TSIZE(p) (p)->pst_tsize
89 # define P_DSIZE(p) (p)->pst_dsize
90 # define P_SSIZE(p) (p)->pst_ssize
92 #define VMUNIX "/stand/vmunix"
93 #define KMEM "/dev/kmem"
94 #define MEM "/dev/mem"
95 #ifdef DOSWAP
96 #define SWAP "/dev/dmem"
97 #endif
99 /* what we consider to be process size: */
100 #define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp))
102 /* definitions for indices in the nlist array */
103 #define X_MPID 0
105 static struct nlist nlst[] = {
106 { "mpid" },
107 { 0 }
111 * These definitions control the format of the per-process area
114 static char header[] =
115 " TTY PID X PRI NICE SIZE RES STATE TIME CPU COMMAND";
116 /* 0123456789.12345 -- field to fill in starts at header+6 */
117 #define UNAME_START 15
119 #define Proc_format \
120 "%8.8s %5d %-8.8s %4d %4d %5s %5s %-5s %6s %5.2f%% %s"
122 /* process state names for the "STATE" column of the display */
124 char *state_abbrev[] =
126 "", "sleep", "run", "stop", "zomb", "trans", "start"
130 /* values that we stash away in _init and use in later routines */
131 static int kmem;
132 static struct pst_status *pst;
134 /* these are retrieved from the OS in _init */
135 static int nproc;
136 static int ncpu = 0;
138 /* these are offsets obtained via nlist and used in the get_ functions */
139 static unsigned long mpid_offset;
141 /* these are for calculating cpu state percentages */
142 static long cp_time[PST_MAX_CPUSTATES];
143 static long cp_old[PST_MAX_CPUSTATES];
144 static long cp_diff[PST_MAX_CPUSTATES];
146 /* these are for detailing the process states */
147 int process_states[7];
148 char *procstatenames[] = {
149 "", " sleeping, ", " running, ", " stopped, ", " zombie, ",
150 " trans, ", " starting, ",
151 NULL
154 /* these are for detailing the cpu states */
155 int cpu_states[PST_MAX_CPUSTATES];
156 char *cpustatenames[] = {
157 /* roll "swait" into "block" and "ssys" into "sys" */
158 "usr", "nice", "sys", "idle", "", "block", "\0swait", "intr", "\0ssys",
159 NULL
162 /* these are for detailing the memory statistics */
163 long memory_stats[8];
164 char *memorynames[] = {
165 "Real: ", "K act, ", "K tot ", "Virtual: ", "K act, ",
166 "K tot, ", "K free", NULL
169 /* these are for getting the memory statistics */
170 static int pageshift; /* log base 2 of the pagesize */
172 /* define pagetok in terms of pageshift */
173 #define pagetok(size) ((size) << pageshift)
175 /* Mapping TTY major/minor numbers is done through this structure */
176 struct ttymap {
177 dev_t dev;
178 char name [9];
180 static struct ttymap *ttynames = NULL;
181 static int nttys = 0;
182 static get_tty_names ();
184 /* comparison routine for qsort */
187 * proc_compare - comparison function for "qsort"
188 * Compares the resource consumption of two processes using five
189 * distinct keys. The keys (in descending order of importance) are:
190 * percent cpu, cpu ticks, state, resident set size, total virtual
191 * memory usage. The process states are ordered as follows (from least
192 * to most important): WAIT, zombie, sleep, stop, start, run. The
193 * array declaration below maps a process state index into a number
194 * that reflects this ordering.
197 static unsigned char sorted_state[] =
199 0, /* not used */
200 3, /* sleep */
201 6, /* run */
202 4, /* stop */
203 2, /* zombie */
204 5, /* start */
205 1, /* other */
208 proc_compare(p1, p2)
209 struct pst_status *p1;
210 struct pst_status *p2;
213 int result;
214 float lresult;
216 /* compare percent cpu (pctcpu) */
217 if ((lresult = p2->pst_pctcpu - p1->pst_pctcpu) == 0)
219 /* use cpticks to break the tie */
220 if ((result = p2->pst_cpticks - p1->pst_cpticks) == 0)
222 /* use process state to break the tie */
223 if ((result = sorted_state[p2->pst_stat] -
224 sorted_state[p1->pst_stat]) == 0)
226 /* use priority to break the tie */
227 if ((result = p2->pst_pri - p1->pst_pri) == 0)
229 /* use resident set size (rssize) to break the tie */
230 if ((result = P_RSSIZE(p2) - P_RSSIZE(p1)) == 0)
232 /* use total memory to break the tie */
233 result = PROCSIZE(p2) - PROCSIZE(p1);
239 else
241 result = lresult < 0 ? -1 : 1;
244 return(result);
247 machine_init(statics)
249 struct statics *statics;
252 struct pst_static info;
253 int i = 0;
254 int pagesize;
256 /* If we can get mpid from the kernel, we'll use it, otherwise */
257 /* we'll guess from the most recently started proces */
258 if ((kmem = open (KMEM, O_RDONLY)) < 0 ||
259 (nlist (VMUNIX, nlst)) < 0 ||
260 (nlst[X_MPID].n_type) == 0)
261 mpid_offset = 0;
262 else
263 mpid_offset = nlst[X_MPID].n_value;
265 if (pstat_getstatic (&info, sizeof (info), 1, 0) < 0)
267 perror ("pstat_getstatic");
268 return -1;
272 * Allocate space for the per-process structures (pst_status). To
273 * make life easier, simply allocate enough storage to hold all the
274 * process information at once. This won't normally be a problem
275 * since machines with lots of processes configured will also have
276 * lots of memory.
278 nproc = info.max_proc;
279 pst = (struct pst_status *) malloc (nproc * sizeof (struct pst_status));
280 if (pst == NULL)
282 fprintf (stderr, "out of memory\n");
283 return -1;
287 * Calculate pageshift -- the value needed to convert pages to Kbytes.
288 * This will usually be 2.
290 pageshift = 0;
291 for (pagesize = info.page_size; pagesize > 1; pagesize >>= 1)
292 pageshift += 1;
293 pageshift -= LOG1024;
295 /* get tty name information */
296 i = 0;
297 get_tty_names ("/dev", &i);
299 /* fill in the statics information */
300 statics->procstate_names = procstatenames;
301 statics->cpustate_names = cpustatenames;
302 statics->memory_names = memorynames;
304 /* all done! */
305 return(0);
308 char *format_header(uname_field)
309 char *uname_field;
311 char *ptr = header + UNAME_START;
312 while (*uname_field != '\0')
313 *ptr++ = *uname_field++;
315 return header;
318 void
319 get_system_info(si)
321 struct system_info *si;
324 static struct pst_dynamic dynamic;
325 int i, n;
326 long total;
328 pstat_getdynamic (&dynamic, sizeof (dynamic), 1, 0);
329 ncpu = dynamic.psd_proc_cnt; /* need this later */
331 /* Load average */
332 si->load_avg[0] = dynamic.psd_avg_1_min;
333 si->load_avg[1] = dynamic.psd_avg_5_min;
334 si->load_avg[2] = dynamic.psd_avg_15_min;
337 * CPU times
338 * to avoid space problems, we roll SWAIT (kernel semaphore block)
339 * into BLOCK (spin lock block) and SSYS (kernel process) into SYS
340 * (system time) Ideally, all screens would be wider :-)
342 dynamic.psd_cpu_time [CP_BLOCK] += dynamic.psd_cpu_time [CP_SWAIT];
343 dynamic.psd_cpu_time [CP_SWAIT] = 0;
344 dynamic.psd_cpu_time [CP_SYS] += dynamic.psd_cpu_time [CP_SSYS];
345 dynamic.psd_cpu_time [CP_SSYS] = 0;
346 for (i = 0; i < PST_MAX_CPUSTATES; i++)
347 cp_time [i] = dynamic.psd_cpu_time [i];
348 percentages(PST_MAX_CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
349 si->cpustates = cpu_states;
352 * VM statistics
354 memory_stats[0] = -1;
355 memory_stats[1] = pagetok (dynamic.psd_arm);
356 memory_stats[2] = pagetok (dynamic.psd_rm);
357 memory_stats[3] = -1;
358 memory_stats[4] = pagetok (dynamic.psd_avm);
359 memory_stats[5] = pagetok (dynamic.psd_vm);
360 memory_stats[6] = pagetok (dynamic.psd_free);
361 si->memory = memory_stats;
364 * If we can get mpid from the kernel, then we will do so now.
365 * Otherwise we'll guess at mpid from the most recently started
366 * process time. Note that this requires us to get the pst array
367 * now rather than in get_process_info(). We rely on
368 * get_system_info() being called before get_system_info() for this
369 * to work reliably.
371 for (i = 0; i < nproc; i++)
372 pst[i].pst_pid = -1;
373 n = pstat_getproc (pst, sizeof (*pst), nproc, 0);
375 if (kmem >= 0 && mpid_offset > 0)
376 (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid");
377 else
379 static int last_start_time = 0;
380 int pid = 0;
382 for (i = 0; i < n; i++)
384 if (last_start_time <= pst[i].pst_start)
386 last_start_time = pst[i].pst_start;
387 if (pid <= pst[i].pst_pid)
388 pid = pst[i].pst_pid;
391 if (pid != 0)
392 si->last_pid = pid;
396 caddr_t get_process_info(si, sel, compare_index)
398 struct system_info *si;
399 struct process_select *sel;
400 int compare_index;
403 static int handle;
404 int i, active, total;
407 * Eliminate unwanted processes
408 * and tot up all the wanted processes by state
410 for (i = 0; i < sizeof (process_states)/sizeof (process_states[0]); i++)
411 process_states [i] = 0;
413 for (total = 0, active = 0, i = 0; pst[i].pst_pid >= 0; i++)
415 int state = pst[i].pst_stat;
417 process_states [state] += 1;
418 total += 1;
420 if (!sel->system && (pst[i].pst_flag & PS_SYS))
422 pst[i].pst_stat = -1;
423 continue;
427 * If we are eliminating idle processes, then a process is regarded
428 * as idle if it is in a short term sleep and not using much
429 * CPU, or stopped, or simple dead.
431 if (!sel->idle
432 && (state == PS_SLEEP || state == PS_STOP || state == PS_ZOMBIE)
433 && (state != PS_SLEEP && pst[i].pst_pctcpu < CPU_IDLE_THRESH/100.0))
434 pst[i].pst_stat = -1;
436 if (sel->uid > 0 && sel->uid != pst[i].pst_uid)
437 pst[i].pst_stat = -1;
439 if (sel->command != NULL &&
440 strncmp (sel->command, pst[i].pst_ucomm, strlen (pst[i].pst_ucomm)) != 0)
441 pst[i].pst_stat = -1;
443 if (pst[i].pst_stat >= 0)
444 active += 1;
446 si->procstates = process_states;
447 si->p_total = total;
448 si->p_active = active;
450 qsort ((char *)pst, i, sizeof(*pst), proc_compare);
452 /* handle is simply an index into the process structures */
453 handle = 0;
454 return (caddr_t) &handle;
458 * Find the terminal name associated with a particular
459 * major/minor number pair
461 static char *term_name (term)
462 struct psdev *term;
464 dev_t dev;
465 int i;
467 if (term->psd_major == -1 && term->psd_minor == -1)
468 return "?";
470 dev = makedev (term->psd_major, term->psd_minor);
471 for (i = 0; i < nttys && ttynames[i].name[0] != '\0'; i++)
473 if (dev == ttynames[i].dev)
474 return ttynames[i].name;
476 return "<unk>";
479 char *format_next_process(handle, get_userid)
481 caddr_t handle;
482 char *(*get_userid)();
485 static char fmt[MAX_COLS]; /* static area where result is built */
486 char run [sizeof ("runNN")];
487 int idx;
488 struct pst_status *proc;
489 char *state;
490 int size;
492 register long cputime;
493 register double pct;
494 int where;
495 struct handle *hp;
496 struct timeval time;
497 struct timezone timezone;
499 /* sanity check */
500 if (handle == NULL)
501 return "";
503 idx = *((int *) handle);
504 while (idx < nproc && pst[idx].pst_stat < 0)
505 idx += 1;
506 if (idx >= nproc || pst[idx].pst_stat < 0)
507 return "";
508 proc = &pst[idx];
509 *((int *) handle) = idx+1;
511 /* set ucomm for system processes, although we shouldn't need to */
512 if (proc->pst_ucomm[0] == '\0')
514 if (proc->pst_pid == 0)
515 strcpy (proc->pst_ucomm, "Swapper");
516 else if (proc->pst_pid == 2)
517 strcpy (proc->pst_ucomm, "Pager");
520 size = proc->pst_tsize + proc->pst_dsize + proc->pst_ssize;
522 if (ncpu > 1 && proc->pst_stat == PS_RUN)
524 sprintf (run, "run%02d", proc->pst_procnum);
525 state = run;
527 else if (proc->pst_stat == PS_SLEEP)
529 switch (proc->pst_pri+PTIMESHARE) {
530 case PSWP: state = "SWP"; break; /* also PMEM */
531 case PRIRWLOCK: state = "RWLOCK"; break;
532 case PRIBETA: state = "BETA"; break;
533 case PRIALPHA: state = "ALPHA"; break;
534 case PRISYNC: state = "SYNC"; break;
535 case PINOD: state = "INOD"; break;
536 case PRIBIO: state = "BIO"; break;
537 case PLLIO: state = "LLIO"; break; /* also PRIUBA */
538 case PZERO: state = "ZERO"; break;
539 case PPIPE: state = "pipe"; break;
540 case PVFS: state = "vfs"; break;
541 case PWAIT: state = "wait"; break;
542 case PLOCK: state = "lock"; break;
543 case PSLEP: state = "slep"; break;
544 case PUSER: state = "user"; break;
545 default:
546 if (proc->pst_pri < PZERO-PTIMESHARE)
547 state = "SLEEP";
548 else
549 state = "sleep";
552 else
553 state = state_abbrev [proc->pst_stat];
555 /* format this entry */
556 sprintf(fmt,
557 Proc_format,
558 term_name (&proc->pst_term),
559 proc->pst_pid,
560 (*get_userid)(proc->pst_uid),
561 proc->pst_pri,
562 proc->pst_nice - NZERO,
563 format_k(size),
564 format_k(proc->pst_rssize),
565 state,
566 format_time(proc->pst_utime + proc->pst_stime),
567 100.0 * proc->pst_pctcpu,
568 printable(proc->pst_ucomm));
570 /* return the result */
571 return(fmt);
577 * getkval(offset, ptr, size, refstr) - get a value out of the kernel.
578 * "offset" is the byte offset into the kernel for the desired value,
579 * "ptr" points to a buffer into which the value is retrieved,
580 * "size" is the size of the buffer (and the object to retrieve),
581 * "refstr" is a reference string used when printing error meessages,
582 * if "refstr" starts with a '!', then a failure on read will not
583 * be fatal (this may seem like a silly way to do things, but I
584 * really didn't want the overhead of another argument).
588 getkval(offset, ptr, size, refstr)
590 unsigned long offset;
591 int *ptr;
592 int size;
593 char *refstr;
596 if (lseek(kmem, (long)offset, SEEK_SET) == -1) {
597 if (*refstr == '!')
598 refstr++;
599 (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
600 refstr, strerror(errno));
601 quit(23);
603 if (read(kmem, (char *) ptr, size) == -1) {
604 if (*refstr == '!')
605 return(0);
606 else {
607 (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM,
608 refstr, strerror(errno));
609 quit(23);
612 return(1);
615 void (*signal(sig, func))()
616 int sig;
617 void (*func)();
619 struct sigaction act;
620 struct sigaction oact;
622 memset (&act, 0, sizeof (act));
623 act.sa_handler = func;
625 if (sigaction (sig, &act, &oact) < 0)
626 return BADSIG;
627 return oact.sa_handler;
631 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
632 * the process does not exist.
633 * It is EXTREMLY IMPORTANT that this function work correctly.
634 * If top runs setuid root (as in SVR4), then this function
635 * is the only thing that stands in the way of a serious
636 * security problem. It validates requests for the "kill"
637 * and "renice" commands.
639 int proc_owner(pid)
640 int pid;
642 int i;
644 for (i = 0; i < nproc; i++)
646 if (pst[i].pst_pid == pid)
647 return pst[i].pst_uid;
649 return -1;
653 static get_tty_names (dir, m)
654 char *dir;
655 int *m;
657 char name [MAXPATHLEN+1];
658 struct dirent **namelist;
659 int i, n;
661 if ((n = scandir (dir, &namelist, NULL, NULL)) < 0)
662 return;
664 if (ttynames == NULL)
666 nttys = n;
667 ttynames = malloc (n*sizeof (*ttynames));
669 else
671 nttys += n;
672 ttynames = realloc (ttynames, nttys*sizeof (*ttynames));
675 for (i = 0; i < n; i++)
677 struct stat statbuf;
678 char *str = namelist[i]->d_name;
679 if (*str == '.')
680 continue;
681 sprintf (name, "%s/%s", dir, str);
682 if (stat (name, &statbuf) < 0)
683 continue;
685 if (!isalpha (*str))
686 str = name + sizeof ("/dev");
687 if (S_ISCHR (statbuf.st_mode))
689 ttynames [*m].dev = statbuf.st_rdev;
690 strncpy (ttynames[*m].name, str, 8);
691 ttynames[*m].name[9] = '\0';
692 *m += 1;
694 else if (S_ISDIR (statbuf.st_mode))
695 get_tty_names (name, m);
697 if (*m < nttys)
698 ttynames[*m].name[0] = '\0';
699 free (namelist);