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