[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.158, Wed May 1 12:54:27 2024 UTC (2 weeks, 4 days ago) by mpi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.157: +6 -1 lines

Add per-CPU caches to the pmemrange allocator.

The caches are used primarily to reduce contention on uvm_lock_fpageq() during
concurrent page faults.  For the moment only uvm_pagealloc() tries to get a
page from the current CPU's cache.  So on some architectures the caches are
also used by the pmap layer.

Each cache is composed of two magazines, design is borrowed from jeff bonwick
vmem's paper and the implementation is similar to the one of pool_cache from
dlg@.  However there is no depot layer and magazines are refilled directly by
the pmemrange allocator.

This version includes splvm()/splx() dances because the buffer cache flips
buffers in interrupt context.  So we have to prevent recursive accesses to
per-CPU magazines.

Tested by naddy@, solene@, krw@, robert@, claudio@ and Laurence Tratt.

ok claudio@, kettenis@

/*	$NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $	*/
/*	$OpenBSD: vmstat.c,v 1.158 2024/05/01 12:54:27 mpi 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. 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.
 */

#include <sys/time.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/namei.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/device.h>
#include <sys/pool.h>
#include <sys/sched.h>
#include <sys/vmmeter.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <nlist.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "dkstats.h"

struct nlist namelist[] = {
#define X_UVMEXP	0		/* sysctl */
	{ "_uvmexp" },
#define	X_TIME_UPTIME	1
	{ "_time_uptime" },
#define X_NCHSTATS	2		/* sysctl */
	{ "_nchstats" },
#define	X_KMEMSTAT	3		/* sysctl */
	{ "_kmemstats" },
#define	X_KMEMBUCKETS	4		/* sysctl */
	{ "_bucket" },
#define	X_FORKSTAT	5		/* sysctl */
	{ "_forkstat" },
#define X_POOLHEAD	6		/* sysctl */
	{ "_pool_head" },
#define	X_NAPTIME	7
	{ "_naptime" },
	{ NULL },
};

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

struct	uvmexp uvmexp, ouvmexp;
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(void);
time_t	getuptime(void);
void	dkstats(void);
void	dointr(void);
void	domem(void);
void	dopool(void);
void	dosum(void);
void	dovmstat(u_int, int);
void	kread(int, void *, size_t);
void	usage(void);
void	dotimes(void);
void	doforkst(void);
void	needhdr(int);
int	pct(int64_t, int64_t);
void	printhdr(void);

char	**choosedrives(char **);

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

extern char *__progname;

int verbose = 0;
int zflag = 0;

int
main(int argc, char *argv[])
{
	char errbuf[_POSIX2_LINE_MAX];
	int c, todo = 0, reps = 0;
	struct winsize winsize;
	const char *errstr;
	u_int interval = 0;

	while ((c = getopt(argc, argv, "c:fiM:mN:stw:vz")) != -1) {
		switch (c) {
		case 'c':
			reps = strtonum(optarg, 0, INT_MAX, &errstr);
			if (errstr)
				errx(1, "-c %s: %s", optarg, errstr);
			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 = (u_int)strtonum(optarg, 0, 1000, &errstr);
			if (errstr)
				errx(1, "-w %s: %s", optarg, errstr);
			break;
		case 'v':
			verbose = 1;
			break;
		case 'z':
			zflag = 1;
			break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if (todo == 0)
		todo = VMSTAT;

	if (nlistf != NULL || memf != NULL) {
		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) {
			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) {
		dkinit(0);	/* Initialize disk stats, no disks selected. */
		argv = choosedrives(argv);	/* Select disks. */
	}

	if (unveil("/", "") == -1)
		err(1, "unveil /");
	if (unveil(NULL, NULL) == -1)
		err(1, "unveil");

	winsize.ws_row = 0;
	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) == 0) {
		if (winsize.ws_row > 0)
			winlines = winsize.ws_row;
	}

#define	BACKWARD_COMPATIBILITY
#ifdef	BACKWARD_COMPATIBILITY
	if (*argv) {
		interval = (u_int)strtonum(*argv, 0, 1000, &errstr);
		if (errstr)
			errx(1, "interval %s: %s", *argv, errstr);

		if (*++argv) {
			reps = strtonum(*argv, 0, INT_MAX, &errstr);
			if (errstr)
				errx(1, "reps %s: %s", *argv, errstr);
		}
	}
#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(char **argv)
{
	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((unsigned char)**argv))
			break;
#endif
		for (i = 0; i < dk_ndrive; i++) {
			if (strcmp(dr_name[i], *argv))
				continue;
			dk_select[i] = 1;
			++ndrives;
			break;
		}
		if (i == dk_ndrive)
			errx(1, "invalid interval or drive name: %s", *argv);
	}
	for (i = 0; i < dk_ndrive && ndrives < 2; i++) {
		if (dk_select[i])
			continue;
		dk_select[i] = 1;
		++ndrives;
	}
	return(argv);
}

time_t
getuptime(void)
{
	struct timespec uptime;
	time_t time_uptime, naptime;

	if (nlistf == NULL && memf == NULL) {
		if (clock_gettime(CLOCK_UPTIME, &uptime) == -1)
			err(1, "clock_gettime");
		return (uptime.tv_sec);
	}

	kread(X_NAPTIME, &naptime, sizeof(naptime));
	kread(X_TIME_UPTIME, &time_uptime, sizeof(time_uptime));
	return (time_uptime - naptime);
}

int	hz;
volatile sig_atomic_t hdrcnt;

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

	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) == -1) {
		warn("could not read kern.clockrate");
		return;
	}
	hz = clkinfo.stathz;

	for (hdrcnt = 1;;) {
		/* Read new disk statistics */
		dkreadstats();
		if (!--hdrcnt || last.dk_ndrive != cur.dk_ndrive)
			printhdr();
		if (nlistf == NULL && memf == NULL) {
			size = sizeof(struct uvmexp);
			mib[0] = CTL_VM;
			mib[1] = VM_UVMEXP;
			if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) == -1) {
				warn("could not get vm.uvmexp");
				memset(&uvmexp, 0, sizeof(struct uvmexp));
			}
		} else {
			kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
		}
		size = sizeof(total);
		mib[0] = CTL_VM;
		mib[1] = VM_METER;
		if (sysctl(mib, 2, &total, &size, NULL, 0) == -1) {
			warn("could not read vm.vmmeter");
			memset(&total, 0, sizeof(total));
		}
		(void)printf("%2u %3u", total.t_rq - 1, total.t_sl);
#define	rate(x)	((unsigned)((((unsigned)x) + halfuptime) / uptime)) /* round */
#define pgtok(a) ((a) * ((unsigned int)uvmexp.pagesize >> 10))
		(void)printf("%5uM %6uM ",
		    pgtok(uvmexp.active + uvmexp.swpginuse) / 1024,
		    pgtok(uvmexp.free) / 1024);
		(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 %5u %4u ",
		    rate(uvmexp.intrs - ouvmexp.intrs),
		    rate(uvmexp.syscalls - ouvmexp.syscalls),
		    rate(uvmexp.swtch - ouvmexp.swtch));
		cpustats();
		(void)printf("\n");
		(void)fflush(stdout);
		if (reps >= 0 && --reps <= 0)
			break;
		ouvmexp = uvmexp;
		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(void)
{
	int i;
	static int printedhdr;

	if (printedhdr && !isatty(STDOUT_FILENO))
		return;

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

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

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

	hdrcnt = 1;
}

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

	/* XXX Why are these set to 0 ? This doesn't look right. */
	pgintime = 0;
	rectime = 0;

	if (nlistf == NULL && memf == NULL) {
		size = sizeof(struct uvmexp);
		mib[0] = CTL_VM;
		mib[1] = VM_UVMEXP;
		if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) == -1) {
			warn("could not read vm.uvmexp");
			memset(&uvmexp, 0, sizeof(struct uvmexp));
		}
	} else {
		kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
	}

	(void)printf("%u reactivates, %u total time (usec)\n",
	    uvmexp.pdreact, rectime);
	if (uvmexp.pdreact != 0)
		(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);
	if (uvmexp.pageins != 0)
		(void)printf("average: %8.1f msec / page in\n",
		    pgintime / (uvmexp.pageins * 10.0));
}

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

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

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

	if (nlistf == NULL && memf == NULL) {
		size = sizeof(struct uvmexp);
		mib[0] = CTL_VM;
		mib[1] = VM_UVMEXP;
		if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) == -1) {
			warn("could not read vm.uvmexp");
			memset(&uvmexp, 0, sizeof(struct uvmexp));
		}
	} else {
		kread(X_UVMEXP, &uvmexp, sizeof(struct 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 zeroed\n", uvmexp.zeropages);
	(void)printf("%11u pages reserved for pagedaemon\n",
		     uvmexp.reserve_pagedaemon);
	(void)printf("%11u pages reserved for kernel\n",
		     uvmexp.reserve_kernel);
	(void)printf("%11u pages in per-cpu caches\n",
		     uvmexp.percpucaches);

	/* per-cpu cache */
	(void)printf("%11u per-cpu cache hits\n", uvmexp.pcphit);
	(void)printf("%11u per-cpu cache misses\n", uvmexp.pcpmiss);
	/* swap */
	(void)printf("%11u swap pages\n", uvmexp.swpages);
	(void)printf("%11u swap pages in use\n", uvmexp.swpginuse);

	/* 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 fpu context switches\n", uvmexp.fpswtch);
	(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 forks\n", uvmexp.forks);
	(void)printf("%11u forks where vmspace is shared\n",
		     uvmexp.forks_sharevm);
	(void)printf("%11u kernel map entries\n", uvmexp.kmapent);
	(void)printf("%11u zeroed page hits\n", uvmexp.pga_zerohit);
	(void)printf("%11u zeroed page misses\n", uvmexp.pga_zeromiss);

	/* daemon counters */
	(void)printf("%11u number of times the pagedaemon 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);

	if (nlistf == NULL && memf == NULL) {
		size = sizeof(nchstats);
		mib[0] = CTL_KERN;
		mib[1] = KERN_NCHSTATS;
		if (sysctl(mib, 2, &nchstats, &size, NULL, 0) == -1) {
			warn("could not read kern.nchstats");
			memset(&nchstats, 0, sizeof(nchstats));
		}
	} else {
		kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
	}

	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
	    nchstats.ncs_badhits + nchstats.ncs_falsehits +
	    nchstats.ncs_miss + nchstats.ncs_long;
	(void)printf("%11lld 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));
}

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

	if (nlistf == NULL && memf == NULL) {
		size = sizeof(struct forkstat);
		mib[0] = CTL_KERN;
		mib[1] = KERN_FORKSTAT;
		if (sysctl(mib, 2, &fks, &size, NULL, 0) == -1) {
			warn("could not read kern.forkstat");
			memset(&fks, 0, sizeof(struct forkstat));
		}
	} else {
		kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
	}

	(void)printf("%u forks, %llu pages, average %.2f\n",
	    fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
	(void)printf("%u vforks, %llu pages, average %.2f\n",
	    fks.cntvfork, fks.sizvfork,
	    (double)fks.sizvfork / (fks.cntvfork ? fks.cntvfork : 1));
	(void)printf("%u __tforks, %llu pages, average %.2f\n",
	    fks.cnttfork, fks.siztfork,
	    (double)fks.siztfork / (fks.cnttfork ? fks.cnttfork : 1));
	(void)printf("%u kthread creations, %llu pages, average %.2f\n",
	    fks.cntkthread, fks.sizkthread,
	    (double)fks.sizkthread / (fks.cntkthread ? fks.cntkthread : 1));
}

void
dkstats(void)
{
	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("%3.0f ",
		    (cur.dk_rxfer[dn] + cur.dk_rxfer[dn]) / etime);
	}
}

void
cpustats(void)
{
	double percent, total;
	int state;

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

void
dointr(void)
{
	int nintr, mib[4], i;
	char intrname[128];
	u_int64_t inttotal;
	time_t uptime;
	size_t siz;

	if (nlistf != NULL || memf != NULL) {
		errx(1,
		    "interrupt statistics are only available on live kernels");
	}

	uptime = getuptime();

	mib[0] = CTL_KERN;
	mib[1] = KERN_INTRCNT;
	mib[2] = KERN_INTRCNT_NUM;
	siz = sizeof(nintr);
	if (sysctl(mib, 3, &nintr, &siz, NULL, 0) == -1) {
		warnx("could not read kern.intrcnt.nintrcnt");
		return;
	}

	(void)printf("%-16s %20s %8s\n", "interrupt", "total", "rate");

	inttotal = 0;
	for (i = 0; i < nintr; i++) {
		char name[128];
		uint64_t cnt;
		int vector;

		mib[0] = CTL_KERN;
		mib[1] = KERN_INTRCNT;
		mib[2] = KERN_INTRCNT_NAME;
		mib[3] = i;
		siz = sizeof(name);
		if (sysctl(mib, 4, name, &siz, NULL, 0) == -1) {
			warnx("could not read kern.intrcnt.name.%d", i);
			return;
		}

		mib[0] = CTL_KERN;
		mib[1] = KERN_INTRCNT;
		mib[2] = KERN_INTRCNT_VECTOR;
		mib[3] = i;
		siz = sizeof(vector);
		if (sysctl(mib, 4, &vector, &siz, NULL, 0) == -1) {
			strlcpy(intrname, name, sizeof(intrname));
		} else {
			snprintf(intrname, sizeof(intrname), "irq%d/%s",
			    vector, name);
		}

		mib[0] = CTL_KERN;
		mib[1] = KERN_INTRCNT;
		mib[2] = KERN_INTRCNT_CNT;
		mib[3] = i;
		siz = sizeof(cnt);
		if (sysctl(mib, 4, &cnt, &siz, NULL, 0) == -1) {
			warnx("could not read kern.intrcnt.cnt.%d", i);
			return;
		}

		if (cnt || zflag)
			(void)printf("%-16.16s %20llu %8llu\n", intrname,
			    cnt, cnt / uptime);
		inttotal += cnt;
	}

	(void)printf("%-16s %20llu %8llu\n", "Total", inttotal,
	    inttotal / uptime);
}

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

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

	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) == -1) {
			warnx("could not read kern.malloc.buckets");
			return;
		}

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

			mib[3] = strtonum(ap, 0, INT_MAX, &errstr);
			if (errstr) {
				warnx("kernel lied about %d being a number", mib[3]);
				return;
			}

			if (sysctl(mib, 4, &buckets[MINBUCKET + i], &siz,
			    NULL, 0) == -1) {
				warn("could not read kern.malloc.bucket.%d", 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 && !verbose)
			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 %8llu %6llu %18llu %7llu %10llu\n", size,
		    (unsigned long long)(kp->kb_total - kp->kb_totalfree),
		    (unsigned long long)kp->kb_totalfree,
		    (unsigned long long)kp->kb_calls,
		    (unsigned long long)kp->kb_highwat,
		    (unsigned long long)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) {
		memset(kmemstats, 0, 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) == -1)
				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 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",
		    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);
		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    %8llu\n",
	     (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
}

static void
print_pool(struct kinfo_pool *pp, char *name)
{
	static int first = 1;
	char maxp[32];
	int ovflw;

	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",
		    "InUse",
		    "Pgreq",
		    "Pgrel",
		    "Npage",
		    "Hiwat",
		    "Minpg",
		    "Maxpg",
		    "Idle");
		first = 0;
	}

	/* Skip unused pools unless verbose output. */
	if (pp->pr_nget == 0 && !verbose)
		return;

	if (pp->pr_maxpages == UINT_MAX)
		snprintf(maxp, sizeof maxp, "inf");
	else
		snprintf(maxp, sizeof 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_nget - 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);
}

static void dopool_kvm(void);
static void dopool_sysctl(void);

void
dopool(void)
{
	if (nlistf == NULL && memf == NULL)
		dopool_sysctl();
	else
		dopool_kvm();
}

void
dopool_sysctl(void)
{
	int mib[4], npools, i;
	long total = 0, inuse = 0;
	struct kinfo_pool pool;
	size_t size;

	mib[0] = CTL_KERN;
	mib[1] = KERN_POOL;
	mib[2] = KERN_POOL_NPOOLS;
	size = sizeof(npools);
	if (sysctl(mib, 3, &npools, &size, NULL, 0) == -1) {
		warn("can't figure out number of pools in kernel");
		return;
	}

	for (i = 1; npools; i++) {
		char name[32];

		mib[0] = CTL_KERN;
		mib[1] = KERN_POOL;
		mib[2] = KERN_POOL_POOL;
		mib[3] = i;
		size = sizeof(pool);
		if (sysctl(mib, 4, &pool, &size, NULL, 0) == -1) {
			if (errno == ENOENT)
				continue;
			warn("error getting pool");
			return;
		}
		npools--;
		mib[2] = KERN_POOL_NAME;
		size = sizeof(name);
		if (sysctl(mib, 4, &name, &size, NULL, 0) == -1) {
			warn("error getting pool name");
			return;
		}
		print_pool(&pool, name);

		inuse += (pool.pr_nget - pool.pr_nput) * pool.pr_size;
		total += pool.pr_npages * pool.pr_pgsize;
	}

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

void
dopool_kvm(void)
{
	SIMPLEQ_HEAD(,pool) pool_head;
	struct pool pool, *pp = &pool;
	struct kinfo_pool pi;
	long total = 0, inuse = 0;
	u_long addr;

	kread(X_POOLHEAD, &pool_head, sizeof(pool_head));
	addr = (u_long)SIMPLEQ_FIRST(&pool_head);

	while (addr != 0) {
		char name[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, (u_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';

		memset(&pi, 0, sizeof(pi));
		pi.pr_size = pp->pr_size;
		pi.pr_pgsize = pp->pr_pgsize;
		pi.pr_itemsperpage = pp->pr_itemsperpage;
		pi.pr_npages = pp->pr_npages;
		pi.pr_minpages = pp->pr_minpages;
		pi.pr_maxpages = pp->pr_maxpages;
		pi.pr_hardlimit = pp->pr_hardlimit;
		pi.pr_nout = pp->pr_nout;
		pi.pr_nitems = pp->pr_nitems;
		pi.pr_nget = pp->pr_nget;
		pi.pr_nput = pp->pr_nput;
		pi.pr_nfail = pp->pr_nfail;
		pi.pr_npagealloc = pp->pr_npagealloc;
		pi.pr_npagefree = pp->pr_npagefree;
		pi.pr_hiwat = pp->pr_hiwat;
		pi.pr_nidle = pp->pr_nidle;

		print_pool(&pi, name);

		inuse += (pi.pr_nget - pi.pr_nput) * pi.pr_size;
		total += pi.pr_npages * pi.pr_pgsize;

		addr = (u_long)SIMPLEQ_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(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)
{
	(void)fprintf(stderr, "usage: %s [-fimstvz] [-c count] [-M core] "
	    "[-N system] [-w wait] [disk ...]\n", __progname);
	exit(1);
}