xref: /openbsd-src/usr.bin/vmstat/vmstat.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$NetBSD: vmstat.c,v 1.29.4.1 1996/06/05 00:21:05 cgd Exp $	*/
2 /*	$OpenBSD: vmstat.c,v 1.111 2008/10/08 17:47:28 deraadt Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1986, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 static char copyright[] =
35 "@(#) Copyright (c) 1980, 1986, 1991, 1993\n\
36 	The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)vmstat.c	8.1 (Berkeley) 6/6/93";
42 #else
43 static const char rcsid[] = "$OpenBSD: vmstat.c,v 1.111 2008/10/08 17:47:28 deraadt Exp $";
44 #endif
45 #endif /* not lint */
46 
47 #include <sys/param.h>
48 #include <sys/time.h>
49 #include <sys/proc.h>
50 #include <sys/user.h>
51 #include <sys/dkstat.h>
52 #include <sys/buf.h>
53 #include <sys/namei.h>
54 #include <sys/malloc.h>
55 #include <sys/fcntl.h>
56 #include <sys/ioctl.h>
57 #include <sys/sysctl.h>
58 #include <sys/device.h>
59 #include <sys/pool.h>
60 #include <time.h>
61 #include <nlist.h>
62 #include <kvm.h>
63 #include <err.h>
64 #include <errno.h>
65 #include <unistd.h>
66 #include <signal.h>
67 #include <stdio.h>
68 #include <ctype.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <paths.h>
72 #include <limits.h>
73 #include "dkstats.h"
74 
75 #include <uvm/uvm_object.h>
76 #include <uvm/uvm_extern.h>
77 
78 struct nlist namelist[] = {
79 #define X_UVMEXP	0		/* sysctl */
80 	{ "_uvmexp" },
81 #define	X_BOOTTIME	1		/* sysctl */
82 	{ "_boottime" },
83 #define X_NCHSTATS	2		/* sysctl */
84 	{ "_nchstats" },
85 #define	X_KMEMSTAT	3		/* sysctl */
86 	{ "_kmemstats" },
87 #define	X_KMEMBUCKETS	4		/* sysctl */
88 	{ "_bucket" },
89 #define	X_FORKSTAT	5		/* sysctl */
90 	{ "_forkstat" },
91 #define X_NSELCOLL	6		/* sysctl */
92 	{ "_nselcoll" },
93 #define X_POOLHEAD	7		/* sysctl */
94 	{ "_pool_head" },
95 #define X_KMPAGESFREE	8		/* sysctl */
96 	{ "_uvm_km_pages_free" },
97 	{ "" },
98 };
99 
100 /* Objects defined in dkstats.c */
101 extern struct _disk	cur, last;
102 extern char	**dr_name;
103 extern int	*dk_select, dk_ndrive;
104 
105 struct	uvmexp uvmexp, ouvmexp;
106 int		ndrives;
107 
108 int	winlines = 20;
109 
110 kvm_t *kd;
111 
112 #define	FORKSTAT	0x01
113 #define	INTRSTAT	0x02
114 #define	MEMSTAT		0x04
115 #define	SUMSTAT		0x08
116 #define	TIMESTAT	0x10
117 #define	VMSTAT		0x20
118 
119 void	cpustats(void);
120 time_t	getuptime(void);
121 void	dkstats(void);
122 void	dointr(void);
123 void	domem(void);
124 void	dopool(void);
125 void	dosum(void);
126 void	dovmstat(u_int, int);
127 void	kread(int, void *, size_t);
128 void	usage(void);
129 void	dotimes(void);
130 void	doforkst(void);
131 void	needhdr(int);
132 int	pct(long, long);
133 void	printhdr(void);
134 
135 char	**choosedrives(char **);
136 
137 /* Namelist and memory file names. */
138 char	*nlistf, *memf;
139 
140 extern char *__progname;
141 
142 int verbose = 0;
143 int zflag = 0;
144 
145 int ncpu;
146 
147 int
148 main(int argc, char *argv[])
149 {
150 	char errbuf[_POSIX2_LINE_MAX];
151 	int c, todo = 0, reps = 0, mib[2];
152 	const char *errstr;
153 	u_int interval = 0;
154 	size_t size;
155 	gid_t gid;
156 
157 	while ((c = getopt(argc, argv, "c:fiM:mN:stw:vz")) != -1) {
158 		switch (c) {
159 		case 'c':
160 			reps = atoi(optarg);
161 			break;
162 		case 'f':
163 			todo |= FORKSTAT;
164 			break;
165 		case 'i':
166 			todo |= INTRSTAT;
167 			break;
168 		case 'M':
169 			memf = optarg;
170 			break;
171 		case 'm':
172 			todo |= MEMSTAT;
173 			break;
174 		case 'N':
175 			nlistf = optarg;
176 			break;
177 		case 's':
178 			todo |= SUMSTAT;
179 			break;
180 		case 't':
181 			todo |= TIMESTAT;
182 			break;
183 		case 'w':
184 			interval = (u_int)strtonum(optarg, 0, 1000, &errstr);
185 			if (errstr)
186 				errx(1, "-w %s: %s", optarg, errstr);
187 			break;
188 		case 'v':
189 			verbose = 1;
190 			break;
191 		case 'z':
192 			zflag = 1;
193 			break;
194 		case '?':
195 		default:
196 			usage();
197 		}
198 	}
199 	argc -= optind;
200 	argv += optind;
201 
202 	if (todo == 0)
203 		todo = VMSTAT;
204 
205 	gid = getgid();
206 	if (nlistf != NULL || memf != NULL) {
207 		if (setresgid(gid, gid, gid) == -1)
208 			err(1, "setresgid");
209 	}
210 
211 	/*
212 	 * Discard setgid privileges if not the running kernel so that bad
213 	 * guys can't print interesting stuff from kernel memory.
214 	 */
215 	if (nlistf != NULL || memf != NULL) {
216 		kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
217 		if (kd == 0)
218 			errx(1, "kvm_openfiles: %s", errbuf);
219 
220 		if (nlistf == NULL && memf == NULL)
221 			if (setresgid(gid, gid, gid) == -1)
222 				err(1, "setresgid");
223 
224 		if ((c = kvm_nlist(kd, namelist)) != 0) {
225 
226 			if (c > 0) {
227 				(void)fprintf(stderr,
228 				    "%s: undefined symbols:", __progname);
229 				for (c = 0;
230 				    c < sizeof(namelist)/sizeof(namelist[0]);
231 				    c++)
232 					if (namelist[c].n_type == 0)
233 						fprintf(stderr, " %s",
234 						    namelist[c].n_name);
235 				(void)fputc('\n', stderr);
236 				exit(1);
237 			} else
238 				errx(1, "kvm_nlist: %s", kvm_geterr(kd));
239 		}
240 	} else if (setresgid(gid, gid, gid) == -1)
241 		err(1, "setresgid");
242 
243 	mib[0] = CTL_HW;
244 	mib[1] = HW_NCPU;
245 	size = sizeof(ncpu);
246 	(void) sysctl(mib, 2, &ncpu, &size, NULL, 0);
247 
248 	if (todo & VMSTAT) {
249 		struct winsize winsize;
250 
251 		dkinit(0);	/* Initialize disk stats, no disks selected. */
252 		argv = choosedrives(argv);	/* Select disks. */
253 		winsize.ws_row = 0;
254 		(void) ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize);
255 		if (winsize.ws_row > 0)
256 			winlines = winsize.ws_row;
257 
258 	}
259 
260 #define	BACKWARD_COMPATIBILITY
261 #ifdef	BACKWARD_COMPATIBILITY
262 	if (*argv) {
263 		interval = (u_int)strtonum(*argv, 0, 1000, &errstr);
264 		if (errstr)
265 			errx(1, "%s: %s", *argv, errstr);
266 
267 		if (*++argv)
268 			reps = atoi(*argv);
269 	}
270 #endif
271 
272 	if (interval) {
273 		if (!reps)
274 			reps = -1;
275 	} else if (reps)
276 		interval = 1;
277 
278 	if (todo & FORKSTAT)
279 		doforkst();
280 	if (todo & MEMSTAT) {
281 		domem();
282 		dopool();
283 	}
284 	if (todo & SUMSTAT)
285 		dosum();
286 	if (todo & TIMESTAT)
287 		dotimes();
288 	if (todo & INTRSTAT)
289 		dointr();
290 	if (todo & VMSTAT)
291 		dovmstat(interval, reps);
292 	exit(0);
293 }
294 
295 char **
296 choosedrives(char **argv)
297 {
298 	int i;
299 
300 	/*
301 	 * Choose drives to be displayed.  Priority goes to (in order) drives
302 	 * supplied as arguments, default drives.  If everything isn't filled
303 	 * in and there are drives not taken care of, display the first few
304 	 * that fit.
305 	 */
306 #define BACKWARD_COMPATIBILITY
307 	for (ndrives = 0; *argv; ++argv) {
308 #ifdef	BACKWARD_COMPATIBILITY
309 		if (isdigit(**argv))
310 			break;
311 #endif
312 		for (i = 0; i < dk_ndrive; i++) {
313 			if (strcmp(dr_name[i], *argv))
314 				continue;
315 			dk_select[i] = 1;
316 			++ndrives;
317 			break;
318 		}
319 	}
320 	for (i = 0; i < dk_ndrive && ndrives < 2; i++) {
321 		if (dk_select[i])
322 			continue;
323 		dk_select[i] = 1;
324 		++ndrives;
325 	}
326 	return(argv);
327 }
328 
329 time_t
330 getuptime(void)
331 {
332 	static struct timeval boottime;
333 	static time_t now;
334 	time_t uptime;
335 	size_t size;
336 	int mib[2];
337 
338 	if (boottime.tv_sec == 0) {
339 		if (nlistf == NULL && memf == NULL) {
340 			size = sizeof(boottime);
341 			mib[0] = CTL_KERN;
342 			mib[1] = KERN_BOOTTIME;
343 			if (sysctl(mib, 2, &boottime, &size, NULL, 0) < 0) {
344 				warn("could not get kern.boottime");
345 				bzero(&boottime, sizeof(boottime));
346 			}
347 		} else {
348 			kread(X_BOOTTIME, &boottime, sizeof(boottime));
349 		}
350 	}
351 	(void)time(&now);
352 	uptime = now - boottime.tv_sec;
353 	if (uptime <= 0 || uptime > 60*60*24*365*10)
354 		errx(1, "time makes no sense; namelist must be wrong");
355 
356 	return(uptime);
357 }
358 
359 int	hz;
360 volatile sig_atomic_t hdrcnt;
361 
362 void
363 dovmstat(u_int interval, int reps)
364 {
365 	time_t uptime, halfuptime;
366 	struct clockinfo clkinfo;
367 	struct vmtotal total;
368 	size_t size;
369 	int mib[2];
370 
371 	uptime = getuptime();
372 	halfuptime = uptime / 2;
373 	(void)signal(SIGCONT, needhdr);
374 
375 	mib[0] = CTL_KERN;
376 	mib[1] = KERN_CLOCKRATE;
377 	size = sizeof(clkinfo);
378 	if (sysctl(mib, 2, &clkinfo, &size, NULL, 0) < 0) {
379 		warn("could not read kern.clockrate");
380 		return;
381 	}
382 	hz = clkinfo.stathz;
383 
384 	for (hdrcnt = 1;;) {
385 		/* Read new disk statistics */
386 		dkreadstats();
387 		if (!--hdrcnt || last.dk_ndrive != cur.dk_ndrive)
388 			printhdr();
389 		if (nlistf == NULL && memf == NULL) {
390 			size = sizeof(struct uvmexp);
391 			mib[0] = CTL_VM;
392 			mib[1] = VM_UVMEXP;
393 			if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
394 				warn("could not get vm.uvmexp");
395 				bzero(&uvmexp, sizeof(struct uvmexp));
396 			}
397 		} else {
398 			kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
399 		}
400 		size = sizeof(total);
401 		mib[0] = CTL_VM;
402 		mib[1] = VM_METER;
403 		if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
404 			warn("could not read vm.vmmeter");
405 			bzero(&total, sizeof(total));
406 		}
407 		(void)printf("%2u%2u%2u",
408 		    total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw);
409 #define	rate(x)	((((unsigned)x) + halfuptime) / uptime)	/* round */
410 #define pgtok(a) ((a) * ((unsigned int)uvmexp.pagesize >> 10))
411 		(void)printf("%7u %7u ",
412 		    pgtok(total.t_avm), pgtok(total.t_free));
413 		(void)printf("%4u ", rate(uvmexp.faults - ouvmexp.faults));
414 		(void)printf("%3u ", rate(uvmexp.pdreact - ouvmexp.pdreact));
415 		(void)printf("%3u ", rate(uvmexp.pageins - ouvmexp.pageins));
416 		(void)printf("%3u %3u ",
417 		    rate(uvmexp.pdpageouts - ouvmexp.pdpageouts), 0);
418 		(void)printf("%3u ", rate(uvmexp.pdscans - ouvmexp.pdscans));
419 		dkstats();
420 		(void)printf("%4u %5u %4u ",
421 		    rate(uvmexp.intrs - ouvmexp.intrs),
422 		    rate(uvmexp.syscalls - ouvmexp.syscalls),
423 		    rate(uvmexp.swtch - ouvmexp.swtch));
424 		cpustats();
425 		(void)printf("\n");
426 		(void)fflush(stdout);
427 		if (reps >= 0 && --reps <= 0)
428 			break;
429 		ouvmexp = uvmexp;
430 		uptime = interval;
431 		/*
432 		 * We round upward to avoid losing low-frequency events
433 		 * (i.e., >= 1 per interval but < 1 per second).
434 		 */
435 		halfuptime = uptime == 1 ? 0 : (uptime + 1) / 2;
436 		(void)sleep(interval);
437 	}
438 }
439 
440 void
441 printhdr(void)
442 {
443 	int i;
444 
445 	(void)printf(" procs    memory       page%*s", 20, "");
446 	if (ndrives > 0)
447 		(void)printf("%s %*straps          cpu\n",
448 		   ((ndrives > 1) ? "disks" : "disk"),
449 		   ((ndrives > 1) ? ndrives * 4 - 5 : 0), "");
450 	else
451 		(void)printf("%*s  traps           cpu\n",
452 		   ndrives * 3, "");
453 
454 	(void)printf(" r b w    avm     fre  flt  re  pi  po  fr  sr ");
455 	for (i = 0; i < dk_ndrive; i++)
456 		if (dk_select[i])
457 			(void)printf("%c%c%c ", dr_name[i][0],
458 			    dr_name[i][1],
459 			    dr_name[i][strlen(dr_name[i]) - 1]);
460 	(void)printf(" int   sys   cs us sy id\n");
461 	hdrcnt = winlines - 2;
462 }
463 
464 /*
465  * Force a header to be prepended to the next output.
466  */
467 /* ARGSUSED */
468 void
469 needhdr(int signo)
470 {
471 
472 	hdrcnt = 1;
473 }
474 
475 void
476 dotimes(void)
477 {
478 	u_int pgintime, rectime;
479 	size_t size;
480 	int mib[2];
481 
482 	/* XXX Why are these set to 0 ? This doesn't look right. */
483 	pgintime = 0;
484 	rectime = 0;
485 
486 	if (nlistf == NULL && memf == NULL) {
487 		size = sizeof(struct uvmexp);
488 		mib[0] = CTL_VM;
489 		mib[1] = VM_UVMEXP;
490 		if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
491 			warn("could not read vm.uvmexp");
492 			bzero(&uvmexp, sizeof(struct uvmexp));
493 		}
494 	} else {
495 		kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
496 	}
497 
498 	(void)printf("%u reactivates, %u total time (usec)\n",
499 	    uvmexp.pdreact, rectime);
500 	if (uvmexp.pdreact != 0)
501 		(void)printf("average: %u usec / reclaim\n",
502 		    rectime / uvmexp.pdreact);
503 	(void)printf("\n");
504 	(void)printf("%u page ins, %u total time (msec)\n",
505 	    uvmexp.pageins, pgintime / 10);
506 	if (uvmexp.pageins != 0)
507 		(void)printf("average: %8.1f msec / page in\n",
508 		    pgintime / (uvmexp.pageins * 10.0));
509 }
510 
511 int
512 pct(long top, long bot)
513 {
514 	int ans;
515 
516 	if (bot == 0)
517 		return(0);
518 	ans = (quad_t)top * 100 / bot;
519 	return (ans);
520 }
521 
522 #define	PCT(top, bot) pct((long)(top), (long)(bot))
523 
524 void
525 dosum(void)
526 {
527 	struct nchstats nchstats;
528 	int mib[2], nselcoll;
529 	long nchtotal;
530 	size_t size;
531 
532 	if (nlistf == NULL && memf == NULL) {
533 		size = sizeof(struct uvmexp);
534 		mib[0] = CTL_VM;
535 		mib[1] = VM_UVMEXP;
536 		if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0) {
537 			warn("could not read vm.uvmexp");
538 			bzero(&uvmexp, sizeof(struct uvmexp));
539 		}
540 	} else {
541 		kread(X_UVMEXP, &uvmexp, sizeof(struct uvmexp));
542 	}
543 
544 	/* vm_page constants */
545 	(void)printf("%11u bytes per page\n", uvmexp.pagesize);
546 
547 	/* vm_page counters */
548 	(void)printf("%11u pages managed\n", uvmexp.npages);
549 	(void)printf("%11u pages free\n", uvmexp.free);
550 	(void)printf("%11u pages active\n", uvmexp.active);
551 	(void)printf("%11u pages inactive\n", uvmexp.inactive);
552 	(void)printf("%11u pages being paged out\n", uvmexp.paging);
553 	(void)printf("%11u pages wired\n", uvmexp.wired);
554 	(void)printf("%11u pages zeroed\n", uvmexp.zeropages);
555 	(void)printf("%11u pages reserved for pagedaemon\n",
556 		     uvmexp.reserve_pagedaemon);
557 	(void)printf("%11u pages reserved for kernel\n",
558 		     uvmexp.reserve_kernel);
559 
560 	/* swap */
561 	(void)printf("%11u swap pages\n", uvmexp.swpages);
562 	(void)printf("%11u swap pages in use\n", uvmexp.swpginuse);
563 	(void)printf("%11u total anon's in system\n", uvmexp.nanon);
564 	(void)printf("%11u free anon's\n", uvmexp.nfreeanon);
565 
566 	/* stat counters */
567 	(void)printf("%11u page faults\n", uvmexp.faults);
568 	(void)printf("%11u traps\n", uvmexp.traps);
569 	(void)printf("%11u interrupts\n", uvmexp.intrs);
570 	(void)printf("%11u cpu context switches\n", uvmexp.swtch);
571 	(void)printf("%11u fpu context switches\n", uvmexp.fpswtch);
572 	(void)printf("%11u software interrupts\n", uvmexp.softs);
573 	(void)printf("%11u syscalls\n", uvmexp.syscalls);
574 	(void)printf("%11u pagein operations\n", uvmexp.pageins);
575 	(void)printf("%11u swap ins\n", uvmexp.swapins);
576 	(void)printf("%11u swap outs\n", uvmexp.swapouts);
577 	(void)printf("%11u forks\n", uvmexp.forks);
578 	(void)printf("%11u forks where vmspace is shared\n",
579 		     uvmexp.forks_sharevm);
580 	(void)printf("%11u kernel map entries\n", uvmexp.kmapent);
581 
582 	/* daemon counters */
583 	(void)printf("%11u number of times the pagedaemon woke up\n",
584 		     uvmexp.pdwoke);
585 	(void)printf("%11u revolutions of the clock hand\n", uvmexp.pdrevs);
586 	(void)printf("%11u pages freed by pagedaemon\n", uvmexp.pdfreed);
587 	(void)printf("%11u pages scanned by pagedaemon\n", uvmexp.pdscans);
588 	(void)printf("%11u pages reactivated by pagedaemon\n", uvmexp.pdreact);
589 	(void)printf("%11u busy pages found by pagedaemon\n", uvmexp.pdbusy);
590 
591 	if (nlistf == NULL && memf == NULL) {
592 		size = sizeof(nchstats);
593 		mib[0] = CTL_KERN;
594 		mib[1] = KERN_NCHSTATS;
595 		if (sysctl(mib, 2, &nchstats, &size, NULL, 0) < 0) {
596 			warn("could not read kern.nchstats");
597 			bzero(&nchstats, sizeof(nchstats));
598 		}
599 	} else {
600 		kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
601 	}
602 
603 	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
604 	    nchstats.ncs_badhits + nchstats.ncs_falsehits +
605 	    nchstats.ncs_miss + nchstats.ncs_long;
606 	(void)printf("%11ld total name lookups\n", nchtotal);
607 	(void)printf("%11s cache hits (%d%% pos + %d%% neg) system %d%% "
608 	    "per-directory\n",
609 	    "", PCT(nchstats.ncs_goodhits, nchtotal),
610 	    PCT(nchstats.ncs_neghits, nchtotal),
611 	    PCT(nchstats.ncs_pass2, nchtotal));
612 	(void)printf("%11s deletions %d%%, falsehits %d%%, toolong %d%%\n", "",
613 	    PCT(nchstats.ncs_badhits, nchtotal),
614 	    PCT(nchstats.ncs_falsehits, nchtotal),
615 	    PCT(nchstats.ncs_long, nchtotal));
616 
617 	if (nlistf == NULL && memf == NULL) {
618 		size = sizeof(nselcoll);
619 		mib[0] = CTL_KERN;
620 		mib[1] = KERN_NSELCOLL;
621 		if (sysctl(mib, 2, &nselcoll, &size, NULL, 0) < 0) {
622 			warn("could not read kern.nselcoll");
623 			nselcoll = 0;
624 		}
625 	} else {
626 		kread(X_NSELCOLL, &nselcoll, sizeof(nselcoll));
627 	}
628 	(void)printf("%11d select collisions\n", nselcoll);
629 }
630 
631 void
632 doforkst(void)
633 {
634 	struct forkstat fks;
635 	size_t size;
636 	int mib[2];
637 
638 	if (nlistf == NULL && memf == NULL) {
639 		size = sizeof(struct forkstat);
640 		mib[0] = CTL_KERN;
641 		mib[1] = KERN_FORKSTAT;
642 		if (sysctl(mib, 2, &fks, &size, NULL, 0) < 0) {
643 			warn("could not read kern.forkstat");
644 			bzero(&fks, sizeof(struct forkstat));
645 		}
646 	} else {
647 		kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
648 	}
649 
650 	(void)printf("%d forks, %d pages, average %.2f\n",
651 	    fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
652 	(void)printf("%d vforks, %d pages, average %.2f\n",
653 	    fks.cntvfork, fks.sizvfork,
654 	    (double)fks.sizvfork / (fks.cntvfork ? fks.cntvfork : 1));
655 	(void)printf("%d rforks, %d pages, average %.2f\n",
656 	    fks.cntrfork, fks.sizrfork,
657 	    (double)fks.sizrfork / (fks.cntrfork ? fks.cntrfork : 1));
658 	(void)printf("%d kthread creations, %d pages, average %.2f\n",
659 	    fks.cntkthread, fks.sizkthread,
660 	    (double)fks.sizkthread / (fks.cntkthread ? fks.cntkthread : 1));
661 }
662 
663 void
664 dkstats(void)
665 {
666 	int dn, state;
667 	double etime;
668 
669 	/* Calculate disk stat deltas. */
670 	dkswap();
671 	etime = 0;
672 	for (state = 0; state < CPUSTATES; ++state) {
673 		etime += cur.cp_time[state];
674 	}
675 	if (etime == 0)
676 		etime = 1;
677 	etime /= hz;
678 	etime /= ncpu;
679 	for (dn = 0; dn < dk_ndrive; ++dn) {
680 		if (!dk_select[dn])
681 			continue;
682 		(void)printf("%3.0f ",
683 		    (cur.dk_rxfer[dn] + cur.dk_rxfer[dn]) / etime);
684 	}
685 }
686 
687 void
688 cpustats(void)
689 {
690 	double percent, total;
691 	int state;
692 
693 	total = 0;
694 	for (state = 0; state < CPUSTATES; ++state)
695 		total += cur.cp_time[state];
696 	if (total)
697 		percent = 100 / total;
698 	else
699 		percent = 0;
700 	(void)printf("%2.0f ", (cur.cp_time[CP_USER] + cur.cp_time[CP_NICE]) * percent);
701 	(void)printf("%2.0f ", (cur.cp_time[CP_SYS] + cur.cp_time[CP_INTR]) * percent);
702 	(void)printf("%2.0f", cur.cp_time[CP_IDLE] * percent);
703 }
704 
705 void
706 dointr(void)
707 {
708 	int nintr, mib[4], i;
709 	char intrname[128];
710 	u_int64_t inttotal;
711 	time_t uptime;
712 	size_t siz;
713 
714 	if (nlistf != NULL || memf != NULL) {
715 		errx(1,
716 		    "interrupt statistics are only available on live kernels");
717 	}
718 
719 	uptime = getuptime();
720 
721 	mib[0] = CTL_KERN;
722 	mib[1] = KERN_INTRCNT;
723 	mib[2] = KERN_INTRCNT_NUM;
724 	siz = sizeof(nintr);
725 	if (sysctl(mib, 3, &nintr, &siz, NULL, 0) < 0) {
726 		warnx("could not read kern.intrcnt.nintrcnt");
727 		return;
728 	}
729 
730 	(void)printf("%-16s %20s %8s\n", "interrupt", "total", "rate");
731 
732 	inttotal = 0;
733 	for (i = 0; i < nintr; i++) {
734 		char name[128];
735 		u_quad_t cnt;
736 		int vector;
737 
738 		mib[0] = CTL_KERN;
739 		mib[1] = KERN_INTRCNT;
740 		mib[2] = KERN_INTRCNT_NAME;
741 		mib[3] = i;
742 		siz = sizeof(name);
743 		if (sysctl(mib, 4, name, &siz, NULL, 0) < 0) {
744 			warnx("could not read kern.intrcnt.name.%d", i);
745 			return;
746 		}
747 
748 		mib[0] = CTL_KERN;
749 		mib[1] = KERN_INTRCNT;
750 		mib[2] = KERN_INTRCNT_VECTOR;
751 		mib[3] = i;
752 		siz = sizeof(vector);
753 		if (sysctl(mib, 4, &vector, &siz, NULL, 0) < 0) {
754 			strlcpy(intrname, name, sizeof(intrname));
755 		} else {
756 			snprintf(intrname, sizeof(intrname), "irq%d/%s",
757 			    vector, name);
758 		}
759 
760 		mib[0] = CTL_KERN;
761 		mib[1] = KERN_INTRCNT;
762 		mib[2] = KERN_INTRCNT_CNT;
763 		mib[3] = i;
764 		siz = sizeof(cnt);
765 		if (sysctl(mib, 4, &cnt, &siz, NULL, 0) < 0) {
766 			warnx("could not read kern.intrcnt.cnt.%d", i);
767 			return;
768 		}
769 
770 		if (cnt || zflag)
771 			(void)printf("%-16.16s %20llu %8llu\n", intrname,
772 			    cnt, cnt / uptime);
773 		inttotal += cnt;
774 	}
775 
776 	(void)printf("%-16s %20llu %8llu\n", "Total", inttotal,
777 	    inttotal / uptime);
778 }
779 
780 /*
781  * These names are defined in <sys/malloc.h>.
782  */
783 const char *kmemnames[] = INITKMEMNAMES;
784 
785 void
786 domem(void)
787 {
788 	struct kmembuckets buckets[MINBUCKET + 16], *kp;
789 	struct kmemstats kmemstats[M_LAST], *ks;
790 	int i, j, len, size, first, mib[4];
791 	u_long totuse = 0, totfree = 0;
792 	char buf[BUFSIZ], *bufp, *ap;
793 	quad_t totreq = 0;
794 	const char *name;
795 	size_t siz;
796 
797 	if (memf == NULL && nlistf == NULL) {
798 		mib[0] = CTL_KERN;
799 		mib[1] = KERN_MALLOCSTATS;
800 		mib[2] = KERN_MALLOC_BUCKETS;
801 		siz = sizeof(buf);
802 		if (sysctl(mib, 3, buf, &siz, NULL, 0) < 0) {
803 			warnx("could not read kern.malloc.buckets");
804 			return;
805 		}
806 
807 		bufp = buf;
808 		mib[2] = KERN_MALLOC_BUCKET;
809 		siz = sizeof(struct kmembuckets);
810 		i = 0;
811 		while ((ap = strsep(&bufp, ",")) != NULL) {
812 			mib[3] = atoi(ap);
813 
814 			if (sysctl(mib, 4, &buckets[MINBUCKET + i], &siz,
815 			    NULL, 0) < 0) {
816 				warn("could not read kern.malloc.bucket.%d", mib[3]);
817 				return;
818 			}
819 			i++;
820 		}
821 	} else {
822 		kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
823 	}
824 
825 	for (first = 1, i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16;
826 	     i++, kp++) {
827 		if (kp->kb_calls == 0 && !verbose)
828 			continue;
829 		if (first) {
830 			(void)printf("Memory statistics by bucket size\n");
831 			(void)printf(
832 		"    Size   In Use   Free           Requests  HighWater  Couldfree\n");
833 			first = 0;
834 		}
835 		size = 1 << i;
836 		(void)printf("%8d %8llu %6llu %18llu %7llu %10llu\n", size,
837 			(unsigned long long)(kp->kb_total - kp->kb_totalfree),
838 			(unsigned long long)kp->kb_totalfree,
839 			(unsigned long long)kp->kb_calls,
840 			(unsigned long long)kp->kb_highwat,
841 			(unsigned long long)kp->kb_couldfree);
842 		totfree += size * kp->kb_totalfree;
843 	}
844 
845 	/*
846 	 * If kmem statistics are not being gathered by the kernel,
847 	 * first will still be 1.
848 	 */
849 	if (first) {
850 		printf(
851 		    "Kmem statistics are not being gathered by the kernel.\n");
852 		return;
853 	}
854 
855 	if (memf == NULL && nlistf == NULL) {
856 		bzero(kmemstats, sizeof(kmemstats));
857 		for (i = 0; i < M_LAST; i++) {
858 			mib[0] = CTL_KERN;
859 			mib[1] = KERN_MALLOCSTATS;
860 			mib[2] = KERN_MALLOC_KMEMSTATS;
861 			mib[3] = i;
862 			siz = sizeof(struct kmemstats);
863 
864 			/*
865 			 * Skip errors -- these are presumed to be unallocated
866 			 * entries.
867 			 */
868 			if (sysctl(mib, 4, &kmemstats[i], &siz, NULL, 0) < 0)
869 				continue;
870 		}
871 	} else {
872 		kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats));
873 	}
874 
875 	(void)printf("\nMemory usage type by bucket size\n");
876 	(void)printf("    Size  Type(s)\n");
877 	kp = &buckets[MINBUCKET];
878 	for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) {
879 		if (kp->kb_calls == 0)
880 			continue;
881 		first = 1;
882 		len = 8;
883 		for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
884 			if (ks->ks_calls == 0)
885 				continue;
886 			if ((ks->ks_size & j) == 0)
887 				continue;
888 			name = kmemnames[i] ? kmemnames[i] : "undefined";
889 			len += 2 + strlen(name);
890 			if (first)
891 				printf("%8d  %s", j, name);
892 			else
893 				printf(",");
894 			if (len >= 80) {
895 				printf("\n\t ");
896 				len = 10 + strlen(name);
897 			}
898 			if (!first)
899 				printf(" %s", name);
900 			first = 0;
901 		}
902 		printf("\n");
903 	}
904 
905 	(void)printf(
906 	   "\nMemory statistics by type                           Type  Kern\n");
907 	(void)printf(
908 "          Type InUse MemUse HighUse  Limit Requests Limit Limit Size(s)\n");
909 	for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
910 		if (ks->ks_calls == 0)
911 			continue;
912 		(void)printf("%14s%6ld%6ldK%7ldK%6ldK%9ld%5u%6u",
913 		    kmemnames[i] ? kmemnames[i] : "undefined",
914 		    ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
915 		    (ks->ks_maxused + 1023) / 1024,
916 		    (ks->ks_limit + 1023) / 1024, ks->ks_calls,
917 		    ks->ks_limblocks, ks->ks_mapblocks);
918 		first = 1;
919 		for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
920 			if ((ks->ks_size & j) == 0)
921 				continue;
922 			if (first)
923 				printf("  %d", j);
924 			else
925 				printf(",%d", j);
926 			first = 0;
927 		}
928 		printf("\n");
929 		totuse += ks->ks_memuse;
930 		totreq += ks->ks_calls;
931 	}
932 	(void)printf("\nMemory Totals:  In Use    Free    Requests\n");
933 	(void)printf("              %7luK %6luK    %8qu\n",
934 	     (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
935 }
936 
937 static void
938 print_pool(struct pool *pp, char *name)
939 {
940 	static int first = 1;
941 	char maxp[32];
942 	int ovflw;
943 
944 	if (first) {
945 		(void)printf("Memory resource pool statistics\n");
946 		(void)printf(
947 		    "%-11s%5s%9s%5s%9s%6s%6s%6s%6s%6s%6s%5s\n",
948 		    "Name",
949 		    "Size",
950 		    "Requests",
951 		    "Fail",
952 		    "InUse",
953 		    "Pgreq",
954 		    "Pgrel",
955 		    "Npage",
956 		    "Hiwat",
957 		    "Minpg",
958 		    "Maxpg",
959 		    "Idle");
960 		first = 0;
961 	}
962 
963 	/* Skip unused pools unless verbose output. */
964 	if (pp->pr_nget == 0 && !verbose)
965 		return;
966 
967 	if (pp->pr_maxpages == UINT_MAX)
968 		snprintf(maxp, sizeof maxp, "inf");
969 	else
970 		snprintf(maxp, sizeof maxp, "%u", pp->pr_maxpages);
971 /*
972  * Print single word.  `ovflow' is number of characters didn't fit
973  * on the last word.  `fmt' is a format string to print this word.
974  * It must contain asterisk for field width.  `width' is a width
975  * occupied by this word.  `fixed' is a number of constant chars in
976  * `fmt'.  `val' is a value to be printed using format string `fmt'.
977  */
978 #define	PRWORD(ovflw, fmt, width, fixed, val) do {	\
979 	(ovflw) += printf((fmt),			\
980 	    (width) - (fixed) - (ovflw) > 0 ?		\
981 	    (width) - (fixed) - (ovflw) : 0,		\
982 	    (val)) - (width);				\
983 	if ((ovflw) < 0)				\
984 		(ovflw) = 0;				\
985 } while (/* CONSTCOND */0)
986 
987 	ovflw = 0;
988 	PRWORD(ovflw, "%-*s", 11, 0, name);
989 	PRWORD(ovflw, " %*u", 5, 1, pp->pr_size);
990 	PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget);
991 	PRWORD(ovflw, " %*lu", 5, 1, pp->pr_nfail);
992 	PRWORD(ovflw, " %*lu", 9, 1, pp->pr_nget - pp->pr_nput);
993 	PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagealloc);
994 	PRWORD(ovflw, " %*lu", 6, 1, pp->pr_npagefree);
995 	PRWORD(ovflw, " %*d", 6, 1, pp->pr_npages);
996 	PRWORD(ovflw, " %*d", 6, 1, pp->pr_hiwat);
997 	PRWORD(ovflw, " %*d", 6, 1, pp->pr_minpages);
998 	PRWORD(ovflw, " %*s", 6, 1, maxp);
999 	PRWORD(ovflw, " %*lu\n", 5, 1, pp->pr_nidle);
1000 }
1001 
1002 static void dopool_kvm(void);
1003 static void dopool_sysctl(void);
1004 
1005 void
1006 dopool(void)
1007 {
1008 	if (nlistf == NULL && memf == NULL)
1009 		dopool_sysctl();
1010 	else
1011 		dopool_kvm();
1012 }
1013 
1014 void
1015 dopool_sysctl(void)
1016 {
1017 	int mib[4], npools, i, kmfp;
1018 	long total = 0, inuse = 0;
1019 	struct pool pool;
1020 	size_t size;
1021 
1022 	mib[0] = CTL_KERN;
1023 	mib[1] = KERN_POOL;
1024 	mib[2] = KERN_POOL_NPOOLS;
1025 	size = sizeof(npools);
1026 	if (sysctl(mib, 3, &npools, &size, NULL, 0) < 0) {
1027 		warn("can't figure out number of pools in kernel");
1028 		return;
1029 	}
1030 
1031 	for (i = 1; npools; i++) {
1032 		char name[32];
1033 
1034 		mib[0] = CTL_KERN;
1035 		mib[1] = KERN_POOL;
1036 		mib[2] = KERN_POOL_POOL;
1037 		mib[3] = i;
1038 		size = sizeof(struct pool);
1039 		if (sysctl(mib, 4, &pool, &size, NULL, 0) < 0) {
1040 			if (errno == ENOENT)
1041 				continue;
1042 			warn("error getting pool");
1043 			return;
1044 		}
1045 		npools--;
1046 		mib[2] = KERN_POOL_NAME;
1047 		size = sizeof(name);
1048 		if (sysctl(mib, 4, &name, &size, NULL, 0) < 0) {
1049 			warn("error getting pool name");
1050 			return;
1051 		}
1052 		print_pool(&pool, name);
1053 
1054 		inuse += (pool.pr_nget - pool.pr_nput) * pool.pr_size;
1055 		total += pool.pr_npages * getpagesize();	/* XXX */
1056 	}
1057 
1058 	inuse /= 1024;
1059 	total /= 1024;
1060 	if (nlistf == NULL && memf == NULL) {
1061 		int mib[] = { CTL_VM, VM_KMPAGESFREE };
1062 		size_t size = sizeof(kmfp);
1063 
1064 		if (sysctl(mib, 2, &kmfp, &size, NULL, 0) < 0) {
1065 			warn("could not read uvm.kmpagesfree");
1066 			return;
1067 		}
1068 	} else {
1069 		kread(X_KMPAGESFREE, &kmfp, sizeof(kmfp));
1070 	}
1071 	total += kmfp * (getpagesize() / 1024);
1072 	printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n",
1073 	    inuse, total, (double)(100 * inuse) / total);
1074 }
1075 
1076 void
1077 dopool_kvm(void)
1078 {
1079 	TAILQ_HEAD(,pool) pool_head;
1080 	struct pool pool, *pp = &pool;
1081 	long total = 0, inuse = 0;
1082 	u_long addr;
1083 	int kmfp;
1084 
1085 	kread(X_POOLHEAD, &pool_head, sizeof(pool_head));
1086 	addr = (u_long)TAILQ_FIRST(&pool_head);
1087 
1088 	while (addr != 0) {
1089 		char name[32];
1090 
1091 		if (kvm_read(kd, addr, (void *)pp, sizeof *pp) != sizeof *pp) {
1092 			(void)fprintf(stderr,
1093 			    "vmstat: pool chain trashed: %s\n",
1094 			    kvm_geterr(kd));
1095 			exit(1);
1096 		}
1097 		if (kvm_read(kd, (u_long)pp->pr_wchan, name, sizeof name) < 0) {
1098 			(void)fprintf(stderr,
1099 			    "vmstat: pool name trashed: %s\n",
1100 			    kvm_geterr(kd));
1101 			exit(1);
1102 		}
1103 
1104 		name[31] = '\0';
1105 
1106 		print_pool(pp, name);
1107 
1108 		inuse += (pp->pr_nget - pp->pr_nput) * pp->pr_size;
1109 		total += pp->pr_npages * getpagesize();	/* XXX */
1110 
1111 		addr = (u_long)TAILQ_NEXT(pp, pr_poollist);
1112 	}
1113 
1114 	inuse /= 1024;
1115 	total /= 1024;
1116 	kread(X_KMPAGESFREE, &kmfp, sizeof(kmfp));
1117 	total += kmfp * (getpagesize() / 1024);
1118 	printf("\nIn use %ldK, total allocated %ldK; utilization %.1f%%\n",
1119 	    inuse, total, (double)(100 * inuse) / total);
1120 }
1121 
1122 /*
1123  * kread reads something from the kernel, given its nlist index.
1124  */
1125 void
1126 kread(int nlx, void *addr, size_t size)
1127 {
1128 	char *sym;
1129 
1130 	if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
1131 		sym = namelist[nlx].n_name;
1132 		if (*sym == '_')
1133 			++sym;
1134 		errx(1, "symbol %s not defined", sym);
1135 	}
1136 	if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) {
1137 		sym = namelist[nlx].n_name;
1138 		if (*sym == '_')
1139 			++sym;
1140 		errx(1, "%s: %s", sym, kvm_geterr(kd));
1141 	}
1142 }
1143 
1144 void
1145 usage(void)
1146 {
1147 	(void)fprintf(stderr, "usage: %s [-fimstvz] [-c count] [-M core] "
1148 	    "[-N system] [-w wait] [disk ...]\n", __progname);
1149 	exit(1);
1150 }
1151