xref: /minix/external/bsd/top/dist/machine/m_irixsgi.c (revision e1cdaee1)
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 SGI machine running IRIX 6.2 and up
37  *
38  * DESCRIPTION:
39  * This is the machine-dependent module for IRIX as supplied by
40  * engineers at SGI.
41  *
42  * CFLAGS: -DHAVE_GETOPT -D_OLD_TERMIOS -DORDER
43  *
44  * AUTHOR: Sandeep Cariapa <cariapa@sgi.com>
45  * AUTHOR: Larry McVoy <lm@sgi.com>
46  * Sandeep did all the hard work; I ported to 6.2 and fixed up some formats.
47  * AUTHOR: John Schimmel <jes@sgi.com>
48  * He did the all irix merge.
49  * AUTHOR: Ariel Faigon <ariel@sgi.com>
50  *	Ported to Ficus/Kudzu (IRIX 6.4+).
51  *	Got rid of all nlist and different (elf64, elf32, COFF) kernel
52  *	dependencies
53  *	Various small fixes and enhancements: multiple CPUs, nicer formats.
54  *	Added -DORDER process display ordering
55  *	cleaned most -fullwarn'ings.
56  *	Need -D_OLD_TERMIOS when compiling on IRIX 6.4 to work on 6.2 systems
57  *	Support much bigger values in memory sizes (over Peta-byte)
58  * AUTHOR: William LeFebvre
59  *      Converted to ANSI C and updated to new module interface
60  */
61 
62 #define _KMEMUSER
63 
64 #include "config.h"
65 
66 #include <sys/types.h>
67 #include <sys/time.h>
68 #include <sys/stat.h>
69 #include <sys/swap.h>
70 #include <sys/proc.h>
71 #include <sys/procfs.h>
72 #include <sys/sysinfo.h>
73 #include <sys/sysmp.h>
74 #include <sys/utsname.h>
75 #include <sys/schedctl.h>	/* for < 6.4 NDPHIMAX et al. */
76 #include <paths.h>
77 #include <assert.h>
78 #include <values.h>
79 #include <dirent.h>
80 #include <stdio.h>
81 #include <unistd.h>
82 #include <stdlib.h>
83 #include <errno.h>
84 #include <fcntl.h>
85 #include <dlfcn.h>
86 
87 #include "top.h"
88 #include "machine.h"
89 #include "utils.h"
90 
91 #define KMEM	"/dev/kmem"
92 
93 typedef double load_avg;
94 #define loaddouble(la) (la)
95 #define intload(i) ((double)(i))
96 
97 /*
98  * Structure for keeping track of CPU times from last time around
99  * the program.  We keep these things in a hash table, which is
100  * recreated at every cycle.
101  */
102 struct oldproc {
103 	pid_t	oldpid;
104 	double	oldtime;
105 	double	oldpct;
106 };
107 static int oldprocs;                    /* size of table */
108 static struct oldproc *oldbase;
109 #define HASH(x) ((x << 1) % oldprocs)
110 
111 
112 #define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10)
113 
114 /*
115  * Ugly hack, save space and complexity of allocating and maintaining
116  * parallel arrays to the prpsinfo array: use spare space (pr_fill area)
117  * in prpsinfo structures to store %CPU calculated values
118  */
119 #define D_align(addr)		(((unsigned long)(addr) & ~0x0fU))
120 #define percent_cpu(pp)		(* (double *) D_align(&((pp)->pr_fill[0])))
121 #define weighted_cpu(pp)	(* (double *) D_align(&((pp)->pr_fill[4])))
122 
123 
124 /* Username field to fill in starts at: */
125 #define UNAME_START 16
126 
127 /*
128  *  These definitions control the format of the per-process area
129  */
130 static char header[] =
131 "    PID    PGRP X         PRI   SIZE   RES STATE    TIME %WCPU  %CPU COMMAND";
132 /*
133  012345678901234567890123456789012345678901234567890123456789012345678901234567
134           10        20        30        40        50        60        70
135  */
136 
137 /*       PID PGRP USER  PRI   SIZE  RES   STATE  TIME  %WCPU %CPU  CMD */
138 #define Proc_format \
139 	"%7d %7d %-8.8s %4.4s %6.6s %5.5s %-6.6s %6.6s %5.2f %5.2f %-.10s"
140 
141 
142 /*
143  * these are for detailing the cpu states
144  * Data is taken from the sysinfo structure (see <sys/sysinfo.h>)
145  * We rely on the following values:
146  *
147  *	#define CPU_IDLE        0
148  *	#define CPU_USER        1
149  *	#define CPU_KERNEL      2
150  *	#define CPU_WAIT        3
151  *	#define CPU_SXBRK       4
152  *	#define CPU_INTR        5
153  */
154 #ifndef CPU_STATES	/* defined only in 6.4 and up */
155 # define CPU_STATES 6
156 #endif
157 
158 int	cpu_states[CPU_STATES];
159 char	*cpustatenames[] = {
160 	"idle", "usr", "ker", "wait", "xbrk", "intr",
161 	NULL
162 };
163 
164 /* these are for detailing the memory statistics */
165 
166 #define MEMSTATS 10
167 int	memory_stats[MEMSTATS];
168 char	*memorynames[] = {
169 	"K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
170 };
171 
172 char	uname_str[40];
173 double	load[3];
174 static  char fmt[MAX_COLS + 2];
175 int	numcpus;
176 
177 /* useful externals */
178 extern int	errno;
179 extern char	*sys_errlist[];
180 
181 extern char	*myname;
182 extern char	*format_k();
183 extern char	*format_time();
184 extern long	percentages();
185 
186 static int kmem;
187 static unsigned long avenrun_offset;
188 
189 static float	irix_ver;		/* for easy numeric comparison */
190 
191 static struct prpsinfo	*pbase;
192 static struct prpsinfo	**pref;
193 static struct oldproc	*oldbase;
194 static int		oldprocs;	/* size of table */
195 
196 static DIR	*procdir;
197 
198 static int	ptable_size;	/* allocated process table size */
199 static int	nproc;		/* estimated process table size */
200 static int	pagesize;
201 
202 /* get_process_info passes back a handle.  This is what it looks like: */
203 struct handle {
204 	struct prpsinfo **next_proc;	/* points to next valid proc pointer */
205 	int		remaining;	/* number of pointers remaining */
206 };
207 
208 static struct handle	handle;
209 
210 void getptable(struct prpsinfo *baseptr);
211 void size(int fd, struct prpsinfo *ps);
212 
213 extern char *ordernames[];
214 
215 /*
216  * Process states letters are mapped into numbers
217  * 6.5 seems to have changed the semantics of prpsinfo.pr_state
218  * so we rely, (like ps does) on the char value pr_sname.
219  * The order we use here is what may be most interesting
220  * to top users:  Most interesting state on top, least on bottom.
221  * 'S' (sleeping) is the most common case so I put it _after_
222  * zombie, even though it is more "active" than zombie.
223  *
224  * State letters and their meanings:
225  *
226  *	R   Process is running (may not have a processor yet)
227  *	I   Process is in intermediate state of creation
228  *	X   Process is waiting for memory
229  *	T   Process is stopped
230  *	Z   Process is terminated and parent not waiting (zombie)
231  *	S   Process is sleeping, waiting for a resource
232  */
233 
234 /* abbreviated process states */
235 static char *state_abbrev[] =
236 { "", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL };
237 
238 /* Same but a little "wordier", used in CPU activity summary */
239 int     process_states[8];	/* per state counters */
240 char	*procstatenames[] = {
241 	/* ready to run is considered running here */
242 	"",		" sleeping, ",	" zombie, ",	" stopped, ",
243 	" swapped, ",	" starting, ",	" ready, ",	" running, ",
244 	NULL
245 };
246 
247 #define S_RUNNING	7
248 #define S_READY		6
249 #define S_STARTING	5
250 #define S_SWAPPED	4
251 #define S_STOPPED	3
252 #define S_ZOMBIE	2
253 #define S_SLEEPING	1
254 
255 #define IS_ACTIVE(pp) \
256 	(first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0)
257 
258 /*
259  * proc_state
260  *	map the pr_sname value to an integer.
261  *	used as an index into state_abbrev[]
262  *	as well as an "order" key
263  */
264 static int proc_state(struct prpsinfo *pp)
265 {
266     char psname = pp->pr_sname;
267 
268     switch (psname) {
269 	case 'R': return
270 		 (pp->pr_sonproc >= 0 && pp->pr_sonproc < numcpus) ?
271 			S_RUNNING /* on a processor */ : S_READY;
272 	case 'I': return S_STARTING;
273 	case 'X': return S_SWAPPED;
274 	case 'T': return S_STOPPED;
275 	case 'Z': return S_ZOMBIE;
276 	case 'S': return S_SLEEPING;
277 	default : return 0;
278     }
279 }
280 
281 
282 /*
283  * To avoid nlist'ing the kernel (with all the different kernel type
284  * complexities), we estimate the size of the needed working process
285  * table by scanning  /proc/pinfo and taking the number of entries
286  * multiplied by some reasonable factor.
287  * Assume current dir is _PATH_PROCFSPI
288  */
289 static int active_proc_count()
290 {
291 	DIR	*dirp;
292 	int	pcnt;
293 
294 	if ((dirp = opendir(".")) == NULL) {
295 		(void) fprintf(stderr, "%s: Unable to open %s\n",
296 					myname, _PATH_PROCFSPI);
297 		exit(1);
298 	}
299 	for (pcnt = 0; readdir(dirp) != NULL; pcnt++)
300 		;
301 	closedir(dirp);
302 
303 	return pcnt;
304 }
305 
306 /*
307  * allocate space for:
308  *	proc structure array
309  *	array of pointers to the above (used for sorting)
310  *	array for storing per-process old CPU usage
311  */
312 void
313 allocate_proc_tables()
314 {
315 	int	n_active = active_proc_count();
316 
317 	if (pbase != NULL)  /* && n_active < ptable_size */
318 		return;
319 
320 	/* Need to realloc if we exceed, but factor should be enough */
321 	nproc = n_active * 5;
322 	oldprocs = 2 * nproc;
323 
324 	pbase = (struct prpsinfo *)
325 		malloc(nproc * sizeof(struct prpsinfo));
326 	pref = (struct prpsinfo **)
327 		malloc(nproc * sizeof(struct prpsinfo *));
328 	oldbase = (struct oldproc *)
329 		malloc (oldprocs * sizeof(struct oldproc));
330 
331 	ptable_size = nproc;
332 
333 	if (pbase == NULL || pref == NULL || oldbase == NULL) {
334 		(void) fprintf(stderr, "%s: malloc: out of memory\n", myname);
335 		exit (1);
336 	}
337 }
338 
339 int
340 machine_init(struct statics *statics)
341 {
342 	struct oldproc	*op, *endbase;
343 	int		pcnt = 0;
344 	struct utsname	utsname;
345 	char		tmpbuf[20];
346 
347 	uname(&utsname);
348 	irix_ver = (float) atof((const char *)utsname.release);
349 	strncpy(tmpbuf, utsname.release, 9);
350 	tmpbuf[9] = '\0';
351 	sprintf(uname_str, "%s %-.14s %s %s",
352 		utsname.sysname, utsname.nodename,
353 		tmpbuf, utsname.machine);
354 
355 	pagesize = getpagesize();
356 
357 	if ((kmem = open(KMEM, O_RDONLY)) == -1) {
358 		perror(KMEM);
359 		return -1;
360 	}
361 
362 	if (chdir(_PATH_PROCFSPI)) {
363 		/* handy for later on when we're reading it */
364 		(void) fprintf(stderr, "%s: Unable to chdir to %s\n",
365 					myname, _PATH_PROCFSPI);
366 		return -1;
367 	}
368 	if ((procdir = opendir(".")) == NULL) {
369 		(void) fprintf(stderr, "%s: Unable to open %s\n",
370 					myname, _PATH_PROCFSPI);
371 		return -1;
372 	}
373 
374 	if ((avenrun_offset = sysmp(MP_KERNADDR, MPKA_AVENRUN)) == -1) {
375 		perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)");
376 		return -1;
377 	}
378 
379 	allocate_proc_tables();
380 
381 	oldprocs = 2 * nproc;
382 	endbase = oldbase + oldprocs;
383 	for (op = oldbase; op < endbase; op++) {
384 		op->oldpid = -1;
385 	}
386 
387 	statics->cpustate_names = cpustatenames;
388 	statics->memory_names = memorynames;
389 	statics->order_names = ordernames;
390 	statics->procstate_names = procstatenames;
391 
392 	return (0);
393 }
394 
395 char   *
396 format_header(register char *uname_field)
397 
398 {
399 	register char *ptr;
400 
401 	ptr = header + UNAME_START;
402 	while (*uname_field != '\0') {
403 		*ptr++ = *uname_field++;
404 	}
405 
406 	return (header);
407 }
408 
409 void
410 get_system_info(struct system_info *si)
411 
412 {
413 	int		i;
414 	int		avenrun[3];
415 	struct rminfo	realmem;
416 	struct sysinfo	sysinfo;
417 	static time_t	cp_old [CPU_STATES];
418 	static time_t	cp_diff[CPU_STATES];	/* for cpu state percentages */
419 	off_t		fswap;		/* current free swap in blocks */
420 	off_t		tswap;		/* total swap in blocks */
421 
422 	(void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun");
423 
424 	for (i = 0; i < 3; i++) {
425 		si->load_avg[i] = loaddouble(avenrun[i]);
426 		si->load_avg[i] /= 1024.0;
427 	}
428 
429 	if ((numcpus = sysmp(MP_NPROCS)) == -1) {
430 		perror("sysmp(MP_NPROCS)");
431 		return;
432 	}
433 
434 	if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) {
435 		perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)");
436 		return;
437 	}
438 
439 	swapctl(SC_GETFREESWAP, &fswap);
440 	swapctl(SC_GETSWAPTOT, &tswap);
441 
442 	memory_stats[0] = pagetok(realmem.physmem);
443 	memory_stats[1] = pagetok(realmem.availrmem);
444 	memory_stats[2] = pagetok(realmem.freemem);
445 	memory_stats[3] = tswap / 2;
446 	memory_stats[4] = fswap / 2;
447 
448 	if (sysmp(MP_SAGET,MPSA_SINFO, &sysinfo,sizeof(struct sysinfo)) == -1) {
449 		perror("sysmp(MP_SAGET,MPSA_SINFO)");
450 		return;
451 	}
452 	(void) percentages(CPU_STATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
453 
454 	si->cpustates = cpu_states;
455 	si->memory = memory_stats;
456 	si->last_pid = -1;
457 
458 	return;
459 }
460 
461 caddr_t
462 get_process_info(struct system_info *si, struct process_select *sel, int compare_index)
463 
464 {
465 	int		i, total_procs, active_procs;
466 	struct prpsinfo	**prefp;
467 	struct prpsinfo	*pp;
468 	int		show_uid;
469 	static char	first_screen = 1;
470 
471 	/* read all the proc structures */
472 	getptable(pbase);
473 
474 	/* get a pointer to the states summary array */
475 	si->procstates = process_states;
476 
477 	/* set up flags which define what we are going to select */
478 	show_uid = sel->uid != -1;
479 
480 	/* count up process states and get pointers to interesting procs */
481 	total_procs = 0;
482 	active_procs = 0;
483 	(void) memset(process_states, 0, sizeof(process_states));
484 	prefp = pref;
485 
486 	for (pp = pbase, i = 0; i < nproc; pp++, i++) {
487 		/*
488 		 * Place pointers to each valid proc structure in pref[].
489 		 * Process slots that are actually in use have a non-zero
490 		 * status field.  Processes with SSYS set are system
491 		 * processes---these get ignored unless show_system is set.
492 		 * Ariel: IRIX 6.4 had to redefine "system processes"
493 		 * They do not exist outside the kernel in new kernels.
494 		 * Now defining as uid==0 and ppid==1 (init children)
495 		 */
496 		if (pp->pr_state &&
497 			(sel->system || !(pp->pr_uid==0 && pp->pr_ppid==1))) {
498 			total_procs++;
499 			process_states[proc_state(pp)]++;
500 			/*
501 			 * zombies are actually interesting (to avoid)
502 			 * although they are not active, so I leave them
503 			 * displayed.
504 			 */
505 			if (/* (! pp->pr_zomb) && */
506 			    (sel->idle || IS_ACTIVE(pp)) &&
507 			    (! show_uid || pp->pr_uid == (uid_t) sel->uid)) {
508 				*prefp++ = pp;
509 				active_procs++;
510 			}
511 		}
512 	}
513 	first_screen = 0;
514 
515 	/* if requested, sort the "interesting" processes */
516 	qsort((char *) pref, active_procs, sizeof(struct prpsinfo *),
517 	      proc_compares[compare_index]);
518 
519 	/* remember active and total counts */
520 	si->p_total = total_procs;
521 	si->p_active = active_procs;
522 
523 	/* pass back a handle */
524 	handle.next_proc = pref;
525 	handle.remaining = active_procs;
526 	return ((caddr_t) &handle);
527 }
528 
529 /*
530  * Added cpu_id to running processes, add 'ready' (to run) state
531  */
532 static char *
533 format_state(struct prpsinfo *pp)
534 
535 {
536 	static char	state_str[16];
537 	int		state = proc_state(pp);
538 
539 	if (state == S_RUNNING) {
540 		/*
541 		 * Alert: 6.2 (MP only?) binary incompatibility
542 		 * pp->pr_sonproc apparently (?) has a different
543 		 * offset on 6.2 machines... I've seen cases where
544 		 * a 6.4 compiled top running on 6.2 printed
545 		 * a garbage CPU-id. To be safe, I print the CPU-id
546 		 * only if it falls within range [0..numcpus-1]
547 		 */
548 		sprintf(state_str, "run/%d", pp->pr_sonproc);
549 		return state_str;
550 	}
551 
552 	/* default */
553 	return state_abbrev[state];
554 }
555 
556 static char *
557 format_prio(struct prpsinfo *pp)
558 
559 {
560     static char	prio_str[10];
561 
562     if (irix_ver < 6.4) {
563 	/*
564 	 * Note: this is _compiled_ on 6.x where x >= 4 but I would like
565 	 * it to run on 6.2 6.3 as well (backward binary compatibility).
566 	 * Scheduling is completely different between these IRIX versions
567 	 * and some scheduling classes may even have different names.
568 	 *
569 	 * The solution: have more than one style of 'priority' depending
570 	 * on the OS version.
571 	 *
572 	 * See npri(1) + nice(2) + realtime(5) for scheduling classes,
573 	 * and priority values.
574 	 */
575 	if (pp->pr_pri <= NDPHIMIN)			/* real time? */
576 		sprintf(prio_str, "+%d", pp->pr_pri);
577 	else if (pp->pr_pri <= NDPNORMMIN)		/* normal interactive */
578 		sprintf(prio_str, "%d", pp->pr_pri);
579 	else						/* batch: low prio */
580 		sprintf(prio_str, "b%d", pp->pr_pri);
581 
582     } else {
583 
584 	/* copied from Kostadis's code */
585 
586 	if (strcmp(pp->pr_clname, "RT") == 0)		/* real time */
587 		sprintf(prio_str, "+%d", pp->pr_pri);
588 	else if (strcmp(pp->pr_clname, "DL") == 0)	/* unsupported ? */
589 		sprintf(prio_str, "d%d", pp->pr_pri);
590 	else if (strcmp(pp->pr_clname, "GN") == 0)
591 		sprintf(prio_str, "g%d", pp->pr_pri);
592 	else if (strcmp(pp->pr_clname, "GB") == 0)
593 		sprintf(prio_str, "p%d", pp->pr_pri);
594 
595 	else if (strcmp(pp->pr_clname, "WL") == 0)	/* weightless */
596 		return "w";
597 	else if (strcmp(pp->pr_clname, "BC") == 0)
598 		return "bc";				/* batch critical */
599 	else if (strcmp(pp->pr_clname, "B") == 0)
600 		return "b";				/* batch */
601 	else
602 		sprintf(prio_str, "%d", pp->pr_pri);
603     }
604     return prio_str;
605 }
606 
607 static double
608 clip_percent(double pct)
609 
610 {
611     if (pct < 0) {
612 	return 0.0;
613     } else if (pct >= 100) {
614 	return 99.99;
615     }
616     return pct;
617 }
618 
619 char *
620 format_next_process(caddr_t handle, char *(*get_userid)())
621 
622 {
623 	struct prpsinfo	*pp;
624 	struct handle	*hp;
625 	long		cputime;
626 
627 	/* find and remember the next proc structure */
628 	hp = (struct handle *) handle;
629 	pp = *(hp->next_proc++);
630 	hp->remaining--;
631 
632 	/* get the process cpu usage since startup */
633 	cputime = pp->pr_time.tv_sec;
634 
635 	/* format this entry */
636 	sprintf(fmt,
637 		Proc_format,
638 		pp->pr_pid,
639 		pp->pr_pgrp,
640 		(*get_userid) (pp->pr_uid),
641 		format_prio(pp),
642 		format_k(pagetok(pp->pr_size)),
643 		format_k(pagetok(pp->pr_rssize)),
644 		format_state(pp),
645 		format_time(cputime),
646 		clip_percent(weighted_cpu(pp)),
647 		clip_percent(percent_cpu(pp)),
648 		printable(pp->pr_fname));
649 
650 	/* return the result */
651 	return (fmt);
652 }
653 
654 /*
655  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
656  *	"offset" is the byte offset into the kernel for the desired value,
657  *  	"ptr" points to a buffer into which the value is retrieved,
658  *  	"size" is the size of the buffer (and the object to retrieve),
659  *  	"refstr" is a reference string used when printing error meessages,
660  *	    if "refstr" starts with a '!', then a failure on read will not
661  *  	    be fatal (this may seem like a silly way to do things, but I
662  *  	    really didn't want the overhead of another argument).
663  *
664  */
665 
666 int
667 getkval(unsigned long offset, int *ptr, int size, char *refstr)
668 
669 {
670 	if (lseek(kmem, (long) offset, SEEK_SET) == -1) {
671 		if (*refstr == '!')
672 			refstr++;
673 		(void) fprintf(stderr, "%s: %s: lseek to %s: %s\n",
674 				myname, KMEM, refstr, strerror(errno));
675 		exit(0);
676 	}
677 	if (read(kmem, (char *) ptr, size) == -1) {
678 		if (*refstr == '!')
679 			return (0);
680 		else {
681 			(void) fprintf(stderr, "%s: %s: reading %s: %s\n",
682 				myname, KMEM, refstr, strerror(errno));
683 			exit(0);
684 		}
685 	}
686 	return (1);
687 }
688 
689 /*
690  *  compare_K - comparison functions for "qsort"
691  *	Compares the resource consumption of two processes using five
692  *  	distinct keys.  The keys are:
693  *  	percent cpu, cpu ticks, state, resident set size, total virtual
694  *  	memory usage.  The process states are ordered as follows (from least
695  *  	to most important):  WAIT, zombie, sleep, stop, idle, run.
696  *  	Different comparison functions are used for different orderings.
697  */
698 
699 /* these are names given to allowed sorting orders -- first is default */
700 char *ordernames[] = {
701 	/*
702 	 * Aliases for user convenience/friendliness:
703 	 *	mem == size
704 	 *	rss == res
705 	 */
706 	"cpu", "size", "mem", "res", "rss",
707 	"time", "state", "command", "prio", NULL
708 };
709 
710 /* forward definitions for comparison functions */
711 int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2);
712 int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2);
713 int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2);
714 int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2);
715 int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2);
716 int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2);
717 int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2);
718 
719 int (*proc_compares[])() = {
720 	compare_cpu,
721 	compare_size,
722 	compare_size,
723 	compare_res,
724 	compare_res,
725 	compare_time,
726 	compare_state,
727 	compare_cmd,
728 	compare_prio,
729 	NULL
730 };
731 
732 
733 /*
734  * The possible comparison expressions.  These are defined in such a way
735  * that they can be merely listed in the source code to define the actual
736  * desired ordering.
737  */
738 
739 #define ORDERKEY_PCTCPU	\
740 	if (dresult = percent_cpu(p2) - percent_cpu(p1),\
741 	(result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
742 #define ORDERKEY_CPTICKS \
743 	if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
744 #define ORDERKEY_STATE  if ((result = proc_state(p2) - proc_state(p1)) == 0)
745 #define ORDERKEY_PRIO	if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
746 #define ORDERKEY_RSSIZE	if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
747 #define ORDERKEY_MEM	if ((result = (p2->pr_size - p1->pr_size)) == 0)
748 #define ORDERKEY_CMD	if ((result = strcmp(p1->pr_fname,p2->pr_fname)) == 0)
749 
750 int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2)
751 {
752 	struct prpsinfo	*p1, *p2;
753 	int		result;
754 	double		dresult;
755 
756 	/* remove one level of indirection */
757 	p1 = *pp1;
758 	p2 = *pp2;
759 	/*
760 	 * order by various keys, resorting to the next one
761 	 * whenever there's a tie in comparisons
762 	 */
763 	ORDERKEY_PCTCPU
764 	ORDERKEY_CPTICKS
765 	ORDERKEY_STATE
766 	ORDERKEY_PRIO
767 	ORDERKEY_RSSIZE
768 	ORDERKEY_MEM
769 	;
770 	return (result);
771 }
772 
773 int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2)
774 {
775 	struct prpsinfo	*p1, *p2;
776 	int		result;
777 	double		dresult;
778 
779 	/* remove one level of indirection */
780 	p1 = *pp1;
781 	p2 = *pp2;
782 	/*
783 	 * order by various keys, resorting to the next one
784 	 * whenever there's a tie in comparisons
785 	 */
786 	ORDERKEY_MEM
787 	ORDERKEY_RSSIZE
788 	ORDERKEY_PCTCPU
789 	ORDERKEY_CPTICKS
790 	ORDERKEY_STATE
791 	ORDERKEY_PRIO
792 	;
793 	return (result);
794 }
795 
796 int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2)
797 {
798 	struct prpsinfo	*p1, *p2;
799 	int		result;
800 	double		dresult;
801 
802 	/* remove one level of indirection */
803 	p1 = *pp1;
804 	p2 = *pp2;
805 	/*
806 	 * order by various keys, resorting to the next one
807 	 * whenever there's a tie in comparisons
808 	 */
809 	ORDERKEY_RSSIZE
810 	ORDERKEY_MEM
811 	ORDERKEY_PCTCPU
812 	ORDERKEY_CPTICKS
813 	ORDERKEY_STATE
814 	ORDERKEY_PRIO
815 	;
816 	return (result);
817 }
818 
819 int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2)
820 {
821 	struct prpsinfo	*p1, *p2;
822 	int		result;
823 	double		dresult;
824 
825 	/* remove one level of indirection */
826 	p1 = *pp1;
827 	p2 = *pp2;
828 	/*
829 	 * order by various keys, resorting to the next one
830 	 * whenever there's a tie in comparisons
831 	 */
832 	ORDERKEY_CPTICKS
833 	ORDERKEY_RSSIZE
834 	ORDERKEY_MEM
835 	ORDERKEY_PCTCPU
836 	ORDERKEY_STATE
837 	ORDERKEY_PRIO
838 	;
839 	return (result);
840 }
841 
842 int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2)
843 {
844 	struct prpsinfo	*p1, *p2;
845 	int		result;
846 	double		dresult;
847 
848 	/* remove one level of indirection */
849 	p1 = *pp1;
850 	p2 = *pp2;
851 	/*
852 	 * order by various keys, resorting to the next one
853 	 * whenever there's a tie in comparisons
854 	 */
855 	ORDERKEY_CMD
856 	ORDERKEY_PCTCPU
857 	ORDERKEY_CPTICKS
858 	ORDERKEY_RSSIZE
859 	;
860 	return (result);
861 }
862 
863 int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2)
864 {
865 	struct prpsinfo	*p1, *p2;
866 	int		result;
867 	double		dresult;
868 
869 	/* remove one level of indirection */
870 	p1 = *pp1;
871 	p2 = *pp2;
872 	/*
873 	 * order by various keys, resorting to the next one
874 	 * whenever there's a tie in comparisons
875 	 */
876 	ORDERKEY_STATE
877 	ORDERKEY_PCTCPU
878 	ORDERKEY_CPTICKS
879 	ORDERKEY_RSSIZE
880 	;
881 	return (result);
882 }
883 
884 int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2)
885 {
886 	struct prpsinfo	*p1, *p2;
887 	int		result;
888 	double		dresult;
889 
890 	/* remove one level of indirection */
891 	p1 = *pp1;
892 	p2 = *pp2;
893 	/*
894 	 * order by various keys, resorting to the next one
895 	 * whenever there's a tie in comparisons
896 	 */
897 	ORDERKEY_PRIO
898 	ORDERKEY_PCTCPU
899 	;
900 	return (result);
901 }
902 
903 
904 
905 /* return the owner of the specified process. */
906 uid_t
907 proc_owner(pid_t pid)
908 
909 {
910 	register struct prpsinfo *p;
911 	int     i;
912 
913 	for (i = 0, p = pbase; i < nproc; i++, p++)
914 		if (p->pr_pid == pid)
915 			return (p->pr_uid);
916 
917 	return (-1);
918 }
919 
920 #ifdef DO_MAPSIZE
921 static void
922 size(int fd, struct prpsinfo *ps)
923 
924 {
925 	prmap_sgi_arg_t maparg;
926 	struct prmap_sgi maps[256];
927 	int	nmaps;
928 	double	sz;
929 	int	i;
930 
931 	maparg.pr_vaddr = (caddr_t) maps;
932 	maparg.pr_size = sizeof maps;
933 	if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) {
934 		/* XXX - this will be confusing */
935 		return;
936 	}
937 	for (i = 0, sz = 0; i < nmaps; ++i) {
938 		sz += (double) maps[i].pr_wsize / MA_WSIZE_FRAC;
939 	}
940 	ps->pr_rssize = (long) sz;
941 }
942 #endif
943 
944 /* get process table */
945 void
946 getptable(struct prpsinfo *baseptr)
947 
948 {
949 	struct prpsinfo		*currproc; /* ptr to current proc struct */
950 	int			i, numprocs;
951 	struct dirent		*direntp;
952 	struct oldproc		*op, *endbase;
953 	static struct timeval	lasttime, thistime;
954 	static double		timediff, alpha, beta;
955 
956 	/* measure time between last call to getptable and current call */
957 	gettimeofday (&thistime, NULL);
958 
959 	/*
960 	 * To avoid divides, we keep times in nanoseconds.  This is
961 	 * scaled by 1e7 rather than 1e9 so that when we divide we
962 	 * get percent.
963 	 */
964 	timediff = ((double) thistime.tv_sec  * 1.0e7 -
965 		    (double) lasttime.tv_sec  * 1.0e7)
966 				+
967 		   ((double) thistime.tv_usec * 10 -
968 		    (double) lasttime.tv_usec * 10);
969 
970 	/*
971 	 * Under extreme load conditions, sca has experienced
972 	 * an assert(timediff > 0) failure here. His guess is that
973 	 * sometimes timed resets the time backwards and gettimeofday
974 	 * returns a lower number on a later call.
975 	 * To be on the safe side I fix it here by setting timediff
976 	 * to some arbitrary small value (in nanoseconds).
977 	 */
978 	if (timediff <= 0.0) timediff = 100.0;
979 
980 	lasttime = thistime;	/* prepare for next round */
981 
982 	/*
983 	 * constants for exponential decaying average.
984 	 *	avg = alpha * new + beta * avg
985 	 * The goal is 50% decay in 30 sec.  However if the sample period
986 	 * is greater than 30 sec, there's not a lot we can do.
987 	 */
988 	if (timediff < 30.0e7) {
989 		alpha = 0.5 * (timediff / 15.0e7);
990 		beta = 1.0 - alpha;
991 	} else {
992 		alpha = 0.5;
993 		beta = 0.5;
994 	}
995 	assert(alpha >= 0); assert(alpha <= 1);
996 	assert(beta >= 0); assert(beta <= 1);
997 
998 	endbase = oldbase + oldprocs;
999 	currproc = baseptr;
1000 
1001 	for (numprocs = 0, rewinddir(procdir); direntp = readdir(procdir);) {
1002 		int     fd;
1003 
1004 		if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
1005 			continue;
1006 
1007 		currproc = baseptr + numprocs;
1008 
1009 		if (ioctl(fd, PIOCPSINFO, currproc) < 0) {
1010 			(void) close(fd);
1011 			continue;
1012 		}
1013 
1014 		/*
1015 		 * SVR4 doesn't keep track of CPU% in the kernel,
1016 		 * so we have to do our own.
1017 		 * See if we've heard of this process before.
1018 		 * If so, compute % based on CPU since last time.
1019 		 */
1020 		op = oldbase + HASH (currproc->pr_pid);
1021 		for (;;) {
1022 			if (op->oldpid == -1) /* not there */
1023 				break;
1024 			if (op->oldpid == currproc->pr_pid) {
1025 				/* found old data */
1026 				percent_cpu(currproc) =
1027 					((currproc->pr_time.tv_sec * 1.0e9 +
1028 					currproc->pr_time.tv_nsec)
1029 					- op->oldtime) / timediff;
1030 
1031 				weighted_cpu(currproc) =
1032 					op->oldpct * beta +
1033 					percent_cpu(currproc) * alpha;
1034 
1035 				break;
1036 			}
1037 			op++;		/* try next entry in hash table */
1038 			if (op == endbase)    /* table wrap around */
1039 				op = oldbase;
1040 		}
1041 
1042 		/* Otherwise, it's new, so use all of its CPU time */
1043 		if (op->oldpid == -1) {
1044 			if (lasttime.tv_sec) {
1045 				percent_cpu(currproc) =
1046 					(currproc->pr_time.tv_sec * 1.0e9 +
1047 					currproc->pr_time.tv_nsec) / timediff;
1048 
1049 				weighted_cpu(currproc) = percent_cpu(currproc);
1050 			} else {
1051 				/* first screen -- no difference is possible */
1052 				percent_cpu(currproc) = 0.0;
1053 				weighted_cpu(currproc) = 0.0;
1054 			}
1055 		}
1056 
1057 #ifdef DO_MAPSIZE
1058 		size(fd, currproc);
1059 #endif
1060 		numprocs++;
1061 		(void) close(fd);
1062 
1063 		/*
1064 		 * Bug: in case process count grew so dramatically
1065 		 * as to exceed to table size. We give up on a full scan.
1066 		 * the chances of this to happen are extremely slim due to
1067 		 * the big factor we're using. getting nproc from nlist
1068 		 * is not worth the headache. realloc wouldn't work either
1069 		 * because we have pointers to the proc table so we cannot
1070 		 * move it around.
1071 		 */
1072 		if (numprocs >= ptable_size) {
1073 			fprintf(stderr,
1074 				"preallocated proc table size (%d) exceeded, "
1075 				"skipping some processes\n", ptable_size);
1076 			break;
1077 		}
1078 	}
1079 	nproc = numprocs;
1080 
1081 	/*
1082 	 * Save current CPU time for next time around
1083 	 * For the moment recreate the hash table each time, as the code
1084 	 * is easier that way.
1085 	 */
1086 	oldprocs = 2 * nproc;
1087 	endbase = oldbase + oldprocs;
1088 
1089 	for (op = oldbase; op < endbase; op++)
1090 		op->oldpid = -1;
1091 
1092 	for (i = 0, currproc = baseptr; i < nproc; i++, currproc++) {
1093 
1094 		/* find an empty spot */
1095 		op = oldbase + HASH (currproc->pr_pid);
1096 		for (;;) {
1097 			if (op->oldpid == -1)
1098 				break;
1099 			op++;
1100 			if (op == endbase)
1101 				op = oldbase;
1102         	}
1103 		op->oldpid = currproc->pr_pid;
1104 		op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
1105 				currproc->pr_time.tv_nsec);
1106 		op->oldpct = weighted_cpu(currproc);
1107 	}
1108 }
1109 
1110