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