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