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