xref: /minix/external/bsd/top/dist/machine/m_hpux10.c (revision 08cbf5a0)
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.
10  *
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.
15  *
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.
19  *
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.
31  */
32 
33 /*
34  * top - a top users display for Unix
35  *
36  * SYNOPSIS:  any hp9000 running hpux version 10.x
37  *
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.
45  *
46  * CFLAGS: -DHAVE_GETOPT
47  *
48  * LIBS:
49  *
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>
53  */
54 
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>
64 
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>
71 
72 #include "top.h"
73 #include "machine.h"
74 #include "utils.h"
75 
76 /*
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.
82  */
83 #ifndef CPU_IDLE_THRESH
84 #define CPU_IDLE_THRESH 0.1
85 #endif
86 
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
91 
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
98 
99 /* what we consider to be process size: */
100 #define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp))
101 
102 /* definitions for indices in the nlist array */
103 #define X_MPID		0
104 
105 static struct nlist nlst[] = {
106     { "mpid" },
107     { 0 }
108 };
109 
110 /*
111  *  These definitions control the format of the per-process area
112  */
113 
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
118 
119 #define Proc_format \
120 	"%8.8s %5d %-8.8s %4d %4d %5s %5s %-5s %6s %5.2f%% %s"
121 
122 /* process state names for the "STATE" column of the display */
123 
124 char *state_abbrev[] =
125 {
126     "", "sleep", "run", "stop", "zomb", "trans", "start"
127 };
128 
129 
130 /* values that we stash away in _init and use in later routines */
131 static int kmem;
132 static struct pst_status *pst;
133 
134 /* these are retrieved from the OS in _init */
135 static int nproc;
136 static int ncpu = 0;
137 
138 /* these are offsets obtained via nlist and used in the get_ functions */
139 static unsigned long mpid_offset;
140 
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];
145 
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
152 };
153 
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
160 };
161 
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
167 };
168 
169 /* these are for getting the memory statistics */
170 static int pageshift;		/* log base 2 of the pagesize */
171 
172 /* define pagetok in terms of pageshift */
173 #define pagetok(size) ((size) << pageshift)
174 
175 /* Mapping TTY major/minor numbers is done through this structure */
176 struct ttymap {
177     dev_t dev;
178     char name [9];
179 };
180 static struct ttymap *ttynames = NULL;
181 static int nttys = 0;
182 static get_tty_names ();
183 
184 /* comparison routine for qsort */
185 
186 /*
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.
195  */
196 
197 static unsigned char sorted_state[] =
198 {
199     0,	/* not used		*/
200     3,	/* sleep		*/
201     6,	/* run			*/
202     4,	/* stop			*/
203     2,	/* zombie		*/
204     5,	/* start		*/
205     1,  /* other                */
206 };
207 
208 proc_compare(p1, p2)
209 struct pst_status *p1;
210 struct pst_status *p2;
211 
212 {
213     int result;
214     float lresult;
215 
216     /* compare percent cpu (pctcpu) */
217     if ((lresult = p2->pst_pctcpu - p1->pst_pctcpu) == 0)
218     {
219 	/* use cpticks to break the tie */
220 	if ((result = p2->pst_cpticks - p1->pst_cpticks) == 0)
221 	{
222 	    /* use process state to break the tie */
223 	    if ((result = sorted_state[p2->pst_stat] -
224 			  sorted_state[p1->pst_stat])  == 0)
225 	    {
226 		/* use priority to break the tie */
227 		if ((result = p2->pst_pri - p1->pst_pri) == 0)
228 		{
229 		    /* use resident set size (rssize) to break the tie */
230 		    if ((result = P_RSSIZE(p2) - P_RSSIZE(p1)) == 0)
231 		    {
232 			/* use total memory to break the tie */
233 			result = PROCSIZE(p2) - PROCSIZE(p1);
234 		    }
235 		}
236 	    }
237 	}
238     }
239     else
240     {
241 	result = lresult < 0 ? -1 : 1;
242     }
243 
244     return(result);
245 }
246 
247 machine_init(statics)
248 
249 struct statics *statics;
250 
251 {
252     struct pst_static info;
253     int i = 0;
254     int pagesize;
255 
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;
264 
265     if (pstat_getstatic (&info, sizeof (info), 1, 0) < 0)
266     {
267 	perror ("pstat_getstatic");
268 	return -1;
269     }
270 
271     /*
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.
277      */
278     nproc = info.max_proc;
279     pst = (struct pst_status *) malloc (nproc * sizeof (struct pst_status));
280     if (pst == NULL)
281     {
282 	fprintf (stderr, "out of memory\n");
283 	return -1;
284     }
285 
286     /*
287      * Calculate pageshift -- the value needed to convert pages to Kbytes.
288      * This will usually be 2.
289      */
290     pageshift = 0;
291     for (pagesize = info.page_size; pagesize > 1; pagesize >>= 1)
292 	pageshift += 1;
293     pageshift -= LOG1024;
294 
295     /* get tty name information */
296     i = 0;
297     get_tty_names ("/dev", &i);
298 
299     /* fill in the statics information */
300     statics->procstate_names = procstatenames;
301     statics->cpustate_names = cpustatenames;
302     statics->memory_names = memorynames;
303 
304     /* all done! */
305     return(0);
306 }
307 
308 char *format_header(uname_field)
309 char *uname_field;
310 {
311     char *ptr = header + UNAME_START;
312     while (*uname_field != '\0')
313 	*ptr++ = *uname_field++;
314 
315     return header;
316 }
317 
318 void
319 get_system_info(si)
320 
321 struct system_info *si;
322 
323 {
324     static struct pst_dynamic dynamic;
325     int i, n;
326     long total;
327 
328     pstat_getdynamic (&dynamic, sizeof (dynamic), 1, 0);
329     ncpu = dynamic.psd_proc_cnt;  /* need this later */
330 
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;
335 
336     /*
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 :-)
341      */
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;
350 
351     /*
352      * VM statistics
353      */
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;
362 
363     /*
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.
370      */
371     for (i = 0; i < nproc; i++)
372 	pst[i].pst_pid = -1;
373     n = pstat_getproc (pst, sizeof (*pst), nproc, 0);
374 
375     if (kmem >= 0 && mpid_offset > 0)
376 	(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid");
377     else
378     {
379 	static int last_start_time = 0;
380 	int pid = 0;
381 
382 	for (i = 0; i < n; i++)
383 	{
384 	    if (last_start_time <= pst[i].pst_start)
385 	    {
386 	    	last_start_time = pst[i].pst_start;
387 		if (pid <= pst[i].pst_pid)
388 		    pid = pst[i].pst_pid;
389 	    }
390 	}
391 	if (pid != 0)
392 	    si->last_pid = pid;
393     }
394 }
395 
396 caddr_t get_process_info(si, sel, compare_index)
397 
398 struct system_info *si;
399 struct process_select *sel;
400 int compare_index;
401 
402 {
403     static int handle;
404     int i, active, total;
405 
406     /*
407      * Eliminate unwanted processes
408      * and tot up all the wanted processes by state
409      */
410     for (i = 0; i < sizeof (process_states)/sizeof (process_states[0]); i++)
411 	process_states [i] = 0;
412 
413     for (total = 0, active = 0, i = 0; pst[i].pst_pid >= 0; i++)
414     {
415 	int state = pst[i].pst_stat;
416 
417 	process_states [state] += 1;
418 	total += 1;
419 
420 	if (!sel->system && (pst[i].pst_flag & PS_SYS))
421 	{
422 	    pst[i].pst_stat = -1;
423 	    continue;
424 	}
425 
426 	/*
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.
430 	 */
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;
435 
436 	if (sel->uid > 0 && sel->uid != pst[i].pst_uid)
437 	    pst[i].pst_stat = -1;
438 
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;
442 
443 	if (pst[i].pst_stat >= 0)
444 	    active += 1;
445     }
446     si->procstates = process_states;
447     si->p_total = total;
448     si->p_active = active;
449 
450     qsort ((char *)pst, i, sizeof(*pst), proc_compare);
451 
452     /* handle is simply an index into the process structures */
453     handle = 0;
454     return (caddr_t) &handle;
455 }
456 
457 /*
458  * Find the terminal name associated with a particular
459  * major/minor number pair
460  */
461 static char *term_name (term)
462 struct psdev *term;
463 {
464     dev_t dev;
465     int i;
466 
467     if (term->psd_major == -1 && term->psd_minor == -1)
468 	return "?";
469 
470     dev = makedev (term->psd_major, term->psd_minor);
471     for (i = 0; i < nttys && ttynames[i].name[0] != '\0'; i++)
472     {
473 	if (dev == ttynames[i].dev)
474 	    return ttynames[i].name;
475     }
476     return "<unk>";
477 }
478 
479 char *format_next_process(handle, get_userid)
480 
481 caddr_t handle;
482 char *(*get_userid)();
483 
484 {
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;
491 
492     register long cputime;
493     register double pct;
494     int where;
495     struct handle *hp;
496     struct timeval time;
497     struct timezone timezone;
498 
499     /* sanity check */
500     if (handle == NULL)
501 	return "";
502 
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;
510 
511     /* set ucomm for system processes, although we shouldn't need to */
512     if (proc->pst_ucomm[0] == '\0')
513     {
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");
518     }
519 
520     size = proc->pst_tsize + proc->pst_dsize + proc->pst_ssize;
521 
522     if (ncpu > 1 && proc->pst_stat == PS_RUN)
523     {
524 	sprintf (run, "run%02d", proc->pst_procnum);
525 	state = run;
526     }
527     else if (proc->pst_stat == PS_SLEEP)
528     {
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";
550 	}
551     }
552     else
553 	state = state_abbrev [proc->pst_stat];
554 
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));
569 
570     /* return the result */
571     return(fmt);
572 }
573 
574 
575 
576 /*
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).
585  *
586  */
587 
588 getkval(offset, ptr, size, refstr)
589 
590 unsigned long offset;
591 int *ptr;
592 int size;
593 char *refstr;
594 
595 {
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);
602     }
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);
610         }
611     }
612     return(1);
613 }
614 
615 void (*signal(sig, func))()
616     int sig;
617     void (*func)();
618 {
619     struct sigaction act;
620     struct sigaction oact;
621 
622     memset (&act, 0, sizeof (act));
623     act.sa_handler = func;
624 
625     if (sigaction (sig, &act, &oact) < 0)
626 	return BADSIG;
627     return oact.sa_handler;
628 }
629 
630 /*
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.
638  */
639 int proc_owner(pid)
640 int pid;
641 {
642     int i;
643 
644     for (i = 0;  i < nproc; i++)
645     {
646 	if (pst[i].pst_pid == pid)
647 	    return pst[i].pst_uid;
648     }
649     return -1;
650 }
651 
652 
653 static get_tty_names (dir, m)
654 char *dir;
655 int *m;
656 {
657     char name [MAXPATHLEN+1];
658     struct dirent **namelist;
659     int i, n;
660 
661     if ((n = scandir (dir, &namelist, NULL, NULL)) < 0)
662 	return;
663 
664     if (ttynames == NULL)
665     {
666 	nttys = n;
667 	ttynames = malloc (n*sizeof (*ttynames));
668     }
669     else
670     {
671 	nttys += n;
672 	ttynames = realloc (ttynames, nttys*sizeof (*ttynames));
673     }
674 
675     for (i = 0; i < n; i++)
676     {
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;
684 
685 	if (!isalpha (*str))
686 	    str = name + sizeof ("/dev");
687 	if (S_ISCHR (statbuf.st_mode))
688 	{
689 	    ttynames [*m].dev = statbuf.st_rdev;
690 	    strncpy (ttynames[*m].name, str, 8);
691 	    ttynames[*m].name[9] = '\0';
692 	    *m += 1;
693 	}
694 	else if (S_ISDIR (statbuf.st_mode))
695 	    get_tty_names (name, m);
696     }
697     if (*m < nttys)
698 	ttynames[*m].name[0] = '\0';
699     free (namelist);
700 }
701 
702