xref: /dflybsd-src/usr.bin/vmstat/vmstat.c (revision 38b5d46cbbb58bd340296ebede89d5f6b4838f4f)
1 /*
2  * Copyright (c) 1980, 1986, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1980, 1986, 1991, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)vmstat.c	8.1 (Berkeley) 6/6/93
31  * $FreeBSD: src/usr.bin/vmstat/vmstat.c,v 1.38.2.4 2001/07/31 19:52:41 tmm Exp $
32  */
33 
34 #include <sys/user.h>
35 #include <sys/param.h>
36 #include <sys/time.h>
37 #include <sys/uio.h>
38 #include <sys/namei.h>
39 #include <sys/malloc.h>
40 #include <sys/signal.h>
41 #include <sys/fcntl.h>
42 #include <sys/ioctl.h>
43 #include <sys/sysctl.h>
44 #include <sys/vmmeter.h>
45 #include <sys/interrupt.h>
46 
47 #include <vm/vm_param.h>
48 #include <vm/vm_zone.h>
49 
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <kinfo.h>
54 #include <kvm.h>
55 #include <limits.h>
56 #include <nlist.h>
57 #include <paths.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <sysexits.h>
62 #include <time.h>
63 #include <unistd.h>
64 #include <devstat.h>
65 
66 static struct nlist namelist[] = {
67 #define	X_BOOTTIME	0
68 	{ "_boottime",	0, 0, 0, 0 },
69 #define X_NCHSTATS	1
70 	{ "_nchstats",	0, 0, 0, 0 },
71 #define	X_KMEMSTATISTICS	2
72 	{ "_kmemstatistics",	0, 0, 0, 0 },
73 #define	X_ZLIST		3
74 	{ "_zlist",	0, 0, 0, 0 },
75 #ifdef notyet
76 #define	X_DEFICIT	4
77 	{ "_deficit",	0, 0, 0, 0 },
78 #define	X_FORKSTAT	5
79 	{ "_forkstat",	0, 0, 0, 0 },
80 #define X_REC		6
81 	{ "_rectime",	0, 0, 0, 0 },
82 #define X_PGIN		7
83 	{ "_pgintime",	0, 0, 0, 0 },
84 #define	X_XSTATS	8
85 	{ "_xstats",	0, 0, 0, 0 },
86 #define X_END		9
87 #else
88 #define X_END		4
89 #endif
90 	{ "", 0, 0, 0, 0 },
91 };
92 
93 #define ONEMB	(1024L * 1024L)
94 #define ONEKB	(1024L)
95 
96 LIST_HEAD(zlist, vm_zone);
97 
98 struct statinfo cur, last;
99 int num_devices, maxshowdevs;
100 long generation;
101 struct device_selection *dev_select;
102 int num_selected;
103 struct devstat_match *matches;
104 int num_matches = 0;
105 int num_devices_specified, num_selections;
106 long select_generation;
107 char **specified_devices;
108 devstat_select_mode select_mode;
109 
110 struct	vmmeter vmm, ovmm;
111 struct	vmstats vms, ovms;
112 
113 int	winlines = 20;
114 int	nflag = 0;
115 int	verbose = 0;
116 
117 kvm_t *kd;
118 
119 struct kinfo_cputime cp_time, old_cp_time, diff_cp_time;
120 
121 #define	FORKSTAT	0x01
122 #define	INTRSTAT	0x02
123 #define	MEMSTAT		0x04
124 #define	SUMSTAT		0x08
125 #define	TIMESTAT	0x10
126 #define	VMSTAT		0x20
127 #define ZMEMSTAT	0x40
128 
129 static void cpustats(void);
130 static void dointr(void);
131 static void domem(void);
132 static void dosum(void);
133 static void dozmem(u_int interval, int reps);
134 static void dovmstat(u_int, int);
135 static void kread(int, void *, size_t);
136 static void usage(void);
137 static char **getdrivedata(char **);
138 static long getuptime(void);
139 static void needhdr(int);
140 static long pct(long, long);
141 
142 #ifdef notyet
143 static void dotimes(void); /* Not implemented */
144 static void doforkst(void);
145 #endif
146 static void printhdr(void);
147 static const char *formatnum(intmax_t value, int width);
148 static void devstats(int dooutput);
149 
150 int
151 main(int argc, char **argv)
152 {
153 	int c, todo;
154 	u_int interval;		/* milliseconds */
155 	int reps;
156 	char *memf, *nlistf;
157 	char errbuf[_POSIX2_LINE_MAX];
158 
159 	memf = nlistf = NULL;
160 	interval = reps = todo = 0;
161 	maxshowdevs = 2;
162 	while ((c = getopt(argc, argv, "c:fiM:mN:n:p:stvw:z")) != -1) {
163 		switch (c) {
164 		case 'c':
165 			reps = atoi(optarg);
166 			break;
167 		case 'f':
168 #ifdef notyet
169 			todo |= FORKSTAT;
170 #else
171 			errx(EX_USAGE, "sorry, -f is not (re)implemented yet");
172 #endif
173 			break;
174 		case 'i':
175 			todo |= INTRSTAT;
176 			break;
177 		case 'M':
178 			memf = optarg;
179 			break;
180 		case 'm':
181 			todo |= MEMSTAT;
182 			break;
183 		case 'N':
184 			nlistf = optarg;
185 			break;
186 		case 'n':
187 			nflag = 1;
188 			maxshowdevs = atoi(optarg);
189 			if (maxshowdevs < 0)
190 				errx(1, "number of devices %d is < 0",
191 				     maxshowdevs);
192 			break;
193 		case 'p':
194 			if (buildmatch(optarg, &matches, &num_matches) != 0)
195 				errx(1, "%s", devstat_errbuf);
196 			break;
197 		case 's':
198 			todo |= SUMSTAT;
199 			break;
200 		case 't':
201 #ifdef notyet
202 			todo |= TIMESTAT;
203 #else
204 			errx(EX_USAGE, "sorry, -t is not (re)implemented yet");
205 #endif
206 			break;
207 		case 'v':
208 			++verbose;
209 			break;
210 		case 'w':
211 			interval = (u_int)(strtod(optarg, NULL) * 1000.0);
212 			break;
213 		case 'z':
214 			todo |= ZMEMSTAT;
215 			break;
216 		default:
217 			usage();
218 		}
219 	}
220 	argc -= optind;
221 	argv += optind;
222 
223 	if (todo == 0)
224 		todo = VMSTAT;
225 
226 	/*
227 	 * Discard setgid privileges if not the running kernel so that bad
228 	 * guys can't print interesting stuff from kernel memory.
229 	 */
230 	if (nlistf != NULL || memf != NULL)
231 		setgid(getgid());
232 
233 	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
234 	if (kd == NULL)
235 		errx(1, "kvm_openfiles: %s", errbuf);
236 
237 	if ((c = kvm_nlist(kd, namelist)) != 0) {
238 		if (c > 0) {
239 			warnx("undefined symbols:");
240 			for (c = 0; c < (int)__arysize(namelist); c++)
241 				if (namelist[c].n_type == 0)
242 					fprintf(stderr, " %s",
243 					    namelist[c].n_name);
244 			fputc('\n', stderr);
245 		} else
246 			warnx("kvm_nlist: %s", kvm_geterr(kd));
247 		exit(1);
248 	}
249 
250 	if (todo & VMSTAT) {
251 		struct winsize winsize;
252 
253 		/*
254 		 * Make sure that the userland devstat version matches the
255 		 * kernel devstat version.  If not, exit and print a
256 		 * message informing the user of his mistake.
257 		 */
258 		if (checkversion() < 0)
259 			errx(1, "%s", devstat_errbuf);
260 
261 
262 		argv = getdrivedata(argv);
263 		winsize.ws_row = 0;
264 		ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
265 		if (winsize.ws_row > 0)
266 			winlines = winsize.ws_row;
267 
268 	}
269 
270 #define	BACKWARD_COMPATIBILITY
271 #ifdef	BACKWARD_COMPATIBILITY
272 	if (*argv) {
273 		interval = (u_int)(strtod(*argv, NULL) * 1000.0);
274 		if (*++argv)
275 			reps = atoi(*argv);
276 	}
277 #endif
278 
279 	if (interval) {
280 		if (!reps)
281 			reps = -1;
282 	} else if (reps) {
283 		interval = 1000;
284 	}
285 
286 #ifdef notyet
287 	if (todo & FORKSTAT)
288 		doforkst();
289 #endif
290 	if (todo & MEMSTAT)
291 		domem();
292 	if (todo & ZMEMSTAT)
293 		dozmem(interval, reps);
294 	if (todo & SUMSTAT)
295 		dosum();
296 #ifdef notyet
297 	if (todo & TIMESTAT)
298 		dotimes();
299 #endif
300 	if (todo & INTRSTAT)
301 		dointr();
302 	if (todo & VMSTAT)
303 		dovmstat(interval, reps);
304 	exit(0);
305 }
306 
307 static char **
308 getdrivedata(char **argv)
309 {
310 	if ((num_devices = getnumdevs()) < 0)
311 		errx(1, "%s", devstat_errbuf);
312 
313 	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
314 	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
315 	bzero(cur.dinfo, sizeof(struct devinfo));
316 	bzero(last.dinfo, sizeof(struct devinfo));
317 
318 	if (getdevs(&cur) == -1)
319 		errx(1, "%s", devstat_errbuf);
320 
321 	num_devices = cur.dinfo->numdevs;
322 	generation = cur.dinfo->generation;
323 
324 	specified_devices = (char **)malloc(sizeof(char *));
325 	for (num_devices_specified = 0; *argv; ++argv) {
326 		if (isdigit(**argv))
327 			break;
328 		num_devices_specified++;
329 		specified_devices = (char **)realloc(specified_devices,
330 						     sizeof(char *) *
331 						     num_devices_specified);
332 		specified_devices[num_devices_specified - 1] = *argv;
333 	}
334 	dev_select = NULL;
335 
336 	if (nflag == 0 && maxshowdevs < num_devices_specified)
337 			maxshowdevs = num_devices_specified;
338 
339 	/*
340 	 * People are generally only interested in disk statistics when
341 	 * they're running vmstat.  So, that's what we're going to give
342 	 * them if they don't specify anything by default.  We'll also give
343 	 * them any other random devices in the system so that we get to
344 	 * maxshowdevs devices, if that many devices exist.  If the user
345 	 * specifies devices on the command line, either through a pattern
346 	 * match or by naming them explicitly, we will give the user only
347 	 * those devices.
348 	 */
349 	if ((num_devices_specified == 0) && (num_matches == 0)) {
350 		if (buildmatch("da", &matches, &num_matches) != 0)
351 			errx(1, "%s", devstat_errbuf);
352 
353 		select_mode = DS_SELECT_ADD;
354 	} else
355 		select_mode = DS_SELECT_ONLY;
356 
357 	/*
358 	 * At this point, selectdevs will almost surely indicate that the
359 	 * device list has changed, so we don't look for return values of 0
360 	 * or 1.  If we get back -1, though, there is an error.
361 	 */
362 	if (selectdevs(&dev_select, &num_selected, &num_selections,
363 		       &select_generation, generation, cur.dinfo->devices,
364 		       num_devices, matches, num_matches, specified_devices,
365 		       num_devices_specified, select_mode,
366 		       maxshowdevs, 0) == -1)
367 		errx(1, "%s", devstat_errbuf);
368 
369 	return(argv);
370 }
371 
372 static long
373 getuptime(void)
374 {
375 	static time_t now, boottime;
376 	time_t uptime;
377 
378 	if (boottime == 0)
379 		kread(X_BOOTTIME, &boottime, sizeof(boottime));
380 	time(&now);
381 	uptime = now - boottime;
382 	if (uptime <= 0 || uptime > 60*60*24*365*10)
383 		errx(1, "time makes no sense; namelist must be wrong");
384 	return(uptime);
385 }
386 
387 int	hdrcnt;
388 
389 static void
390 dovmstat(u_int interval, int reps)
391 {
392 	struct vmtotal total;
393 	struct devinfo *tmp_dinfo;
394 	size_t vmm_size = sizeof(vmm);
395 	size_t vms_size = sizeof(vms);
396 	size_t vmt_size = sizeof(total);
397 	int initial = 1;
398 	int dooutput = 1;
399 
400 	signal(SIGCONT, needhdr);
401 	if (reps != 0)
402 		dooutput = 0;
403 
404 	for (hdrcnt = 1;;) {
405 		if (!--hdrcnt)
406 			printhdr();
407 		if (kinfo_get_sched_cputime(&cp_time))
408 			err(1, "kinfo_get_sched_cputime");
409 
410 		tmp_dinfo = last.dinfo;
411 		last.dinfo = cur.dinfo;
412 		cur.dinfo = tmp_dinfo;
413 		last.busy_time = cur.busy_time;
414 
415 		/*
416 		 * Here what we want to do is refresh our device stats.
417 		 * getdevs() returns 1 when the device list has changed.
418 		 * If the device list has changed, we want to go through
419 		 * the selection process again, in case a device that we
420 		 * were previously displaying has gone away.
421 		 */
422 		switch (getdevs(&cur)) {
423 		case -1:
424 			errx(1, "%s", devstat_errbuf);
425 			break;
426 		case 1: {
427 			int retval;
428 
429 			num_devices = cur.dinfo->numdevs;
430 			generation = cur.dinfo->generation;
431 
432 			retval = selectdevs(&dev_select, &num_selected,
433 					    &num_selections, &select_generation,
434 					    generation, cur.dinfo->devices,
435 					    num_devices, matches, num_matches,
436 					    specified_devices,
437 					    num_devices_specified, select_mode,
438 					    maxshowdevs, 0);
439 			switch (retval) {
440 			case -1:
441 				errx(1, "%s", devstat_errbuf);
442 				break;
443 			case 1:
444 				printhdr();
445 				break;
446 			default:
447 				break;
448 			}
449 		}
450 		default:
451 			break;
452 		}
453 
454 		if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) {
455 			perror("sysctlbyname: vm.vmstats");
456 			exit(1);
457 		}
458 		if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) {
459 			perror("sysctlbyname: vm.vmmeter");
460 			exit(1);
461 		}
462 		if (sysctlbyname("vm.vmtotal", &total, &vmt_size, NULL, 0)) {
463 			perror("sysctlbyname: vm.vmtotal");
464 			exit(1);
465 		}
466 		if (dooutput) {
467 			printf("%3ld %2ld %2ld",
468 			       total.t_rq - 1,
469 			       total.t_dw + total.t_pw,
470 			       total.t_sw);
471 		}
472 
473 #define rate(x)		\
474 	(intmax_t)(initial ? (x) : ((intmax_t)(x) * 1000 + interval / 2) \
475 				   / interval)
476 
477 		if (dooutput) {
478 			printf(" %s ",
479 			       formatnum((int64_t)total.t_free *
480 					 vms.v_page_size, 4));
481 			printf("%s ",
482 			       formatnum(rate(vmm.v_vm_faults -
483 					      ovmm.v_vm_faults), 5));
484 			printf("%s ",
485 			       formatnum(rate(vmm.v_reactivated -
486 					      ovmm.v_reactivated), 4));
487 			printf("%s ",
488 			       formatnum(rate(vmm.v_swapin + vmm.v_vnodein -
489 				      (ovmm.v_swapin + ovmm.v_vnodein)), 4));
490 			printf("%s ",
491 			       formatnum(rate(vmm.v_swapout + vmm.v_vnodeout -
492 				    (ovmm.v_swapout + ovmm.v_vnodeout)), 4));
493 			printf("%s ",
494 			       formatnum(rate(vmm.v_tfree -
495 					      ovmm.v_tfree), 4));
496 		}
497 		devstats(dooutput);
498 		if (dooutput) {
499 			printf("%s ",
500 			       formatnum(rate(vmm.v_intr -
501 					      ovmm.v_intr), 5));
502 			printf("%s ",
503 			       formatnum(rate(vmm.v_syscall -
504 					      ovmm.v_syscall), 5));
505 			printf("%s ",
506 			       formatnum(rate(vmm.v_swtch -
507 					      ovmm.v_swtch), 5));
508 			cpustats();
509 			printf("\n");
510 			fflush(stdout);
511 		}
512 		if (reps >= 0 && --reps <= 0)
513 			break;
514 		ovmm = vmm;
515 		usleep(interval * 1000);
516 		initial = 0;
517 		dooutput = 1;
518 	}
519 }
520 
521 static const char *
522 formatnum(intmax_t value, int width)
523 {
524 	static char buf[64];
525 	const char *fmt;
526 	double d;
527 
528 	d = (double)value;
529 	fmt = "n/a";
530 
531 	switch(width) {
532 	case 4:
533 		if (value < 1024) {
534 			fmt = "%4.0f";
535 		} else if (value < 10*1024) {
536 			fmt = "%3.1fK";
537 			d = d / 1024;
538 		} else if (value < 1000*1024) {
539 			fmt = "%3.0fK";
540 			d = d / 1024;
541 		} else if (value < 10*1024*1024) {
542 			fmt = "%3.1fM";
543 			d = d / (1024 * 1024);
544 		} else if (value < 1000*1024*1024) {
545 			fmt = "%3.0fM";
546 			d = d / (1024 * 1024);
547 		} else {
548 			fmt = "%3.1fG";
549 			d = d / (1024.0 * 1024.0 * 1024.0);
550 		}
551 		break;
552 	case 5:
553 		if (value < 1024) {
554 			fmt = "%5.0f";
555 		} else if (value < 10*1024) {
556 			fmt = "%4.2fK";
557 			d = d / 1024;
558 		} else if (value < 1000*1024) {
559 			fmt = "%4.0fK";
560 			d = d / 1024;
561 		} else if (value < 10*1024*1024) {
562 			fmt = "%4.2fM";
563 			d = d / (1024 * 1024);
564 		} else if (value < 1000*1024*1024) {
565 			fmt = "%4.0fM";
566 			d = d / (1024 * 1024);
567 		} else {
568 			fmt = "%4.2fG";
569 			d = d / (1024.0 * 1024.0 * 1024.0);
570 		}
571 		break;
572 	default:
573 		fprintf(stderr, "formatnum: unsupported width %d\n", width);
574 		exit(1);
575 		break;
576 	}
577 	snprintf(buf, sizeof(buf), fmt, d);
578 	return buf;
579 }
580 
581 static void
582 printhdr(void)
583 {
584 	int i, num_shown;
585 
586 	num_shown = (num_selected < maxshowdevs) ? num_selected : maxshowdevs;
587 	printf("--procs-- ---memory-- -------paging------ ");
588 	if (num_shown > 1)
589 		printf("--disks%.*s",
590 		       num_shown * 4 - 6,
591 		       "---------------------------------");
592 	else if (num_shown == 1)
593 		printf("disk");
594 	printf(" -----faults------ ---cpu---\n");
595 	printf("  r  b  w   fre   flt   re   pi   po   fr ");
596 	for (i = 0; i < num_devices; i++)
597 		if ((dev_select[i].selected)
598 		 && (dev_select[i].selected <= maxshowdevs))
599 			printf(" %c%c%d ", dev_select[i].device_name[0],
600 				     dev_select[i].device_name[1],
601 				     dev_select[i].unit_number);
602 	printf("  int   sys   ctx us sy id\n");
603 	hdrcnt = winlines - 2;
604 }
605 
606 /*
607  * Force a header to be prepended to the next output.
608  */
609 static void
610 needhdr(__unused int signo)
611 {
612 
613 	hdrcnt = 1;
614 }
615 
616 static long
617 pct(long top, long bot)
618 {
619 	long ans;
620 
621 	if (bot == 0)
622 		return(0);
623 	ans = (quad_t)top * 100 / bot;
624 	return (ans);
625 }
626 
627 #define	PCT(top, bot) pct((long)(top), (long)(bot))
628 
629 static void
630 dosum(void)
631 {
632 	struct nchstats *nch_tmp, nchstats;
633 	size_t vms_size = sizeof(vms);
634 	size_t vmm_size = sizeof(vmm);
635 	int cpucnt;
636 	u_long nchtotal;
637 	u_long nchpathtotal;
638 	size_t nch_size = sizeof(struct nchstats) * SMP_MAXCPU;
639 
640 	if (sysctlbyname("vm.vmstats", &vms, &vms_size, NULL, 0)) {
641 		perror("sysctlbyname: vm.vmstats");
642 		exit(1);
643 	}
644 	if (sysctlbyname("vm.vmmeter", &vmm, &vmm_size, NULL, 0)) {
645 		perror("sysctlbyname: vm.vmstats");
646 		exit(1);
647 	}
648 	printf("%9u cpu context switches\n", vmm.v_swtch);
649 	printf("%9u device interrupts\n", vmm.v_intr);
650 	printf("%9u software interrupts\n", vmm.v_soft);
651 	printf("%9u traps\n", vmm.v_trap);
652 	printf("%9u system calls\n", vmm.v_syscall);
653 	printf("%9u kernel threads created\n", vmm.v_kthreads);
654 	printf("%9u  fork() calls\n", vmm.v_forks);
655 	printf("%9u vfork() calls\n", vmm.v_vforks);
656 	printf("%9u rfork() calls\n", vmm.v_rforks);
657 	printf("%9u exec() calls\n", vmm.v_exec);
658 	printf("%9u swap pager pageins\n", vmm.v_swapin);
659 	printf("%9u swap pager pages paged in\n", vmm.v_swappgsin);
660 	printf("%9u swap pager pageouts\n", vmm.v_swapout);
661 	printf("%9u swap pager pages paged out\n", vmm.v_swappgsout);
662 	printf("%9u vnode pager pageins\n", vmm.v_vnodein);
663 	printf("%9u vnode pager pages paged in\n", vmm.v_vnodepgsin);
664 	printf("%9u vnode pager pageouts\n", vmm.v_vnodeout);
665 	printf("%9u vnode pager pages paged out\n", vmm.v_vnodepgsout);
666 	printf("%9u page daemon wakeups\n", vmm.v_pdwakeups);
667 	printf("%9u pages examined by the page daemon\n", vmm.v_pdpages);
668 	printf("%9u pages reactivated\n", vmm.v_reactivated);
669 	printf("%9u copy-on-write faults\n", vmm.v_cow_faults);
670 	printf("%9u copy-on-write optimized faults\n", vmm.v_cow_optim);
671 	printf("%9u zero fill pages zeroed\n", vmm.v_zfod);
672 	printf("%9u zero fill pages prezeroed\n", vmm.v_ozfod);
673 	printf("%9u intransit blocking page faults\n", vmm.v_intrans);
674 	printf("%9u total VM faults taken\n", vmm.v_vm_faults);
675 	printf("%9u pages affected by kernel thread creation\n", vmm.v_kthreadpages);
676 	printf("%9u pages affected by  fork()\n", vmm.v_forkpages);
677 	printf("%9u pages affected by vfork()\n", vmm.v_vforkpages);
678 	printf("%9u pages affected by rfork()\n", vmm.v_rforkpages);
679 	printf("%9u pages freed\n", vmm.v_tfree);
680 	printf("%9u pages freed by daemon\n", vmm.v_dfree);
681 	printf("%9u pages freed by exiting processes\n", vmm.v_pfree);
682 	printf("%9u pages active\n", vms.v_active_count);
683 	printf("%9u pages inactive\n", vms.v_inactive_count);
684 	printf("%9u pages in VM cache\n", vms.v_cache_count);
685 	printf("%9u pages wired down\n", vms.v_wire_count);
686 	printf("%9u pages free\n", vms.v_free_count);
687 	printf("%9u bytes per page\n", vms.v_page_size);
688 	printf("%9u global smp invltlbs\n", vmm.v_smpinvltlb);
689 
690 	if ((nch_tmp = malloc(nch_size)) == NULL) {
691 		perror("malloc");
692 		exit(1);
693 	} else {
694 		if (sysctlbyname("vfs.cache.nchstats", nch_tmp, &nch_size, NULL, 0)) {
695 			perror("sysctlbyname vfs.cache.nchstats");
696 			free(nch_tmp);
697 			exit(1);
698 		} else {
699 			if ((nch_tmp = realloc(nch_tmp, nch_size)) == NULL) {
700 				perror("realloc");
701 				exit(1);
702 			}
703 		}
704 	}
705 
706 	cpucnt = nch_size / sizeof(struct nchstats);
707 	kvm_nch_cpuagg(nch_tmp, &nchstats, cpucnt);
708 
709 	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
710 	    nchstats.ncs_badhits + nchstats.ncs_falsehits +
711 	    nchstats.ncs_miss;
712 	nchpathtotal = nchstats.ncs_longhits + nchstats.ncs_longmiss;
713 	printf("%9ld total path lookups\n", nchpathtotal);
714 	printf("%9ld total component lookups\n", nchtotal);
715 	printf(
716 	    "%9s cache hits (%ld%% pos + %ld%% neg)\n",
717 	    "", PCT(nchstats.ncs_goodhits, nchtotal),
718 	    PCT(nchstats.ncs_neghits, nchtotal));
719 	printf("%9s deletions %ld%%, falsehits %ld%%\n", "",
720 	    PCT(nchstats.ncs_badhits, nchtotal),
721 	    PCT(nchstats.ncs_falsehits, nchtotal));
722 	free(nch_tmp);
723 }
724 
725 #ifdef notyet
726 void
727 doforkst(void)
728 {
729 	struct forkstat fks;
730 
731 	kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
732 	printf("%d forks, %d pages, average %.2f\n",
733 	    fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
734 	printf("%d vforks, %d pages, average %.2f\n",
735 	    fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork);
736 }
737 #endif
738 
739 static void
740 devstats(int dooutput)
741 {
742 	int dn;
743 	long double transfers_per_second;
744 	long double busy_seconds;
745 
746 	diff_cp_time.cp_user = cp_time.cp_user - old_cp_time.cp_user;
747 	diff_cp_time.cp_nice = cp_time.cp_nice - old_cp_time.cp_nice;
748 	diff_cp_time.cp_sys = cp_time.cp_sys - old_cp_time.cp_sys;
749 	diff_cp_time.cp_intr = cp_time.cp_intr - old_cp_time.cp_intr;
750 	diff_cp_time.cp_idle = cp_time.cp_idle - old_cp_time.cp_idle;
751 	old_cp_time = cp_time;
752 
753 	busy_seconds = compute_etime(cur.busy_time, last.busy_time);
754 
755 	for (dn = 0; dn < num_devices; dn++) {
756 		int di;
757 
758 		if ((dev_select[dn].selected == 0)
759 		 || (dev_select[dn].selected > maxshowdevs))
760 			continue;
761 
762 		di = dev_select[dn].position;
763 
764 		if (compute_stats(&cur.dinfo->devices[di],
765 				  &last.dinfo->devices[di], busy_seconds,
766 				  NULL, NULL, NULL,
767 				  NULL, &transfers_per_second, NULL,
768 				  NULL, NULL) != 0)
769 			errx(1, "%s", devstat_errbuf);
770 
771 		if (dooutput)
772 			printf("%s ", formatnum(transfers_per_second, 4));
773 	}
774 }
775 
776 static void
777 cpustats(void)
778 {
779 	uint64_t total;
780 	double totusage;
781 
782 	total = diff_cp_time.cp_user + diff_cp_time.cp_nice +
783 	    diff_cp_time.cp_sys + diff_cp_time.cp_intr + diff_cp_time.cp_idle;
784 
785 	if (total)
786 		totusage = 100.0 / total;
787 	else
788 		totusage = 0;
789 	printf("%2.0f ",
790 	       (diff_cp_time.cp_user + diff_cp_time.cp_nice) * totusage);
791 	printf("%2.0f ",
792 	       (diff_cp_time.cp_sys + diff_cp_time.cp_intr) * totusage);
793 	printf("%2.0f",
794 	       diff_cp_time.cp_idle * totusage);
795 }
796 
797 static void
798 dointr(void)
799 {
800 	u_long *intrcnt, uptime;
801 	u_int64_t inttotal;
802 	size_t nintr, inamlen, i, size;
803 	int nwidth;
804 	char *intrstr;
805 	char **intrname;
806 
807 	uptime = getuptime();
808 	if (sysctlbyname("hw.intrnames", NULL, &inamlen, NULL, 0) != 0)
809 		errx(1, "sysctlbyname");
810 	intrstr = malloc(inamlen);
811 	if (intrstr == NULL)
812 		err(1, "malloc");
813 	sysctlbyname("hw.intrnames", intrstr, &inamlen, NULL, 0);
814 	for (nintr = 0, i = 0; i < inamlen; ++i) {
815 		if (intrstr[i] == 0)
816 			nintr++;
817 	}
818 	intrname = malloc(nintr * sizeof(char *));
819 	for (i = 0; i < nintr; ++i) {
820 		intrname[i] = intrstr;
821 		intrstr += strlen(intrstr) + 1;
822 	}
823 
824 	size = nintr * sizeof(*intrcnt);
825 	intrcnt = calloc(nintr, sizeof(*intrcnt));
826 	if (intrcnt == NULL)
827 		err(1, "malloc");
828 	sysctlbyname("hw.intrcnt", intrcnt, &size, NULL, 0);
829 
830 	nwidth = 21;
831 	for (i = 0; i < nintr; ++i) {
832 		if (nwidth < (int)strlen(intrname[i]))
833 			nwidth = (int)strlen(intrname[i]);
834 	}
835 	if (verbose) nwidth += 12;
836 
837 	printf("%-*.*s %11s %10s\n",
838 		nwidth, nwidth, "interrupt", "total", "rate");
839 	inttotal = 0;
840 	for (i = 0; i < nintr; ++i) {
841 		int named;
842 		char *infop, irqinfo[72];
843 
844 		if ((named = strncmp(intrname[i], "irq", 3)) != 0 ||
845 		    intrcnt[i] > 0) {
846 			infop = intrname[i];
847 			if (verbose) {
848 				ssize_t irq, cpu;
849 
850 				irq = i % MAX_INTS;
851 				cpu = i / MAX_INTS;
852 				if (named) {
853 					snprintf(irqinfo, sizeof(irqinfo),
854 						 "irq%-3zd %3zd: %s",
855 						 irq, cpu, intrname[i]);
856 				} else {
857 					snprintf(irqinfo, sizeof(irqinfo),
858 						 "irq%-3zd %3zd: ", irq, cpu);
859 				}
860 				infop = irqinfo;
861 			}
862 			printf("%-*.*s %11lu %10lu\n",
863 				nwidth, nwidth, infop,
864 				intrcnt[i], intrcnt[i] / uptime);
865 		}
866 		inttotal += intrcnt[i];
867 	}
868 	printf("%-*.*s %11llu %10llu\n",
869 		nwidth, nwidth, "Total",
870 		(long long)inttotal, (long long)(inttotal / uptime));
871 }
872 
873 #define	MAX_KMSTATS	1024
874 
875 enum ksuse { KSINUSE, KSMEMUSE };
876 
877 static long
878 cpuagg(struct malloc_type *ks, enum ksuse use)
879 {
880     int i;
881     long ttl;
882 
883     ttl = 0;
884 
885     switch(use) {
886     case KSINUSE:
887 	for (i = 0; i < SMP_MAXCPU; ++i)
888 	    ttl += ks->ks_use[i].inuse;
889 	break;
890     case KSMEMUSE:
891 	for (i = 0; i < SMP_MAXCPU; ++i)
892 	    ttl += ks->ks_use[i].memuse;
893 	break;
894     }
895     return(ttl);
896 }
897 
898 static void
899 domem(void)
900 {
901 	struct malloc_type *ks;
902 	int i, j;
903 	int first, nkms;
904 	long totuse = 0, totfree = 0, totreq = 0;
905 	struct malloc_type kmemstats[MAX_KMSTATS], *kmsp;
906 	char buf[1024];
907 
908 	kread(X_KMEMSTATISTICS, &kmsp, sizeof(kmsp));
909 	for (nkms = 0; nkms < MAX_KMSTATS && kmsp != NULL; nkms++) {
910 		if (sizeof(kmemstats[0]) != kvm_read(kd, (u_long)kmsp,
911 		    &kmemstats[nkms], sizeof(kmemstats[0])))
912 			err(1, "kvm_read(%p)", (void *)kmsp);
913 		if (sizeof(buf) !=  kvm_read(kd,
914 	            (u_long)kmemstats[nkms].ks_shortdesc, buf, sizeof(buf)))
915 			err(1, "kvm_read(%p)",
916 			    kmemstats[nkms].ks_shortdesc);
917 		buf[sizeof(buf) - 1] = '\0';
918 		kmemstats[nkms].ks_shortdesc = strdup(buf);
919 		kmsp = kmemstats[nkms].ks_next;
920 	}
921 	if (kmsp != NULL)
922 		warnx("truncated to the first %d memory types", nkms);
923 
924 	printf(
925 	    "\nMemory statistics by type                          Type  Kern\n");
926 	printf(
927 "              Type   InUse  MemUse HighUse       Limit  Requests  Limit Limit\n");
928 	for (i = 0, ks = &kmemstats[0]; i < nkms; i++, ks++) {
929 		long ks_inuse;
930 		long ks_memuse;
931 
932 		if (ks->ks_calls == 0)
933 			continue;
934 
935 		ks_inuse = cpuagg(ks, KSINUSE);
936 		ks_memuse = cpuagg(ks, KSMEMUSE);
937 
938 		if (ks->ks_maxused > 99*ONEMB ||
939 		    ks_inuse > 99*ONEMB ||
940 		    ks_memuse > 99*ONEMB) {
941 			printf("%19s%7ld%7ldM%7ldM%11zuM%10jd%5u%6u",
942 			    ks->ks_shortdesc,
943 			    ks_inuse, (ks_memuse + ONEMB - 1) / ONEMB,
944 			    (ks->ks_maxused + ONEMB - 1) / ONEMB,
945 			    (ks->ks_limit + ONEMB - 1) / ONEMB,
946 			    (intmax_t)ks->ks_calls,
947 			    ks->ks_limblocks, ks->ks_mapblocks);
948 		} else {
949 			printf("%19s%7ld%7ldK%7ldK%11zuM%10jd%5u%6u",
950 			    ks->ks_shortdesc,
951 			    ks_inuse, (ks_memuse + ONEKB - 1) / ONEKB,
952 			    (ks->ks_maxused + ONEKB - 1) / ONEKB,
953 			    (ks->ks_limit + ONEMB) / ONEMB,
954 			    (intmax_t)ks->ks_calls,
955 			    ks->ks_limblocks, ks->ks_mapblocks);
956 		}
957 		first = 1;
958 		for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
959 			if ((ks->ks_size & j) == 0)
960 				continue;
961 			if (first)
962 				printf("  ");
963 			else
964 				printf(",");
965 			if(j<1024)
966 				printf("%d",j);
967 			else
968 				printf("%dK",j>>10);
969 			first = 0;
970 		}
971 		printf("\n");
972 		totuse += cpuagg(ks, KSMEMUSE);
973 		totreq += ks->ks_calls;
974 	}
975 	printf("\nMemory Totals:  In Use    Free    Requests\n");
976 
977 	if (totuse > 99*ONEMB || totfree > 99*ONEMB) {
978 		printf("              %7ldM %6ldM    %8ld\n",
979 		       (totuse + ONEMB - 1) / ONEMB,
980 		       (totfree + ONEMB - 1) / ONEMB, totreq);
981 	} else {
982 		printf("              %7ldK %6ldK    %8ld\n",
983 		       (totuse + ONEKB - 1) / ONEKB,
984 		       (totfree + ONEKB - 1) / ONEKB, totreq);
985 	}
986 }
987 
988 #define MAXSAVE	16
989 
990 static void
991 dozmem(u_int interval, int reps)
992 {
993 	struct zlist	zlist;
994 	struct vm_zone	*kz;
995 	struct vm_zone	zone;
996 	struct vm_zone	copy;
997 	struct vm_zone	save[MAXSAVE];
998 	char name[64];
999 	size_t namesz;
1000 	int i;
1001 	int first = 1;
1002 
1003 	bzero(save, sizeof(save));
1004 
1005 again:
1006 	kread(X_ZLIST, &zlist, sizeof(zlist));
1007 	kz = LIST_FIRST(&zlist);
1008 	i = 0;
1009 
1010 	while (kz) {
1011 		if (kvm_read(kd, (intptr_t)kz, &zone, sizeof(zone)) !=
1012 		    (ssize_t)sizeof(zone)) {
1013 			perror("kvm_read");
1014 			break;
1015 		}
1016 		copy = zone;
1017 		zone.znalloc -= save[i].znalloc;
1018 		save[i] = copy;
1019 		namesz = sizeof(name);
1020 		if (kvm_readstr(kd, (intptr_t)zone.zname, name, &namesz) == NULL) {
1021 			perror("kvm_read");
1022 			break;
1023 		}
1024 		if (first && interval) {
1025 			/* do nothing */
1026 		} else if (zone.zmax) {
1027 			printf("%-10s %9ld/%9ld %5ldM used"
1028 			       " use=%-9lu %6.2f%%\n",
1029 				name,
1030 				(long)(zone.ztotal - zone.zfreecnt),
1031 				(long)zone.zmax,
1032 				(long)(zone.ztotal - zone.zfreecnt) *
1033 					zone.zsize / (1024 * 1024),
1034 				(unsigned long)zone.znalloc,
1035 				(double)(zone.ztotal - zone.zfreecnt) *
1036 					100.0 / (double)zone.zmax);
1037 		} else {
1038 			printf("%-10s %9ld           %5ldM used"
1039 			       " use=%-9lu\n",
1040 				name,
1041 				(long)(zone.ztotal - zone.zfreecnt),
1042 				(long)(zone.ztotal - zone.zfreecnt) *
1043 					zone.zsize / (1024 * 1024),
1044 				(unsigned long)zone.znalloc);
1045 		}
1046 		kz = LIST_NEXT(&zone, zlink);
1047 		++i;
1048 	}
1049 	if (reps) {
1050 		first = 0;
1051 		fflush(stdout);
1052 		usleep(interval * 1000);
1053 		--reps;
1054 		printf("\n");
1055 		goto again;
1056 	}
1057 }
1058 
1059 /*
1060  * kread reads something from the kernel, given its nlist index.
1061  */
1062 static void
1063 kread(int nlx, void *addr, size_t size)
1064 {
1065 	const char *sym;
1066 
1067 	if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
1068 		sym = namelist[nlx].n_name;
1069 		if (*sym == '_')
1070 			++sym;
1071 		errx(1, "symbol %s not defined", sym);
1072 	}
1073 	if (kvm_read(kd, namelist[nlx].n_value, addr, size) != (ssize_t)size) {
1074 		sym = namelist[nlx].n_name;
1075 		if (*sym == '_')
1076 			++sym;
1077 		errx(1, "%s: %s", sym, kvm_geterr(kd));
1078 	}
1079 }
1080 
1081 static void
1082 usage(void)
1083 {
1084 	fprintf(stderr, "%s%s",
1085 		"usage: vmstat [-imsvz] [-c count] [-M core] [-N system] [-w wait]\n",
1086 		"              [-n devs] [disks]\n");
1087 	exit(1);
1088 }
1089