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