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