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