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