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
93typedef 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 */
102struct oldproc {
103	pid_t	oldpid;
104	double	oldtime;
105	double	oldpct;
106};
107static int oldprocs;                    /* size of table */
108static 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 */
130static 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
158int	cpu_states[CPU_STATES];
159char	*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
167int	memory_stats[MEMSTATS];
168char	*memorynames[] = {
169	"K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL
170};
171
172char	uname_str[40];
173double	load[3];
174static  char fmt[MAX_COLS + 2];
175int	numcpus;
176
177/* useful externals */
178extern int	errno;
179extern char	*sys_errlist[];
180
181extern char	*myname;
182extern char	*format_k();
183extern char	*format_time();
184extern long	percentages();
185
186static int kmem;
187static unsigned long avenrun_offset;
188
189static float	irix_ver;		/* for easy numeric comparison */
190
191static struct prpsinfo	*pbase;
192static struct prpsinfo	**pref;
193static struct oldproc	*oldbase;
194static int		oldprocs;	/* size of table */
195
196static DIR	*procdir;
197
198static int	ptable_size;	/* allocated process table size */
199static int	nproc;		/* estimated process table size */
200static int	pagesize;
201
202/* get_process_info passes back a handle.  This is what it looks like: */
203struct handle {
204	struct prpsinfo **next_proc;	/* points to next valid proc pointer */
205	int		remaining;	/* number of pointers remaining */
206};
207
208static struct handle	handle;
209
210void getptable(struct prpsinfo *baseptr);
211void size(int fd, struct prpsinfo *ps);
212
213extern 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 */
235static char *state_abbrev[] =
236{ "", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL };
237
238/* Same but a little "wordier", used in CPU activity summary */
239int     process_states[8];	/* per state counters */
240char	*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 */
264static 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 */
289static 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 */
312void
313allocate_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
339int
340machine_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
395char   *
396format_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
409void
410get_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
461caddr_t
462get_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 */
532static char *
533format_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
556static char *
557format_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
607static double
608clip_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
619char *
620format_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
666int
667getkval(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 */
700char *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 */
711int compare_cpu(struct prpsinfo **pp1, struct prpsinfo **pp2);
712int compare_size(struct prpsinfo **pp1, struct prpsinfo **pp2);
713int compare_res(struct prpsinfo **pp1, struct prpsinfo **pp2);
714int compare_time(struct prpsinfo **pp1, struct prpsinfo **pp2);
715int compare_state(struct prpsinfo **pp1, struct prpsinfo **pp2);
716int compare_cmd(struct prpsinfo **pp1, struct prpsinfo **pp2);
717int compare_prio(struct prpsinfo **pp1, struct prpsinfo **pp2);
718
719int (*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
750int 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
773int 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
796int 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
819int 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
842int 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
863int 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
884int 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. */
906uid_t
907proc_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
921static void
922size(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 */
945void
946getptable(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