machine.c revision 27340
124143Sjoerg/*
224143Sjoerg * top - a top users display for Unix
324143Sjoerg *
424143Sjoerg * SYNOPSIS:  For FreeBSD-2.x system
524143Sjoerg *
624143Sjoerg * DESCRIPTION:
724143Sjoerg * Originally written for BSD4.4 system by Christos Zoulas.
824143Sjoerg * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
924143Sjoerg *
1024143Sjoerg * This is the machine-dependent module for FreeBSD 2.2
1124143Sjoerg * Works for:
1224143Sjoerg *	FreeBSD 2.2, and probably FreeBSD 2.1.x
1324143Sjoerg *
1424143Sjoerg * LIBS: -lkvm
1524143Sjoerg *
1624143Sjoerg * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
1724143Sjoerg *          Steven Wallace  <swallace@freebsd.org>
1824143Sjoerg *          Wolfram Schneider <wosch@FreeBSD.org>
1924143Sjoerg *
2027340Speter * $Id: machine.c,v 1.3 1997/04/21 13:53:47 ache Exp $
2124143Sjoerg */
2224143Sjoerg
2324143Sjoerg
2424143Sjoerg#include <sys/types.h>
2524143Sjoerg#include <sys/signal.h>
2624143Sjoerg#include <sys/param.h>
2724143Sjoerg
2824143Sjoerg#include "os.h"
2924143Sjoerg#include <stdio.h>
3024143Sjoerg#include <nlist.h>
3124143Sjoerg#include <math.h>
3224143Sjoerg#include <kvm.h>
3324143Sjoerg#include <sys/errno.h>
3424143Sjoerg#include <sys/sysctl.h>
3524143Sjoerg#include <sys/dkstat.h>
3624143Sjoerg#include <sys/file.h>
3724143Sjoerg#include <sys/time.h>
3824143Sjoerg#include <sys/proc.h>
3924143Sjoerg#include <sys/user.h>
4024143Sjoerg#include <sys/vmmeter.h>
4124143Sjoerg
4224143Sjoerg/* Swap */
4324143Sjoerg#include <stdlib.h>
4424143Sjoerg#include <sys/rlist.h>
4524143Sjoerg#include <sys/conf.h>
4624143Sjoerg
4724143Sjoerg#include <osreldate.h> /* for changes in kernel structures */
4824143Sjoerg
4924143Sjoerg#include "top.h"
5024143Sjoerg#include "machine.h"
5124143Sjoerg
5224143Sjoergstatic int check_nlist __P((struct nlist *));
5324143Sjoergstatic int getkval __P((unsigned long, int *, int, char *));
5424143Sjoergextern char* printable __P((char *));
5524143Sjoergint swapmode __P((int *retavail, int *retfree));
5627340Speterstatic int smpmode;
5724143Sjoerg
5824143Sjoerg
5924143Sjoerg
6024143Sjoerg/* get_process_info passes back a handle.  This is what it looks like: */
6124143Sjoerg
6224143Sjoergstruct handle
6324143Sjoerg{
6424143Sjoerg    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
6524143Sjoerg    int remaining;		/* number of pointers remaining */
6624143Sjoerg};
6724143Sjoerg
6824143Sjoerg/* declarations for load_avg */
6924143Sjoerg#include "loadavg.h"
7024143Sjoerg
7124143Sjoerg#define PP(pp, field) ((pp)->kp_proc . field)
7224143Sjoerg#define EP(pp, field) ((pp)->kp_eproc . field)
7324143Sjoerg#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
7424143Sjoerg
7524143Sjoerg/* define what weighted cpu is.  */
7624143Sjoerg#define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \
7724143Sjoerg			 ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu))))
7824143Sjoerg
7924143Sjoerg/* what we consider to be process size: */
8024143Sjoerg#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
8124143Sjoerg
8224143Sjoerg/* definitions for indices in the nlist array */
8324143Sjoerg
8424143Sjoerg
8524143Sjoergstatic struct nlist nlst[] = {
8624143Sjoerg#define X_CCPU		0
8724143Sjoerg    { "_ccpu" },		/* 0 */
8824143Sjoerg#define X_CP_TIME	1
8924143Sjoerg    { "_cp_time" },		/* 1 */
9024143Sjoerg#define X_HZ		2
9124143Sjoerg    { "_hz" },		        /* 2 */
9224143Sjoerg#define X_STATHZ	3
9324143Sjoerg    { "_stathz" },		/* 3 */
9424143Sjoerg#define X_AVENRUN	4
9524143Sjoerg    { "_averunnable" },		/* 4 */
9624143Sjoerg
9724143Sjoerg/* Swap */
9824143Sjoerg#define VM_SWAPLIST	5
9924143Sjoerg	{ "_swaplist" },/* list of free swap areas */
10024143Sjoerg#define VM_SWDEVT	6
10124143Sjoerg	{ "_swdevt" },	/* list of swap devices and sizes */
10224143Sjoerg#define VM_NSWAP	7
10324143Sjoerg	{ "_nswap" },	/* size of largest swap device */
10424143Sjoerg#define VM_NSWDEV	8
10524143Sjoerg	{ "_nswdev" },	/* number of swap devices */
10624143Sjoerg#define VM_DMMAX	9
10724143Sjoerg	{ "_dmmax" },	/* maximum size of a swap block */
10824143Sjoerg#define X_BUFSPACE	10
10924143Sjoerg	{ "_bufspace" },	/* K in buffer cache */
11024143Sjoerg#define X_CNT           11
11124143Sjoerg    { "_cnt" },		        /* struct vmmeter cnt */
11224143Sjoerg
11324143Sjoerg/* Last pid */
11424143Sjoerg#define X_LASTPID	12
11524143Sjoerg    { "_nextpid" },
11624143Sjoerg    { 0 }
11724143Sjoerg};
11824143Sjoerg
11924143Sjoerg/*
12024143Sjoerg *  These definitions control the format of the per-process area
12124143Sjoerg */
12224143Sjoerg
12327340Speterstatic char smp_header[] =
12425077Sache  "  PID X                PRI NICE SIZE   RES STATE C   TIME   WCPU    CPU COMMAND";
12524143Sjoerg/* 0123456   -- field to fill in starts at header+6 */
12627340Speter#define SMP_UNAME_START 6
12724143Sjoerg
12827340Speter#define smp_Proc_format \
12925077Sache	"%5d %-16.16s%3d%3d%7s %6s %-6.6s%1x%7s %5.2f%% %5.2f%% %.6s"
13024143Sjoerg
13127340Speterstatic char up_header[] =
13225077Sache  "  PID X                PRI NICE SIZE    RES STATE    TIME   WCPU    CPU COMMAND";
13324143Sjoerg/* 0123456   -- field to fill in starts at header+6 */
13427340Speter#define UP_UNAME_START 6
13524143Sjoerg
13627340Speter#define up_Proc_format \
13727340Speter	"%5d %-16.16s%3d %3d%7s %6s %-6.6s%.0d%7s %5.2f%% %5.2f%% %.6s"
13824143Sjoerg
13924143Sjoerg
14024143Sjoerg
14124143Sjoerg/* process state names for the "STATE" column of the display */
14224143Sjoerg/* the extra nulls in the string "run" are for adding a slash and
14324143Sjoerg   the processor number when needed */
14424143Sjoerg
14524143Sjoergchar *state_abbrev[] =
14624143Sjoerg{
14724143Sjoerg    "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB",
14824143Sjoerg};
14924143Sjoerg
15024143Sjoerg
15124143Sjoergstatic kvm_t *kd;
15224143Sjoerg
15324143Sjoerg/* values that we stash away in _init and use in later routines */
15424143Sjoerg
15524143Sjoergstatic double logcpu;
15624143Sjoerg
15724143Sjoerg/* these are retrieved from the kernel in _init */
15824143Sjoerg
15924143Sjoergstatic          long hz;
16024143Sjoergstatic load_avg  ccpu;
16124143Sjoerg
16224143Sjoerg/* these are offsets obtained via nlist and used in the get_ functions */
16324143Sjoerg
16424143Sjoergstatic unsigned long cp_time_offset;
16524143Sjoergstatic unsigned long avenrun_offset;
16624143Sjoergstatic unsigned long lastpid_offset;
16724143Sjoergstatic long lastpid;
16824143Sjoergstatic unsigned long cnt_offset;
16924143Sjoergstatic unsigned long bufspace_offset;
17024143Sjoergstatic long cnt;
17124143Sjoerg
17224143Sjoerg/* these are for calculating cpu state percentages */
17324143Sjoerg
17424143Sjoergstatic long cp_time[CPUSTATES];
17524143Sjoergstatic long cp_old[CPUSTATES];
17624143Sjoergstatic long cp_diff[CPUSTATES];
17724143Sjoerg
17824143Sjoerg/* these are for detailing the process states */
17924143Sjoerg
18024143Sjoergint process_states[6];
18124143Sjoergchar *procstatenames[] = {
18224143Sjoerg    "", " starting, ", " running, ", " sleeping, ", " stopped, ",
18324143Sjoerg    " zombie, ",
18424143Sjoerg    NULL
18524143Sjoerg};
18624143Sjoerg
18724143Sjoerg/* these are for detailing the cpu states */
18824143Sjoerg
18924143Sjoergint cpu_states[CPUSTATES];
19024143Sjoergchar *cpustatenames[] = {
19124143Sjoerg    "user", "nice", "system", "interrupt", "idle", NULL
19224143Sjoerg};
19324143Sjoerg
19424143Sjoerg/* these are for detailing the memory statistics */
19524143Sjoerg
19624143Sjoergint memory_stats[7];
19724143Sjoergchar *memorynames[] = {
19824143Sjoerg    "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
19924143Sjoerg    NULL
20024143Sjoerg};
20124143Sjoerg
20224143Sjoergint swap_stats[7];
20324143Sjoergchar *swapnames[] = {
20424143Sjoerg/*   0           1            2           3            4       5 */
20524143Sjoerg    "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
20624143Sjoerg    NULL
20724143Sjoerg};
20824143Sjoerg
20924143Sjoerg
21024143Sjoerg/* these are for keeping track of the proc array */
21124143Sjoerg
21224143Sjoergstatic int nproc;
21324143Sjoergstatic int onproc = -1;
21424143Sjoergstatic int pref_len;
21524143Sjoergstatic struct kinfo_proc *pbase;
21624143Sjoergstatic struct kinfo_proc **pref;
21724143Sjoerg
21824143Sjoerg/* these are for getting the memory statistics */
21924143Sjoerg
22024143Sjoergstatic int pageshift;		/* log base 2 of the pagesize */
22124143Sjoerg
22224143Sjoerg/* define pagetok in terms of pageshift */
22324143Sjoerg
22424143Sjoerg#define pagetok(size) ((size) << pageshift)
22524143Sjoerg
22624143Sjoerg/* useful externals */
22724143Sjoerglong percentages();
22824143Sjoerg
22924143Sjoergint
23024143Sjoergmachine_init(statics)
23124143Sjoerg
23224143Sjoergstruct statics *statics;
23324143Sjoerg
23424143Sjoerg{
23524143Sjoerg    register int i = 0;
23624143Sjoerg    register int pagesize;
23727340Speter    int modelen;
23824143Sjoerg
23927340Speter    modelen = sizeof(smpmode);
24027340Speter    if (sysctlbyname("kern.smp_active", &smpmode, &modelen, NULL, 0) < 0 ||
24127340Speter	modelen != sizeof(smpmode))
24227340Speter	    smpmode = 0;
24327340Speter
24424143Sjoerg    if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
24524143Sjoerg	return -1;
24624143Sjoerg
24724143Sjoerg
24824143Sjoerg    /* get the list of symbols we want to access in the kernel */
24924143Sjoerg    (void) kvm_nlist(kd, nlst);
25024143Sjoerg    if (nlst[0].n_type == 0)
25124143Sjoerg    {
25224143Sjoerg	fprintf(stderr, "top: nlist failed\n");
25324143Sjoerg	return(-1);
25424143Sjoerg    }
25524143Sjoerg
25624143Sjoerg    /* make sure they were all found */
25724143Sjoerg    if (i > 0 && check_nlist(nlst) > 0)
25824143Sjoerg    {
25924143Sjoerg	return(-1);
26024143Sjoerg    }
26124143Sjoerg
26224143Sjoerg    /* get the symbol values out of kmem */
26324143Sjoerg    (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!");
26424143Sjoerg    if (!hz) {
26524143Sjoerg	(void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
26624143Sjoerg		       nlst[X_HZ].n_name);
26724143Sjoerg    }
26824143Sjoerg
26924143Sjoerg    (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),	sizeof(ccpu),
27024143Sjoerg	    nlst[X_CCPU].n_name);
27124143Sjoerg
27224143Sjoerg    /* stash away certain offsets for later use */
27324143Sjoerg    cp_time_offset = nlst[X_CP_TIME].n_value;
27424143Sjoerg    avenrun_offset = nlst[X_AVENRUN].n_value;
27524143Sjoerg    lastpid_offset =  nlst[X_LASTPID].n_value;
27624143Sjoerg    cnt_offset = nlst[X_CNT].n_value;
27724143Sjoerg    bufspace_offset = nlst[X_BUFSPACE].n_value;
27824143Sjoerg
27924143Sjoerg    /* this is used in calculating WCPU -- calculate it ahead of time */
28024143Sjoerg    logcpu = log(loaddouble(ccpu));
28124143Sjoerg
28224143Sjoerg    pbase = NULL;
28324143Sjoerg    pref = NULL;
28424143Sjoerg    nproc = 0;
28524143Sjoerg    onproc = -1;
28624143Sjoerg    /* get the page size with "getpagesize" and calculate pageshift from it */
28724143Sjoerg    pagesize = getpagesize();
28824143Sjoerg    pageshift = 0;
28924143Sjoerg    while (pagesize > 1)
29024143Sjoerg    {
29124143Sjoerg	pageshift++;
29224143Sjoerg	pagesize >>= 1;
29324143Sjoerg    }
29424143Sjoerg
29524143Sjoerg    /* we only need the amount of log(2)1024 for our conversion */
29624143Sjoerg    pageshift -= LOG1024;
29724143Sjoerg
29824143Sjoerg    /* fill in the statics information */
29924143Sjoerg    statics->procstate_names = procstatenames;
30024143Sjoerg    statics->cpustate_names = cpustatenames;
30124143Sjoerg    statics->memory_names = memorynames;
30224143Sjoerg    statics->swap_names = swapnames;
30324143Sjoerg
30424143Sjoerg    /* all done! */
30524143Sjoerg    return(0);
30624143Sjoerg}
30724143Sjoerg
30824143Sjoergchar *format_header(uname_field)
30924143Sjoerg
31024143Sjoergregister char *uname_field;
31124143Sjoerg
31224143Sjoerg{
31324143Sjoerg    register char *ptr;
31424143Sjoerg
31527340Speter    if (smpmode)
31627340Speter	ptr = smp_header + SMP_UNAME_START;
31727340Speter    else
31827340Speter	ptr = up_header + UP_UNAME_START;
31927340Speter
32024143Sjoerg    while (*uname_field != '\0')
32124143Sjoerg    {
32224143Sjoerg	*ptr++ = *uname_field++;
32324143Sjoerg    }
32424143Sjoerg
32527340Speter    return(smpmode ? smp_header : up_header);
32624143Sjoerg}
32724143Sjoerg
32824143Sjoergstatic int swappgsin = -1;
32924143Sjoergstatic int swappgsout = -1;
33024143Sjoergextern struct timeval timeout;
33124143Sjoerg
33224143Sjoergvoid
33324143Sjoergget_system_info(si)
33424143Sjoerg
33524143Sjoergstruct system_info *si;
33624143Sjoerg
33724143Sjoerg{
33824143Sjoerg    long total;
33924143Sjoerg    load_avg avenrun[3];
34024143Sjoerg
34124143Sjoerg    /* get the cp_time array */
34224143Sjoerg    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
34324143Sjoerg		   nlst[X_CP_TIME].n_name);
34424143Sjoerg    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
34524143Sjoerg		   nlst[X_AVENRUN].n_name);
34624143Sjoerg
34724143Sjoerg    (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid),
34824143Sjoerg		   "!");
34924143Sjoerg
35024143Sjoerg    /* convert load averages to doubles */
35124143Sjoerg    {
35224143Sjoerg	register int i;
35324143Sjoerg	register double *infoloadp;
35424143Sjoerg	load_avg *avenrunp;
35524143Sjoerg
35624143Sjoerg#ifdef notyet
35724143Sjoerg	struct loadavg sysload;
35824143Sjoerg	int size;
35924143Sjoerg	getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
36024143Sjoerg#endif
36124143Sjoerg
36224143Sjoerg	infoloadp = si->load_avg;
36324143Sjoerg	avenrunp = avenrun;
36424143Sjoerg	for (i = 0; i < 3; i++)
36524143Sjoerg	{
36624143Sjoerg#ifdef notyet
36724143Sjoerg	    *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
36824143Sjoerg#endif
36924143Sjoerg	    *infoloadp++ = loaddouble(*avenrunp++);
37024143Sjoerg	}
37124143Sjoerg    }
37224143Sjoerg
37324143Sjoerg    /* convert cp_time counts to percentages */
37424143Sjoerg    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
37524143Sjoerg
37624143Sjoerg    /* sum memory & swap statistics */
37724143Sjoerg    {
37824143Sjoerg	struct vmmeter sum;
37924143Sjoerg	static unsigned int swap_delay = 0;
38024143Sjoerg	static int swapavail = 0;
38124143Sjoerg	static int swapfree = 0;
38224143Sjoerg	static int bufspace = 0;
38324143Sjoerg
38424143Sjoerg        (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum),
38524143Sjoerg		   "_cnt");
38624143Sjoerg        (void) getkval(bufspace_offset, (int *)(&bufspace), sizeof(bufspace),
38724143Sjoerg		   "_bufspace");
38824143Sjoerg
38924143Sjoerg	/* convert memory stats to Kbytes */
39024143Sjoerg	memory_stats[0] = pagetok(sum.v_active_count);
39124143Sjoerg	memory_stats[1] = pagetok(sum.v_inactive_count);
39224143Sjoerg	memory_stats[2] = pagetok(sum.v_wire_count);
39324143Sjoerg	memory_stats[3] = pagetok(sum.v_cache_count);
39424143Sjoerg	memory_stats[4] = bufspace / 1024;
39524143Sjoerg	memory_stats[5] = pagetok(sum.v_free_count);
39624143Sjoerg	memory_stats[6] = -1;
39724143Sjoerg
39824143Sjoerg	/* first interval */
39924143Sjoerg        if (swappgsin < 0) {
40024143Sjoerg	    swap_stats[4] = 0;
40124143Sjoerg	    swap_stats[5] = 0;
40224143Sjoerg	}
40324143Sjoerg
40424143Sjoerg	/* compute differences between old and new swap statistic */
40524143Sjoerg	else {
40624143Sjoerg	    swap_stats[4] = pagetok(((sum.v_swappgsin - swappgsin)));
40724143Sjoerg	    swap_stats[5] = pagetok(((sum.v_swappgsout - swappgsout)));
40824143Sjoerg	}
40924143Sjoerg
41024143Sjoerg        swappgsin = sum.v_swappgsin;
41124143Sjoerg	swappgsout = sum.v_swappgsout;
41224143Sjoerg
41324143Sjoerg	/* call CPU heavy swapmode() only for changes */
41424143Sjoerg        if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) {
41524143Sjoerg	    swap_stats[3] = swapmode(&swapavail, &swapfree);
41624143Sjoerg	    swap_stats[0] = swapavail;
41724143Sjoerg	    swap_stats[1] = swapavail - swapfree;
41824143Sjoerg	    swap_stats[2] = swapfree;
41924143Sjoerg	}
42024143Sjoerg        swap_delay = 1;
42124143Sjoerg	swap_stats[6] = -1;
42224143Sjoerg    }
42324143Sjoerg
42424143Sjoerg    /* set arrays and strings */
42524143Sjoerg    si->cpustates = cpu_states;
42624143Sjoerg    si->memory = memory_stats;
42724143Sjoerg    si->swap = swap_stats;
42824143Sjoerg
42924143Sjoerg
43024143Sjoerg    if(lastpid > 0) {
43124143Sjoerg	si->last_pid = lastpid;
43224143Sjoerg    } else {
43324143Sjoerg	si->last_pid = -1;
43424143Sjoerg    }
43524143Sjoerg}
43624143Sjoerg
43724143Sjoergstatic struct handle handle;
43824143Sjoerg
43924143Sjoergcaddr_t get_process_info(si, sel, compare)
44024143Sjoerg
44124143Sjoergstruct system_info *si;
44224143Sjoergstruct process_select *sel;
44324143Sjoergint (*compare)();
44424143Sjoerg
44524143Sjoerg{
44624143Sjoerg    register int i;
44724143Sjoerg    register int total_procs;
44824143Sjoerg    register int active_procs;
44924143Sjoerg    register struct kinfo_proc **prefp;
45024143Sjoerg    register struct kinfo_proc *pp;
45124143Sjoerg
45224143Sjoerg    /* these are copied out of sel for speed */
45324143Sjoerg    int show_idle;
45424143Sjoerg    int show_system;
45524143Sjoerg    int show_uid;
45624143Sjoerg    int show_command;
45724143Sjoerg
45824143Sjoerg
45924143Sjoerg    pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
46024143Sjoerg    if (nproc > onproc)
46124143Sjoerg	pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
46224143Sjoerg		* (onproc = nproc));
46324143Sjoerg    if (pref == NULL || pbase == NULL) {
46424143Sjoerg	(void) fprintf(stderr, "top: Out of memory.\n");
46524143Sjoerg	quit(23);
46624143Sjoerg    }
46724143Sjoerg    /* get a pointer to the states summary array */
46824143Sjoerg    si->procstates = process_states;
46924143Sjoerg
47024143Sjoerg    /* set up flags which define what we are going to select */
47124143Sjoerg    show_idle = sel->idle;
47224143Sjoerg    show_system = sel->system;
47324143Sjoerg    show_uid = sel->uid != -1;
47424143Sjoerg    show_command = sel->command != NULL;
47524143Sjoerg
47624143Sjoerg    /* count up process states and get pointers to interesting procs */
47724143Sjoerg    total_procs = 0;
47824143Sjoerg    active_procs = 0;
47924143Sjoerg    memset((char *)process_states, 0, sizeof(process_states));
48024143Sjoerg    prefp = pref;
48124143Sjoerg    for (pp = pbase, i = 0; i < nproc; pp++, i++)
48224143Sjoerg    {
48324143Sjoerg	/*
48424143Sjoerg	 *  Place pointers to each valid proc structure in pref[].
48524143Sjoerg	 *  Process slots that are actually in use have a non-zero
48624143Sjoerg	 *  status field.  Processes with P_SYSTEM set are system
48724143Sjoerg	 *  processes---these get ignored unless show_sysprocs is set.
48824143Sjoerg	 */
48924143Sjoerg	if (PP(pp, p_stat) != 0 &&
49024143Sjoerg	    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
49124143Sjoerg	{
49224143Sjoerg	    total_procs++;
49324143Sjoerg	    process_states[(unsigned char) PP(pp, p_stat)]++;
49424143Sjoerg	    if ((PP(pp, p_stat) != SZOMB) &&
49524143Sjoerg		(show_idle || (PP(pp, p_pctcpu) != 0) ||
49624143Sjoerg		 (PP(pp, p_stat) == SRUN)) &&
49724143Sjoerg		(!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
49824143Sjoerg	    {
49924143Sjoerg		*prefp++ = pp;
50024143Sjoerg		active_procs++;
50124143Sjoerg	    }
50224143Sjoerg	}
50324143Sjoerg    }
50424143Sjoerg
50524143Sjoerg    /* if requested, sort the "interesting" processes */
50624143Sjoerg    if (compare != NULL)
50724143Sjoerg    {
50824143Sjoerg	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
50924143Sjoerg    }
51024143Sjoerg
51124143Sjoerg    /* remember active and total counts */
51224143Sjoerg    si->p_total = total_procs;
51324143Sjoerg    si->p_active = pref_len = active_procs;
51424143Sjoerg
51524143Sjoerg    /* pass back a handle */
51624143Sjoerg    handle.next_proc = pref;
51724143Sjoerg    handle.remaining = active_procs;
51824143Sjoerg    return((caddr_t)&handle);
51924143Sjoerg}
52024143Sjoerg
52124143Sjoergchar fmt[128];		/* static area where result is built */
52224143Sjoerg
52324143Sjoergchar *format_next_process(handle, get_userid)
52424143Sjoerg
52524143Sjoergcaddr_t handle;
52624143Sjoergchar *(*get_userid)();
52724143Sjoerg
52824143Sjoerg{
52924143Sjoerg    register struct kinfo_proc *pp;
53024143Sjoerg    register long cputime;
53124143Sjoerg    register double pct;
53224143Sjoerg    struct handle *hp;
53324143Sjoerg    char status[16];
53424143Sjoerg
53524143Sjoerg    /* find and remember the next proc structure */
53624143Sjoerg    hp = (struct handle *)handle;
53724143Sjoerg    pp = *(hp->next_proc++);
53824143Sjoerg    hp->remaining--;
53924143Sjoerg
54024143Sjoerg
54124143Sjoerg    /* get the process's user struct and set cputime */
54224143Sjoerg    if ((PP(pp, p_flag) & P_INMEM) == 0) {
54324143Sjoerg	/*
54424143Sjoerg	 * Print swapped processes as <pname>
54524143Sjoerg	 */
54624143Sjoerg	char *comm = PP(pp, p_comm);
54724143Sjoerg#define COMSIZ sizeof(PP(pp, p_comm))
54824143Sjoerg	char buf[COMSIZ];
54924143Sjoerg	(void) strncpy(buf, comm, COMSIZ);
55024143Sjoerg	comm[0] = '<';
55124143Sjoerg	(void) strncpy(&comm[1], buf, COMSIZ - 2);
55224143Sjoerg	comm[COMSIZ - 2] = '\0';
55324143Sjoerg	(void) strncat(comm, ">", COMSIZ - 1);
55424143Sjoerg	comm[COMSIZ - 1] = '\0';
55524143Sjoerg    }
55624143Sjoerg
55724143Sjoerg#if 0
55824143Sjoerg    /* This does not produce the correct results */
55924143Sjoerg    cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks);
56024143Sjoerg#endif
56124143Sjoerg    cputime = PP(pp, p_rtime).tv_sec;	/* This does not count interrupts */
56224143Sjoerg
56324143Sjoerg    /* calculate the base for cpu percentages */
56424143Sjoerg    pct = pctdouble(PP(pp, p_pctcpu));
56524143Sjoerg
56624143Sjoerg    /* generate "STATE" field */
56724143Sjoerg    switch (PP(pp, p_stat)) {
56824143Sjoerg	case SRUN:
56927340Speter	    if (smpmode && PP(pp, p_oncpu) >= 0)
57024143Sjoerg		sprintf(status, "CPU%d", PP(pp, p_oncpu));
57124143Sjoerg	    else
57224143Sjoerg		strcpy(status, "RUN");
57324143Sjoerg	    break;
57424143Sjoerg	case SSLEEP:
57524143Sjoerg	    if (PP(pp, p_wmesg) != NULL) {
57624143Sjoerg		sprintf(status, "%.6s", EP(pp, e_wmesg));
57724143Sjoerg		break;
57824143Sjoerg	    }
57924143Sjoerg	    /* fall through */
58024143Sjoerg	default:
58124143Sjoerg	    sprintf(status, "%.6s", state_abbrev[(unsigned char) PP(pp, p_stat)]);
58224143Sjoerg	    break;
58324143Sjoerg    }
58424143Sjoerg
58524143Sjoerg    /* format this entry */
58624143Sjoerg    sprintf(fmt,
58727340Speter	    smpmode ? smp_Proc_format : up_Proc_format,
58824143Sjoerg	    PP(pp, p_pid),
58924143Sjoerg	    (*get_userid)(EP(pp, e_pcred.p_ruid)),
59024143Sjoerg	    PP(pp, p_priority) - PZERO,
59124143Sjoerg	    PP(pp, p_nice) - NZERO,
59224143Sjoerg	    format_k2(pagetok(PROCSIZE(pp))),
59324143Sjoerg	    format_k2(pagetok(VP(pp, vm_rssize))),
59424143Sjoerg	    status,
59527340Speter	    smpmode ? PP(pp, p_lastcpu) : 0,
59624143Sjoerg	    format_time(cputime),
59724143Sjoerg	    10000.0 * weighted_cpu(pct, pp) / hz,
59824143Sjoerg	    10000.0 * pct / hz,
59924143Sjoerg	    printable(PP(pp, p_comm)));
60024143Sjoerg
60124143Sjoerg    /* return the result */
60224143Sjoerg    return(fmt);
60324143Sjoerg}
60424143Sjoerg
60524143Sjoerg
60624143Sjoerg/*
60724143Sjoerg * check_nlist(nlst) - checks the nlist to see if any symbols were not
60824143Sjoerg *		found.  For every symbol that was not found, a one-line
60924143Sjoerg *		message is printed to stderr.  The routine returns the
61024143Sjoerg *		number of symbols NOT found.
61124143Sjoerg */
61224143Sjoerg
61324143Sjoergstatic int check_nlist(nlst)
61424143Sjoerg
61524143Sjoergregister struct nlist *nlst;
61624143Sjoerg
61724143Sjoerg{
61824143Sjoerg    register int i;
61924143Sjoerg
62024143Sjoerg    /* check to see if we got ALL the symbols we requested */
62124143Sjoerg    /* this will write one line to stderr for every symbol not found */
62224143Sjoerg
62324143Sjoerg    i = 0;
62424143Sjoerg    while (nlst->n_name != NULL)
62524143Sjoerg    {
62624143Sjoerg	if (nlst->n_type == 0)
62724143Sjoerg	{
62824143Sjoerg	    /* this one wasn't found */
62924143Sjoerg	    (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
63024143Sjoerg			   nlst->n_name);
63124143Sjoerg	    i = 1;
63224143Sjoerg	}
63324143Sjoerg	nlst++;
63424143Sjoerg    }
63524143Sjoerg
63624143Sjoerg    return(i);
63724143Sjoerg}
63824143Sjoerg
63924143Sjoerg
64024143Sjoerg/*
64124143Sjoerg *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
64224143Sjoerg *	"offset" is the byte offset into the kernel for the desired value,
64324143Sjoerg *  	"ptr" points to a buffer into which the value is retrieved,
64424143Sjoerg *  	"size" is the size of the buffer (and the object to retrieve),
64524143Sjoerg *  	"refstr" is a reference string used when printing error meessages,
64624143Sjoerg *	    if "refstr" starts with a '!', then a failure on read will not
64724143Sjoerg *  	    be fatal (this may seem like a silly way to do things, but I
64824143Sjoerg *  	    really didn't want the overhead of another argument).
64924143Sjoerg *
65024143Sjoerg */
65124143Sjoerg
65224143Sjoergstatic int getkval(offset, ptr, size, refstr)
65324143Sjoerg
65424143Sjoergunsigned long offset;
65524143Sjoergint *ptr;
65624143Sjoergint size;
65724143Sjoergchar *refstr;
65824143Sjoerg
65924143Sjoerg{
66024143Sjoerg    if (kvm_read(kd, offset, (char *) ptr, size) != size)
66124143Sjoerg    {
66224143Sjoerg	if (*refstr == '!')
66324143Sjoerg	{
66424143Sjoerg	    return(0);
66524143Sjoerg	}
66624143Sjoerg	else
66724143Sjoerg	{
66824143Sjoerg	    fprintf(stderr, "top: kvm_read for %s: %s\n",
66924143Sjoerg		refstr, strerror(errno));
67024143Sjoerg	    quit(23);
67124143Sjoerg	}
67224143Sjoerg    }
67324143Sjoerg    return(1);
67424143Sjoerg}
67524143Sjoerg
67624143Sjoerg/* comparison routine for qsort */
67724143Sjoerg
67824143Sjoerg/*
67924143Sjoerg *  proc_compare - comparison function for "qsort"
68024143Sjoerg *	Compares the resource consumption of two processes using five
68124143Sjoerg *  	distinct keys.  The keys (in descending order of importance) are:
68224143Sjoerg *  	percent cpu, cpu ticks, state, resident set size, total virtual
68324143Sjoerg *  	memory usage.  The process states are ordered as follows (from least
68424143Sjoerg *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
68524143Sjoerg *  	array declaration below maps a process state index into a number
68624143Sjoerg *  	that reflects this ordering.
68724143Sjoerg */
68824143Sjoerg
68924143Sjoergstatic unsigned char sorted_state[] =
69024143Sjoerg{
69124143Sjoerg    0,	/* not used		*/
69224143Sjoerg    3,	/* sleep		*/
69324143Sjoerg    1,	/* ABANDONED (WAIT)	*/
69424143Sjoerg    6,	/* run			*/
69524143Sjoerg    5,	/* start		*/
69624143Sjoerg    2,	/* zombie		*/
69724143Sjoerg    4	/* stop			*/
69824143Sjoerg};
69924143Sjoerg
70024143Sjoergint
70124143Sjoergproc_compare(pp1, pp2)
70224143Sjoerg
70324143Sjoergstruct proc **pp1;
70424143Sjoergstruct proc **pp2;
70524143Sjoerg
70624143Sjoerg{
70724143Sjoerg    register struct kinfo_proc *p1;
70824143Sjoerg    register struct kinfo_proc *p2;
70924143Sjoerg    register int result;
71024143Sjoerg    register pctcpu lresult;
71124143Sjoerg
71224143Sjoerg    /* remove one level of indirection */
71324143Sjoerg    p1 = *(struct kinfo_proc **) pp1;
71424143Sjoerg    p2 = *(struct kinfo_proc **) pp2;
71524143Sjoerg
71624143Sjoerg    /* compare percent cpu (pctcpu) */
71724143Sjoerg    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
71824143Sjoerg    {
71924143Sjoerg	/* use cpticks to break the tie */
72024143Sjoerg	if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
72124143Sjoerg	{
72224143Sjoerg	    /* use process state to break the tie */
72324143Sjoerg	    if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
72424143Sjoerg			  sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
72524143Sjoerg	    {
72624143Sjoerg		/* use priority to break the tie */
72724143Sjoerg		if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
72824143Sjoerg		{
72924143Sjoerg		    /* use resident set size (rssize) to break the tie */
73024143Sjoerg		    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
73124143Sjoerg		    {
73224143Sjoerg			/* use total memory to break the tie */
73324143Sjoerg			result = PROCSIZE(p2) - PROCSIZE(p1);
73424143Sjoerg		    }
73524143Sjoerg		}
73624143Sjoerg	    }
73724143Sjoerg	}
73824143Sjoerg    }
73924143Sjoerg    else
74024143Sjoerg    {
74124143Sjoerg	result = lresult < 0 ? -1 : 1;
74224143Sjoerg    }
74324143Sjoerg
74424143Sjoerg    return(result);
74524143Sjoerg}
74624143Sjoerg
74724143Sjoerg
74824143Sjoerg/*
74924143Sjoerg * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
75024143Sjoerg *		the process does not exist.
75124143Sjoerg *		It is EXTREMLY IMPORTANT that this function work correctly.
75224143Sjoerg *		If top runs setuid root (as in SVR4), then this function
75324143Sjoerg *		is the only thing that stands in the way of a serious
75424143Sjoerg *		security problem.  It validates requests for the "kill"
75524143Sjoerg *		and "renice" commands.
75624143Sjoerg */
75724143Sjoerg
75824143Sjoergint proc_owner(pid)
75924143Sjoerg
76024143Sjoergint pid;
76124143Sjoerg
76224143Sjoerg{
76324143Sjoerg    register int cnt;
76424143Sjoerg    register struct kinfo_proc **prefp;
76524143Sjoerg    register struct kinfo_proc *pp;
76624143Sjoerg
76724143Sjoerg    prefp = pref;
76824143Sjoerg    cnt = pref_len;
76924143Sjoerg    while (--cnt >= 0)
77024143Sjoerg    {
77124143Sjoerg	pp = *prefp++;
77224143Sjoerg	if (PP(pp, p_pid) == (pid_t)pid)
77324143Sjoerg	{
77424143Sjoerg	    return((int)EP(pp, e_pcred.p_ruid));
77524143Sjoerg	}
77624143Sjoerg    }
77724143Sjoerg    return(-1);
77824143Sjoerg}
77924143Sjoerg
78024143Sjoerg
78124143Sjoerg/*
78224143Sjoerg * swapmode is based on a program called swapinfo written
78324143Sjoerg * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
78424143Sjoerg */
78524143Sjoerg
78624143Sjoerg#define	SVAR(var) __STRING(var)	/* to force expansion */
78724143Sjoerg#define	KGET(idx, var)							\
78824143Sjoerg	KGET1(idx, &var, sizeof(var), SVAR(var))
78924143Sjoerg#define	KGET1(idx, p, s, msg)						\
79024143Sjoerg	KGET2(nlst[idx].n_value, p, s, msg)
79124143Sjoerg#define	KGET2(addr, p, s, msg)						\
79224143Sjoerg	if (kvm_read(kd, (u_long)(addr), p, s) != s) {		        \
79324143Sjoerg		warnx("cannot read %s: %s", msg, kvm_geterr(kd));       \
79424143Sjoerg		return (0);                                             \
79524143Sjoerg       }
79624143Sjoerg#define	KGETRET(addr, p, s, msg)					\
79724143Sjoerg	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
79824143Sjoerg		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
79924143Sjoerg		return (0);						\
80024143Sjoerg	}
80124143Sjoerg
80224143Sjoerg
80324143Sjoergint
80424143Sjoergswapmode(retavail, retfree)
80524143Sjoerg	int *retavail;
80624143Sjoerg	int *retfree;
80724143Sjoerg{
80824143Sjoerg	char *header;
80924143Sjoerg	int hlen, nswap, nswdev, dmmax;
81024143Sjoerg	int i, div, avail, nfree, npfree, used;
81124143Sjoerg	struct swdevt *sw;
81224143Sjoerg	long blocksize, *perdev;
81324143Sjoerg	u_long ptr;
81424143Sjoerg	struct rlist head;
81524143Sjoerg#if __FreeBSD_version >= 220000
81624143Sjoerg	struct rlisthdr swaplist;
81724143Sjoerg#else
81824143Sjoerg	struct rlist *swaplist;
81924143Sjoerg#endif
82024143Sjoerg	struct rlist *swapptr;
82124143Sjoerg
82224143Sjoerg	/*
82324143Sjoerg	 * Counter for error messages. If we reach the limit,
82424143Sjoerg	 * stop reading information from swap devices and
82524143Sjoerg	 * return zero. This prevent endless 'bad address'
82624143Sjoerg	 * messages.
82724143Sjoerg	 */
82824143Sjoerg	static warning = 10;
82924143Sjoerg
83024143Sjoerg	if (warning <= 0) {
83124143Sjoerg	    /* a single warning */
83224143Sjoerg	    if (!warning) {
83324143Sjoerg		warning--;
83424143Sjoerg		fprintf(stderr,
83524143Sjoerg			"Too much errors, stop reading swap devices ...\n");
83624143Sjoerg		(void)sleep(3);
83724143Sjoerg	    }
83824143Sjoerg	    return(0);
83924143Sjoerg	}
84024143Sjoerg	warning--; /* decrease counter, see end of function */
84124143Sjoerg
84224143Sjoerg	KGET(VM_NSWAP, nswap);
84324143Sjoerg	if (!nswap) {
84424143Sjoerg		fprintf(stderr, "No swap space available\n");
84524143Sjoerg		return(0);
84624143Sjoerg	}
84724143Sjoerg
84824143Sjoerg	KGET(VM_NSWDEV, nswdev);
84924143Sjoerg	KGET(VM_DMMAX, dmmax);
85024143Sjoerg	KGET1(VM_SWAPLIST, &swaplist, sizeof(swaplist), "swaplist");
85124143Sjoerg	if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL ||
85224143Sjoerg	    (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL)
85324143Sjoerg		err(1, "malloc");
85424143Sjoerg	KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt");
85524143Sjoerg	KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt");
85624143Sjoerg
85724143Sjoerg	/* Count up swap space. */
85824143Sjoerg	nfree = 0;
85924143Sjoerg	memset(perdev, 0, nswdev * sizeof(*perdev));
86024143Sjoerg#if  __FreeBSD_version >= 220000
86124143Sjoerg	swapptr = swaplist.rlh_list;
86224143Sjoerg	while (swapptr) {
86324143Sjoerg#else
86424143Sjoerg	while (swaplist) {
86524143Sjoerg#endif
86624143Sjoerg		int	top, bottom, next_block;
86724143Sjoerg#if  __FreeBSD_version >= 220000
86824143Sjoerg		KGET2(swapptr, &head, sizeof(struct rlist), "swapptr");
86924143Sjoerg#else
87024143Sjoerg		KGET2(swaplist, &head, sizeof(struct rlist), "swaplist");
87124143Sjoerg#endif
87224143Sjoerg
87324143Sjoerg		top = head.rl_end;
87424143Sjoerg		bottom = head.rl_start;
87524143Sjoerg
87624143Sjoerg		nfree += top - bottom + 1;
87724143Sjoerg
87824143Sjoerg		/*
87924143Sjoerg		 * Swap space is split up among the configured disks.
88024143Sjoerg		 *
88124143Sjoerg		 * For interleaved swap devices, the first dmmax blocks
88224143Sjoerg		 * of swap space some from the first disk, the next dmmax
88324143Sjoerg		 * blocks from the next, and so on up to nswap blocks.
88424143Sjoerg		 *
88524143Sjoerg		 * The list of free space joins adjacent free blocks,
88624143Sjoerg		 * ignoring device boundries.  If we want to keep track
88724143Sjoerg		 * of this information per device, we'll just have to
88824143Sjoerg		 * extract it ourselves.
88924143Sjoerg		 */
89024143Sjoerg		while (top / dmmax != bottom / dmmax) {
89124143Sjoerg			next_block = ((bottom + dmmax) / dmmax);
89224143Sjoerg			perdev[(bottom / dmmax) % nswdev] +=
89324143Sjoerg				next_block * dmmax - bottom;
89424143Sjoerg			bottom = next_block * dmmax;
89524143Sjoerg		}
89624143Sjoerg		perdev[(bottom / dmmax) % nswdev] +=
89724143Sjoerg			top - bottom + 1;
89824143Sjoerg
89924143Sjoerg#if  __FreeBSD_version >= 220000
90024143Sjoerg		swapptr = head.rl_next;
90124143Sjoerg#else
90224143Sjoerg		swaplist = head.rl_next;
90324143Sjoerg#endif
90424143Sjoerg	}
90524143Sjoerg
90624143Sjoerg	header = getbsize(&hlen, &blocksize);
90724143Sjoerg	div = blocksize / 512;
90824143Sjoerg	avail = npfree = 0;
90924143Sjoerg	for (i = 0; i < nswdev; i++) {
91024143Sjoerg		int xsize, xfree;
91124143Sjoerg
91224143Sjoerg		/*
91324143Sjoerg		 * Don't report statistics for partitions which have not
91424143Sjoerg		 * yet been activated via swapon(8).
91524143Sjoerg		 */
91624143Sjoerg
91724143Sjoerg		xsize = sw[i].sw_nblks;
91824143Sjoerg		xfree = perdev[i];
91924143Sjoerg		used = xsize - xfree;
92024143Sjoerg		npfree++;
92124143Sjoerg		avail += xsize;
92224143Sjoerg	}
92324143Sjoerg
92424143Sjoerg	/*
92524143Sjoerg	 * If only one partition has been set up via swapon(8), we don't
92624143Sjoerg	 * need to bother with totals.
92724143Sjoerg	 */
92824143Sjoerg	*retavail = avail / 2;
92924143Sjoerg	*retfree = nfree / 2;
93024143Sjoerg	used = avail - nfree;
93124143Sjoerg	free(sw); free(perdev);
93224143Sjoerg
93324143Sjoerg	/* increase counter, no errors occurs */
93424143Sjoerg	warning++;
93524143Sjoerg
93624143Sjoerg	return  (int)(((double)used / (double)avail * 100.0) + 0.5);
93724143Sjoerg}
938