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