[BACK]Return to vmstat.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vmstat

File: [local] / src / usr.bin / vmstat / vmstat.c (download)

Revision 1.52, Fri May 11 06:46:40 2001 UTC (23 years, 1 month ago) by angelos
Branch: MAIN
Changes since 1.51: +101 -11 lines

Use sysctl(3) to retrieve nchstats, forkstat, nselcoll, and kmemstats
if the "default" files (running system) were specified for the ksyms
and memory dump. Eventually, it won't be necessary for vmstat to be
privileged.

/*	$NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $	*/
/*	$OpenBSD: vmstat.c,v 1.52 2001/05/11 06:46:40 angelos Exp $	*/

/*
 * Copyright (c) 1980, 1986, 1991, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1980, 1986, 1991, 1993\n\
	The Regents of the University of California.  All rights reserved.\n";
#endif /* not lint */

#ifndef lint
#if 0
static char sccsid[] = "@(#)vmstat.c	8.1 (Berkeley) 6/6/93";
#else
static char rcsid[] = "$NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $";
#endif
#endif /* not lint */

#define __POOL_EXPOSE

#include <sys/param.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/dkstat.h>
#include <sys/buf.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/device.h>
#include <sys/pool.h>
#include <vm/vm.h>
#include <time.h>
#include <nlist.h>
#include <kvm.h>
#include <err.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include <limits.h>
#include "dkstats.h"

#ifdef UVM
#include <uvm/uvm_extern.h>
#endif

struct nlist namelist[] = {
#define	X_CPTIME	0
	{ "_cp_time" },
#if defined(UVM)
#define X_UVMEXP	1
	{ "_uvmexp" },
#else
#define X_SUM		1
	{ "_cnt" },
#endif
#define	X_BOOTTIME	2
	{ "_boottime" },
#define X_HZ		3
	{ "_hz" },
#define X_STATHZ	4
	{ "_stathz" },
#define X_NCHSTATS	5
	{ "_nchstats" },
#define	X_INTRNAMES	6
	{ "_intrnames" },
#define	X_EINTRNAMES	7
	{ "_eintrnames" },
#define	X_INTRCNT	8
	{ "_intrcnt" },
#define	X_EINTRCNT	9
	{ "_eintrcnt" },
#define	X_KMEMSTAT	10
	{ "_kmemstats" },
#define	X_KMEMBUCKETS	11
	{ "_bucket" },
#define X_ALLEVENTS	12
	{ "_allevents" },
#define	X_FORKSTAT	13
	{ "_forkstat" },
#define X_POOLHEAD	14
	{ "_pool_head" },
#define X_NSELCOLL	15
	{ "_nselcoll" },
#define X_END		16
#if defined(__pc532__)
#define	X_IVT		(X_END)
	{ "_ivt" },
#endif
#if defined(__i386__)
#define	X_INTRHAND	(X_END)
	{ "_intrhand" },
#define	X_INTRSTRAY	(X_END+1)
	{ "_intrstray" },
#endif
	{ "" },
};

/* Objects defined in dkstats.c */
extern struct _disk	cur;
extern char	**dr_name;
extern int	*dk_select, dk_ndrive;

#ifdef UVM
struct	uvmexp uvmexp, ouvmexp;
#else
struct	vmmeter sum, osum;
#endif
int		ndrives;

int	winlines = 20;

kvm_t *kd;

#define	FORKSTAT	0x01
#define	INTRSTAT	0x02
#define	MEMSTAT		0x04
#define	SUMSTAT		0x08
#define	TIMESTAT	0x10
#define	VMSTAT		0x20

void	cpustats __P((void));
void	dkstats __P((void));
void	dointr __P((void));
void	domem __P((void));
void	dopool __P((void));
void	dosum __P((void));
void	dovmstat __P((u_int, int));
void	kread __P((int, void *, size_t));
void	usage __P((void));
void	dotimes __P((void));
void	doforkst __P((void));
void	printhdr __P((void));

char	**choosedrives __P((char **));

/* Namelist and memory file names. */
char	*nlistf, *memf;

extern char *__progname;

int
main(argc, argv)
	register int argc;
	register char **argv;
{
	extern int optind;
	extern char *optarg;
	register int c, todo;
	u_int interval;
	int reps;
	char errbuf[_POSIX2_LINE_MAX];

	memf = nlistf = NULL;
	interval = reps = todo = 0;
	while ((c = getopt(argc, argv, "c:fiM:mN:stw:")) != -1) {
		switch (c) {
		case 'c':
			reps = atoi(optarg);
			break;
		case 'f':
			todo |= FORKSTAT;
			break;
		case 'i':
			todo |= INTRSTAT;
			break;
		case 'M':
			memf = optarg;
			break;
		case 'm':
			todo |= MEMSTAT;
			break;
		case 'N':
			nlistf = optarg;
			break;
		case 's':
			todo |= SUMSTAT;
			break;
		case 't':
			todo |= TIMESTAT;
			break;
		case 'w':
			interval = atoi(optarg);
			break;
		case '?':
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (todo == 0)
		todo = VMSTAT;

	/*
	 * Discard setgid privileges if not the running kernel so that bad
	 * guys can't print interesting stuff from kernel memory.
	 */
	if (nlistf != NULL || memf != NULL) {
		setegid(getgid());
		setgid(getgid());
	}

	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
	if (kd == 0)
		errx(1, "kvm_openfiles: %s", errbuf);

	if ((c = kvm_nlist(kd, namelist)) != 0) {

		setegid(getgid());
		setgid(getgid());

		if (c > 0) {
			(void)fprintf(stderr,
			    "%s: undefined symbols:", __progname);
			for (c = 0;
			    c < sizeof(namelist)/sizeof(namelist[0]); c++)
				if (namelist[c].n_type == 0)
					fprintf(stderr, " %s",
					    namelist[c].n_name);
			(void)fputc('\n', stderr);
			exit(1);
		} else
			errx(1, "kvm_nlist: %s", kvm_geterr(kd));
	}

	if (todo & VMSTAT) {
		struct winsize winsize;

		dkinit(0);	/* Initialize disk stats, no disks selected. */
		argv = choosedrives(argv);	/* Select disks. */
		winsize.ws_row = 0;
		(void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
		if (winsize.ws_row > 0)
			winlines = winsize.ws_row;

	}

	setegid(getgid());
	setgid(getgid());

#define	BACKWARD_COMPATIBILITY
#ifdef	BACKWARD_COMPATIBILITY
	if (*argv) {
		interval = atoi(*argv);
		if (*++argv)
			reps = atoi(*argv);
	}
#endif

	if (interval) {
		if (!reps)
			reps = -1;
	} else if (reps)
		interval = 1;

	if (todo & FORKSTAT)
		doforkst();
	if (todo & MEMSTAT) {
		domem();
		dopool();
	}
	if (todo & SUMSTAT)
		dosum();
	if (todo & TIMESTAT)
		dotimes();
	if (todo & INTRSTAT)
		dointr();
	if (todo & VMSTAT)
		dovmstat(interval, reps);
	exit(0);
}

char **
choosedrives(argv)
	char **argv;
{
	register int i;

	/*
	 * Choose drives to be displayed.  Priority goes to (in order) drives
	 * supplied as arguments, default drives.  If everything isn't filled
	 * in and there are drives not taken care of, display the first few
	 * that fit.
	 */
#define BACKWARD_COMPATIBILITY
	for (ndrives = 0; *argv; ++argv) {
#ifdef	BACKWARD_COMPATIBILITY
		if (isdigit(**argv))
			break;
#endif
		for (i = 0; i < dk_ndrive; i++) {
			if (strcmp(dr_name[i], *argv))
				continue;
			dk_select[i] = 1;
			++ndrives;
			break;
		}
	}
	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
		if (dk_select[i])
			continue;
		dk_select[i] = 1;
		++ndrives;
	}
	return(argv);
}

time_t
getuptime()
{
	static time_t now;
	static struct timeval boottime;
	time_t uptime;
	int mib[2];
	size_t size;

	if (boottime.tv_sec == 0) {
		if (nlist == NULL && memf == NULL) {
			kread(X_BOOTTIME, &boottime, sizeof(boottime));
		} else {
			size = sizeof(boottime);
			mib[0] = CTL_KERN;
			mib[1] = KERN_BOOTTIME;
			if (sysctl(mib, 2, &boottime, &size, NULL, 0) < 0) {
				printf("Can't get kerninfo: %s\n",
				       strerror(errno));
				bzero(&boottime, sizeof(boottime));
			}
		}
	}
	(void)time(&now);
	uptime = now - boottime.tv_sec;
	if (uptime <= 0 || uptime > 60*60*24*365*10)
		errx(1, "time makes no sense; namelist must be wrong");

	return(uptime);
}

int	hz, hdrcnt;

void
dovmstat(interval, reps)
	u_int interval;
	int reps;
{
	struct vmtotal total;
	time_t uptime, halfuptime;
	void needhdr();
	int mib[2];
	struct clockinfo clkinfo;
	size_t size;

	uptime = getuptime();
	halfuptime = uptime / 2;
	(void)signal(SIGCONT, needhdr);

	mib[0] = CTL_KERN;
	mib[1] = KERN_CLOCKRATE;
	size = sizeof(clkinfo);
	if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
		printf("Can't get kerninfo: %s\n", strerror(errno));
		return;
	}
	hz = clkinfo.stathz;

	for (hdrcnt = 1;;) {
		if (!--hdrcnt)
			printhdr();
		/* Read new disk statistics */
		dkreadstats();
#ifdef UVM
		if (nlist == NULL && memf == NULL) {
			kread(X_UVMEXP, &uvmexp, sizeof(uvmexp));
		} else {
			size = sizeof(uvmexp);
			mib[0] = CTL_VM;
			mib[1] = VM_UVMEXP;
			if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
				printf("Can't get kerninfo: %s\n",
				       strerror(errno));
				bzero(&uvmexp, sizeof(uvmexp));
			}
		}
#else
		kread(X_SUM, &sum, sizeof(sum));
#endif
		size = sizeof(total);
		mib[0] = CTL_VM;
		mib[1] = VM_METER;
		if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
			printf("Can't get kerninfo: %s\n", strerror(errno));
			bzero(&total, sizeof(total));
		}
		(void)printf("%2u%2u%2u",
		    total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw);
#define	rate(x)	(((x) + halfuptime) / uptime)	/* round */
#ifdef UVM
#define pgtok(a) ((a) * ((int)uvmexp.pagesize >> 10))
#else
#define pgtok(a) ((a) * ((int)sum.v_page_size >> 10))
#endif
		(void)printf("%7u%7u ",
		    pgtok(total.t_avm), pgtok(total.t_free));
#ifdef UVM
		(void)printf("%4u ", rate(uvmexp.faults - ouvmexp.faults));
		(void)printf("%3u ", rate(uvmexp.pdreact - ouvmexp.pdreact));
		(void)printf("%3u ", rate(uvmexp.pageins - ouvmexp.pageins));
		(void)printf("%3u %3u ",
		    rate(uvmexp.pdpageouts - ouvmexp.pdpageouts), 0);
		(void)printf("%3u ", rate(uvmexp.pdscans - ouvmexp.pdscans));
		dkstats();
		(void)printf("%4u %4u %3u ",
		    rate(uvmexp.intrs - ouvmexp.intrs),
		    rate(uvmexp.syscalls - ouvmexp.syscalls),
		    rate(uvmexp.swtch - ouvmexp.swtch));
#else
		(void)printf("%4u ", rate(sum.v_faults - osum.v_faults));
		(void)printf("%3u ",
		    rate(sum.v_reactivated - osum.v_reactivated));
		(void)printf("%3u ", rate(sum.v_pageins - osum.v_pageins));
		(void)printf("%3u %3u ",
		    rate(sum.v_pageouts - osum.v_pageouts), 0);
		(void)printf("%3u ", rate(sum.v_scan - osum.v_scan));
		dkstats();
		(void)printf("%4u %4u %3u ",
		    rate(sum.v_intr - osum.v_intr),
		    rate(sum.v_syscall - osum.v_syscall),
		    rate(sum.v_swtch - osum.v_swtch));
#endif
		cpustats();
		(void)printf("\n");
		(void)fflush(stdout);
		if (reps >= 0 && --reps <= 0)
			break;
#ifdef UVM
		ouvmexp = uvmexp;
#else
		osum = sum;
#endif
		uptime = interval;
		/*
		 * We round upward to avoid losing low-frequency events
		 * (i.e., >= 1 per interval but < 1 per second).
		 */
		halfuptime = uptime == 1 ? 0 : (uptime + 1) / 2;
		(void)sleep(interval);
	}
}

void
printhdr()
{
	register int i;

	(void)printf(" procs   memory       page%*s", 20, "");
	if (ndrives > 0)
		(void)printf("%s %*sfaults   cpu\n",
		   ((ndrives > 1) ? "disks" : "disk"),
		   ((ndrives > 1) ? ndrives * 3 - 4 : 0), "");
	else
		(void)printf("%*s  faults   cpu\n",
		   ndrives * 3, "");

	(void)printf(" r b w    avm    fre  flt  re  pi  po  fr  sr ");
	for (i = 0; i < dk_ndrive; i++)
		if (dk_select[i])
			(void)printf("%c%c ", dr_name[i][0],
			    dr_name[i][strlen(dr_name[i]) - 1]);
	(void)printf("  in   sy  cs us sy id\n");
	hdrcnt = winlines - 2;
}

/*
 * Force a header to be prepended to the next output.
 */
void
needhdr()
{

	hdrcnt = 1;
}

void
dotimes()
{
	u_int pgintime, rectime;
	int mib[2];
	size_t size;

	pgintime = 0;
	rectime = 0;
#ifdef UVM
	if (nlist == NULL && memf == NULL) {
		kread(X_UVMEXP, &uvmexp, sizeof(uvmexp));
	} else {
		size = sizeof(uvmexp);
		mib[0] = CTL_VM;
		mib[1] = VM_UVMEXP;
		if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
			printf("Can't get kerninfo: %s\n", strerror(errno));
			bzero(&uvmexp, sizeof(uvmexp));
		}
	}

	(void)printf("%u reactivates, %u total time (usec)\n",
	    uvmexp.pdreact, rectime);
	(void)printf("average: %u usec / reclaim\n", rectime / uvmexp.pdreact);
	(void)printf("\n");
	(void)printf("%u page ins, %u total time (msec)\n",
	    uvmexp.pageins, pgintime / 10);
	(void)printf("average: %8.1f msec / page in\n",
	    pgintime / (uvmexp.pageins * 10.0));
#else
	kread(X_SUM, &sum, sizeof(sum));
	(void)printf("%u reactivates, %u total time (usec)\n",
	    sum.v_reactivated, rectime);
	(void)printf("average: %u usec / reclaim\n", rectime / sum.v_reactivated);
	(void)printf("\n");
	(void)printf("%u page ins, %u total time (msec)\n",
	    sum.v_pageins, pgintime / 10);
	(void)printf("average: %8.1f msec / page in\n",
	    pgintime / (sum.v_pageins * 10.0));
#endif
}

int
pct(top, bot)
	long top, bot;
{
	long ans;

	if (bot == 0)
		return(0);
	ans = (quad_t)top * 100 / bot;
	return (ans);
}

#define	PCT(top, bot) pct((long)(top), (long)(bot))

void
dosum()
{
	struct nchstats nchstats;
	long nchtotal;
	size_t size;
	int mib[2], nselcoll;

#ifdef UVM
	if (nlist == NULL && memf == NULL) {
		kread(X_UVMEXP, &nchstats, sizeof(uvmexp));
	} else {
		size = sizeof(uvmexp);
		mib[0] = CTL_VM;
		mib[1] = VM_UVMEXP;
		if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
			printf("Can't get kerninfo: %s\n", strerror(errno));
			bzero(&uvmexp, sizeof(uvmexp));
		}
	}

	/* vm_page constants */
	(void)printf("%11u bytes per page\n", uvmexp.pagesize);

	/* vm_page counters */
	(void)printf("%11u pages managed\n", uvmexp.npages);
	(void)printf("%11u pages free\n", uvmexp.free);
	(void)printf("%11u pages active\n", uvmexp.active);
	(void)printf("%11u pages inactive\n", uvmexp.inactive);
	(void)printf("%11u pages being paged out\n", uvmexp.paging);
	(void)printf("%11u pages wired\n", uvmexp.wired);
	(void)printf("%11u pages reserved for pagedaemon\n",
		     uvmexp.reserve_pagedaemon);
	(void)printf("%11u pages reserved for kernel\n",
		     uvmexp.reserve_kernel);

	/* swap */
	(void)printf("%11u swap pages\n", uvmexp.swpages);
	(void)printf("%11u swap pages in use\n", uvmexp.swpginuse);
	(void)printf("%11u total anon's in system\n", uvmexp.nanon);
	(void)printf("%11u free anon's\n", uvmexp.nfreeanon);

	/* stat counters */
	(void)printf("%11u page faults\n", uvmexp.faults);
	(void)printf("%11u traps\n", uvmexp.traps);
	(void)printf("%11u interrupts\n", uvmexp.intrs);
	(void)printf("%11u cpu context switches\n", uvmexp.swtch);
	(void)printf("%11u software interrupts\n", uvmexp.softs);
	(void)printf("%11u syscalls\n", uvmexp.syscalls);
	(void)printf("%11u pagein operations\n", uvmexp.pageins);
	(void)printf("%11u swap ins\n", uvmexp.swapins);
	(void)printf("%11u swap outs\n", uvmexp.swapouts);
	(void)printf("%11u forks\n", uvmexp.forks);
	(void)printf("%11u forks where vmspace is shared\n",
		     uvmexp.forks_sharevm);

	/* daemon counters */
	(void)printf("%11u number of times the pagedeamon woke up\n",
		     uvmexp.pdwoke);
	(void)printf("%11u revolutions of the clock hand\n", uvmexp.pdrevs);
	(void)printf("%11u pages freed by pagedaemon\n", uvmexp.pdfreed);
	(void)printf("%11u pages scanned by pagedaemon\n", uvmexp.pdscans);
	(void)printf("%11u pages reactivated by pagedaemon\n", uvmexp.pdreact);
	(void)printf("%11u busy pages found by pagedaemon\n", uvmexp.pdbusy);
#else
	kread(X_SUM, &sum, sizeof(sum));
	(void)printf("%11u cpu context switches\n", sum.v_swtch);
	(void)printf("%11u device interrupts\n", sum.v_intr);
	(void)printf("%11u software interrupts\n", sum.v_soft);
	(void)printf("%11u traps\n", sum.v_trap);
	(void)printf("%11u system calls\n", sum.v_syscall);
	(void)printf("%11u total faults taken\n", sum.v_faults);
	(void)printf("%11u swap ins\n", sum.v_swpin);
	(void)printf("%11u swap outs\n", sum.v_swpout);
	(void)printf("%11u pages swapped in\n", sum.v_pswpin);
	(void)printf("%11u pages swapped out\n", sum.v_pswpout);
	(void)printf("%11u page ins\n", sum.v_pageins);
	(void)printf("%11u page outs\n", sum.v_pageouts);
	(void)printf("%11u pages paged in\n", sum.v_pgpgin);
	(void)printf("%11u pages paged out\n", sum.v_pgpgout);
	(void)printf("%11u pages reactivated\n", sum.v_reactivated);
	(void)printf("%11u intransit blocking page faults\n", sum.v_intrans);
	(void)printf("%11u zero fill pages created\n", sum.v_nzfod);
	(void)printf("%11u zero fill page faults\n", sum.v_zfod);
	(void)printf("%11u pages examined by the clock daemon\n", sum.v_scan);
	(void)printf("%11u revolutions of the clock hand\n", sum.v_rev);
	(void)printf("%11u VM object cache lookups\n", sum.v_lookups);
	(void)printf("%11u VM object hits\n", sum.v_hits);
	(void)printf("%11u total VM faults taken\n", sum.v_vm_faults);
	(void)printf("%11u copy-on-write faults\n", sum.v_cow_faults);
	(void)printf("%11u pages freed by daemon\n", sum.v_dfree);
	(void)printf("%11u pages freed by exiting processes\n", sum.v_pfree);
	(void)printf("%11u pages free\n", sum.v_free_count);
	(void)printf("%11u pages wired down\n", sum.v_wire_count);
	(void)printf("%11u pages active\n", sum.v_active_count);
	(void)printf("%11u pages inactive\n", sum.v_inactive_count);
	(void)printf("%11u bytes per page\n", sum.v_page_size);
#endif

	if (nlist == NULL && memf == NULL) {
		kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
	} else {
		size = sizeof(nchstats);
		mib[0] = CTL_KERN;
		mib[1] = KERN_NCHSTATS;
		if (sysctl(mib, 2, &nchstats, &size, NULL, 0) < 0) {
		    	printf("Can't get kerninfo: %s\n", strerror(errno));
			bzero(&nchstats, sizeof(nchstats));
		}
	}

	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
	    nchstats.ncs_badhits + nchstats.ncs_falsehits +
	    nchstats.ncs_miss + nchstats.ncs_long;
	(void)printf("%11ld total name lookups\n", nchtotal);
	(void)printf("%11s cache hits (%d%% pos + %d%% neg) system %d%% "
	    "per-directory\n",
	    "", PCT(nchstats.ncs_goodhits, nchtotal),
	    PCT(nchstats.ncs_neghits, nchtotal),
	    PCT(nchstats.ncs_pass2, nchtotal));
	(void)printf("%11s deletions %d%%, falsehits %d%%, toolong %d%%\n", "",
	    PCT(nchstats.ncs_badhits, nchtotal),
	    PCT(nchstats.ncs_falsehits, nchtotal),
	    PCT(nchstats.ncs_long, nchtotal));

	if (nlist == NULL && memf == NULL) {
		kread(X_NSELCOLL, &nselcoll, sizeof(nselcoll));
	} else {
		size = sizeof(nselcoll);
		mib[0] = CTL_KERN;
		mib[1] = KERN_NSELCOLL;
		if (sysctl(mib, 2, &nselcoll, &size, NULL, 0) < 0) {
		    	printf("Can't get kerninfo: %s\n", strerror(errno));
			nselcoll = 0;
		}
	}
	(void)printf("%11d select collisions\n", nselcoll);
}

void
doforkst()
{
	struct forkstat fks;
	size_t size;
	int mib[2];

	if (nlist == NULL && memf == NULL) {
		kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
	} else {
		size = sizeof(struct forkstat);
		mib[0] = CTL_KERN;
		mib[1] = KERN_FORKSTAT;
		if (sysctl(mib, 2, &fks, &size, NULL, 0) < 0) {
		    	printf("Can't get kerninfo: %s\n", strerror(errno));
			bzero(&fks, sizeof(struct forkstat));
		}
	}

	(void)printf("%d forks, %d pages, average %.2f\n",
	    fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
	(void)printf("%d vforks, %d pages, average %.2f\n",
	    fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / (fks.cntvfork ? fks.cntvfork : 1));
	(void)printf("%d rforks, %d pages, average %.2f\n",
	    fks.cntrfork, fks.sizrfork, (double)fks.sizrfork / (fks.cntrfork ? fks.cntrfork : 1));
	(void)printf("%d kthread creations, %d pages, average %.2f\n",
	    fks.cntkthread, fks.sizkthread, (double)fks.sizkthread / (fks.cntkthread ? fks.cntkthread : 1));
}

void
dkstats()
{
	register int dn, state;
	double etime;

	/* Calculate disk stat deltas. */
	dkswap();
	etime = 0;
	for (state = 0; state < CPUSTATES; ++state) {
		etime += cur.cp_time[state];
	}
	if (etime == 0)
		etime = 1;
	etime /= hz;
	for (dn = 0; dn < dk_ndrive; ++dn) {
		if (!dk_select[dn])
			continue;
		(void)printf("%2.0f ", cur.dk_xfer[dn] / etime);
	}
}

void
cpustats()
{
	register int state;
	double pct, total;

	total = 0;
	for (state = 0; state < CPUSTATES; ++state)
		total += cur.cp_time[state];
	if (total)
		pct = 100 / total;
	else
		pct = 0;
	(void)printf("%2.0f ", (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * pct);
	(void)printf("%2.0f ", (cur.cp_time[CP_SYS] + cur.cp_time[CP_INTR]) * pct);
	(void)printf("%2.0f", cur.cp_time[CP_IDLE] * pct);
}

#if defined(__pc532__)
/* To get struct iv ...*/
#define _KERNEL
#include <machine/psl.h>
#undef _KERNEL
void
dointr()
{
	register long i, j, inttotal;
	time_t uptime;
	static char iname[64];
	struct iv ivt[32], *ivp = ivt;

	iname[63] = '\0';
	uptime = getuptime();
	kread(X_IVT, ivp, sizeof(ivt));

	for (i = 0; i < 2; i++) {
		(void)printf("%sware interrupts:\n", i ? "\nsoft" : "hard");
		(void)printf("interrupt         total     rate\n");
		inttotal = 0;
		for (j = 0; j < 16; j++, ivp++) {
			if (ivp->iv_vec && ivp->iv_use && ivp->iv_cnt) {
				if (kvm_read(kd, (u_long)ivp->iv_use, iname, 63) != 63) {
					errx(1, "iv_use: %s", kvm_geterr(kd));
				}
				(void)printf("%-12s %10ld %8ld\n", iname,
				    ivp->iv_cnt, ivp->iv_cnt / uptime);
				inttotal += ivp->iv_cnt;
			}
		}
		(void)printf("Total        %10ld %8ld\n",
		    inttotal, inttotal / uptime);
	}
}
#elif defined(__i386__)
/* To get struct intrhand */
#define _KERNEL
#include <machine/psl.h>
#undef _KERNEL
void
dointr()
{
	struct intrhand *intrhand[16], *ihp, ih;
	u_long inttotal;
	time_t uptime;
	u_long intrstray[16];
	char iname[17], fname[31];
	int i;

	iname[16] = '\0';
	uptime = getuptime();
	kread(X_INTRHAND, intrhand, sizeof(intrhand));
	kread(X_INTRSTRAY, intrstray, sizeof(intrstray));

	(void)printf("interrupt             total     rate\n");
	inttotal = 0;
	for (i = 0; i < 16; i++) {
		ihp = intrhand[i];
		while (ihp) {
			if (kvm_read(kd, (u_long)ihp, &ih, sizeof(ih)) != sizeof(ih))
				errx(1, "vmstat: ih: %s", kvm_geterr(kd));
			if (kvm_read(kd, (u_long)ih.ih_what, iname, 16) != 16)
				errx(1, "vmstat: ih_what: %s", kvm_geterr(kd));
			snprintf(fname, sizeof fname, "irq%d/%s", i, iname);
			printf("%-16.16s %10lu %8lu\n", fname, ih.ih_count,
			    ih.ih_count / uptime);
			inttotal += ih.ih_count;
			ihp = ih.ih_next;
		}
	}
	for (i = 0; i < 16; i++)
		if (intrstray[i]) {
			printf("Stray irq %-2d     %10lu %8lu\n",
			       i, intrstray[i], intrstray[i] / uptime);
			inttotal += intrstray[i];
		}
	printf("Total            %10lu %8lu\n", inttotal, inttotal / uptime);
}
#else
void
dointr()
{
	register long *intrcnt, inttotal;
	time_t uptime;
	register int nintr, inamlen;
	register char *intrname;
	struct evcntlist allevents;
	struct evcnt evcnt, *evptr;
	struct device dev;

	uptime = getuptime();
	nintr = namelist[X_EINTRCNT].n_value - namelist[X_INTRCNT].n_value;
	inamlen =
	    namelist[X_EINTRNAMES].n_value - namelist[X_INTRNAMES].n_value;
	intrcnt = malloc((size_t)nintr);
	intrname = malloc((size_t)inamlen);
	if (intrcnt == NULL || intrname == NULL)
		err(1, "malloc");
	kread(X_INTRCNT, intrcnt, (size_t)nintr);
	kread(X_INTRNAMES, intrname, (size_t)inamlen);
	(void)printf("interrupt             total     rate\n");
	inttotal = 0;
	nintr /= sizeof(long);
	while (--nintr >= 0) {
		if (*intrcnt)
			(void)printf("%-14s %12ld %8ld\n", intrname,
			    *intrcnt, *intrcnt / uptime);
		intrname += strlen(intrname) + 1;
		inttotal += *intrcnt++;
	}
	kread(X_ALLEVENTS, &allevents, sizeof allevents);
	evptr = allevents.tqh_first;
	while (evptr) {
		if (kvm_read(kd, (long)evptr, (void *)&evcnt,
		    sizeof evcnt) != sizeof evcnt)
			errx(1, "event chain trashed: %s", kvm_geterr(kd));
		if (strcmp(evcnt.ev_name, "intr") == 0) {
			if (kvm_read(kd, (long)evcnt.ev_dev, (void *)&dev,
			    sizeof dev) != sizeof dev)
				errx(1, "event chain trashed: %s", kvm_geterr(kd));
			if (evcnt.ev_count)
				(void)printf("%-14s %12d %8ld\n", dev.dv_xname,
				    evcnt.ev_count, (long)(evcnt.ev_count / uptime));
			inttotal += evcnt.ev_count++;
		}
		evptr = evcnt.ev_list.tqe_next;
	}
	(void)printf("Total          %12ld %8ld\n", inttotal, inttotal / uptime);
}
#endif

/*
 * These names are defined in <sys/malloc.h>.
 */
char *kmemnames[] = INITKMEMNAMES;

void
domem()
{
	register struct kmembuckets *kp;
	register struct kmemstats *ks;
	register int i, j;
	int len, size, first;
	u_long totuse = 0, totfree = 0;
	quad_t totreq = 0;
	char *name;
	struct kmemstats kmemstats[M_LAST];
	struct kmembuckets buckets[MINBUCKET + 16];
	int mib[4];
	size_t siz;
	char buf[BUFSIZ], *bufp, *ap;

	if (memf == NULL && nlistf == NULL) {
	        mib[0] = CTL_KERN;
		mib[1] = KERN_MALLOCSTATS;
		mib[2] = KERN_MALLOC_BUCKETS;
		siz = sizeof(buf);
		if (sysctl(mib, 3, buf, &siz, NULL, 0) < 0) {
		        printf("Could not acquire information on kernel memory bucket sizes.\n");
			return;
		}

		bufp = buf;
		mib[2] = KERN_MALLOC_BUCKET;
		siz = sizeof(struct kmembuckets);
		i = 0;
		while ((ap = strsep(&bufp, ",")) != NULL) {
		        mib[3] = atoi(ap);

			if (sysctl(mib, 4, &buckets[MINBUCKET + i], &siz,
				   NULL, 0) < 0) {
			        printf("Failed to read statistics for bucket %d.\n", mib[3]);
				return;
			}
			i++;
		}
	} else {
	        kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
	}

	for (first = 1, i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16;
	     i++, kp++) {
		if (kp->kb_calls == 0)
			continue;
		if (first) {
			(void)printf("Memory statistics by bucket size\n");
			(void)printf(
		"    Size   In Use   Free           Requests  HighWater  Couldfree\n");
			first = 0;
		}
		size = 1 << i;
		(void)printf("%8d %8qu %6qu %18qu %7qu %10qu\n", size,
			kp->kb_total - kp->kb_totalfree,
			kp->kb_totalfree, kp->kb_calls,
			kp->kb_highwat, kp->kb_couldfree);
		totfree += size * kp->kb_totalfree;
	}

	/*
	 * If kmem statistics are not being gathered by the kernel,
	 * first will still be 1.
	 */
	if (first) {
		printf(
		    "Kmem statistics are not being gathered by the kernel.\n");
		return;
	}

	if (memf == NULL && nlistf == NULL) {
		bzero(kmemstats, sizeof(kmemstats));
		for (i = 0; i < M_LAST; i++) {
	        	mib[0] = CTL_KERN;
			mib[1] = KERN_MALLOCSTATS;
			mib[2] = KERN_MALLOC_KMEMSTATS;
			mib[3] = i;
			siz = sizeof(struct kmemstats);

			/* 
			 * Skip errors -- these are presumed to be unallocated
			 * entries.
			 */
			if (sysctl(mib, 4, &kmemstats[i], &siz, NULL, 0) < 0)
				continue;
		}
	} else {
		kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats));
	}

	(void)printf("\nMemory usage type by bucket size\n");
	(void)printf("    Size  Type(s)\n");
	kp = &buckets[MINBUCKET];
	for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) {
		if (kp->kb_calls == 0)
			continue;
		first = 1;
		len = 8;
		for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
			if (ks->ks_calls == 0)
				continue;
			if ((ks->ks_size & j) == 0)
				continue;
			name = kmemnames[i] ? kmemnames[i] : "undefined";
			len += 2 + strlen(name);
			if (first)
				printf("%8d  %s", j, name);
			else
				printf(",");
			if (len >= 80) {
				printf("\n\t ");
				len = 10 + strlen(name);
			}
			if (!first)
				printf(" %s", name);
			first = 0;
		}
		printf("\n");
	}

	(void)printf(
	   "\nMemory statistics by type                           Type  Kern\n");
	(void)printf(
"          Type InUse MemUse HighUse  Limit Requests Limit Limit Size(s)\n");
	for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
		if (ks->ks_calls == 0)
			continue;
		(void)printf("%14s%6ld%6ldK%7ldK%6ldK%9ld%5u%6u",
		    kmemnames[i] ? kmemnames[i] : "undefined",
		    ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
		    (ks->ks_maxused + 1023) / 1024,
		    (ks->ks_limit + 1023) / 1024, ks->ks_calls,
		    ks->ks_limblocks, ks->ks_mapblocks);
		first = 1;
		for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
			if ((ks->ks_size & j) == 0)
				continue;
			if (first)
				printf("  %d", j);
			else
				printf(",%d", j);
			first = 0;
		}
		printf("\n");
		totuse += ks->ks_memuse;
		totreq += ks->ks_calls;
	}
	(void)printf("\nMemory Totals:  In Use    Free    Requests\n");
	(void)printf("              %7luK %6luK    %8qu\n",
	     (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
}

void
dopool(void)
{
	int first, ovflw;
	long addr;
	long total = 0, inuse = 0;
	TAILQ_HEAD(,pool) pool_head;
	struct pool pool, *pp = &pool;

	kread(X_POOLHEAD, &pool_head, sizeof(pool_head));
	addr = (long)TAILQ_FIRST(&pool_head);

	for (first = 1; addr != 0; ) {
		char name[32], maxp[32];
		if (kvm_read(kd, addr, (void *)pp, sizeof *pp) != sizeof *pp) {
			(void)fprintf(stderr,
			    "vmstat: pool chain trashed: %s\n",
			    kvm_geterr(kd));
			exit(1);
		}
		if (kvm_read(kd, (long)pp->pr_wchan, name, sizeof name) < 0) {
			(void)fprintf(stderr,
			    "vmstat: pool name trashed: %s\n",
			    kvm_geterr(kd));
			exit(1);
		}
		name[31] = '\0';

		if (first) {
			(void)printf("Memory resource pool statistics\n");
			(void)printf(
			    "%-11s%5s%9s%5s%9s%6s%6s%6s%6s%6s%6s%5s\n",
			    "Name",
			    "Size",
			    "Requests",
			    "Fail",
			    "Releases",
			    "Pgreq",
			    "Pgrel",
			    "Npage",
			    "Hiwat",
			    "Minpg",
			    "Maxpg",
			    "Idle");
			first = 0;
		}
		if (pp->pr_maxpages == UINT_MAX)
			sprintf(maxp, "inf");
		else
			sprintf(maxp, "%u", pp->pr_maxpages);
/*
 * Print single word.  `ovflow' is number of characters didn't fit
 * on the last word.  `fmt' is a format string to print this word.
 * It must contain asterisk for field width.  `width' is a width
 * occupied by this word.  `fixed' is a number of constant chars in
 * `fmt'.  `val' is a value to be printed using format string `fmt'.
 */
#define	PRWORD(ovflw, fmt, width, fixed, val) do {	\
	(ovflw) += printf((fmt),			\
	    (width) - (fixed) - (ovflw) > 0 ?		\
	    (width) - (fixed) - (ovflw) : 0,		\
	    (val)) - (width);				\
	if ((ovflw) < 0)				\
		(ovflw) = 0;				\
} while (/* CONSTCOND */0)
		ovflw = 0;
		PRWORD(ovflw, "%-*s", 11, 0, name);
		PRWORD(ovflw, " %*u", 5, 1, pp->pr_size);
		PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget);
		PRWORD(ovflw, " %*lu", 5, 1, pp->pr_nfail);
		PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nput);
		PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagealloc);
		PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagefree);
		PRWORD(ovflw, " %*d", 6, 1, pp->pr_npages);
		PRWORD(ovflw, " %*d", 6, 1, pp->pr_hiwat);
		PRWORD(ovflw, " %*d", 6, 1, pp->pr_minpages);
		PRWORD(ovflw, " %*s", 6, 1, maxp);
		PRWORD(ovflw, " %*lu\n", 5, 1, pp->pr_nidle);

		inuse += (pp->pr_nget - pp->pr_nput) * pp->pr_size;
		total += pp->pr_npages * pp->pr_pagesz;
		addr = (long)TAILQ_NEXT(pp, pr_poollist);
	}

	inuse /= 1024;
	total /= 1024;
	printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n",
	    inuse, total, (double)(100 * inuse) / total);
}

/*
 * kread reads something from the kernel, given its nlist index.
 */
void
kread(nlx, addr, size)
	int nlx;
	void *addr;
	size_t size;
{
	char *sym;

	if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
		sym = namelist[nlx].n_name;
		if (*sym == '_')
			++sym;
		errx(1, "symbol %s not defined", sym);
	}
	if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) {
		sym = namelist[nlx].n_name;
		if (*sym == '_')
			++sym;
		errx(1, "%s: %s", sym, kvm_geterr(kd));
	}
}

void
usage()
{
	(void)fprintf(stderr, "usage: %s [-fimst] [-c count] [-M core] "
	    "[-N system] [-w wait] [disks]\n", __progname);
	exit(1);
}