xref: /freebsd/usr.bin/vmstat/vmstat.c (revision 817420dc8eac7df799c78f5309b75092b7f7cd40)
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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1980, 1986, 1991, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)vmstat.c	8.1 (Berkeley) 6/6/93";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/time.h>
50 #include <sys/proc.h>
51 #include <sys/dkstat.h>
52 #include <sys/uio.h>
53 #include <sys/namei.h>
54 #include <sys/malloc.h>
55 #include <sys/signal.h>
56 #include <sys/fcntl.h>
57 #include <sys/ioctl.h>
58 #include <sys/sysctl.h>
59 #include <sys/vmmeter.h>
60 
61 #include <vm/vm_param.h>
62 #include <vm/vm_zone.h>
63 
64 #include <ctype.h>
65 #include <err.h>
66 #include <errno.h>
67 #include <kvm.h>
68 #include <limits.h>
69 #include <nlist.h>
70 #include <paths.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <sysexits.h>
75 #include <time.h>
76 #include <unistd.h>
77 #include <devstat.h>
78 
79 struct nlist namelist[] = {
80 #define	X_CPTIME	0
81 	{ "_cp_time" },
82 #define X_SUM		1
83 	{ "_cnt" },
84 #define	X_BOOTTIME	2
85 	{ "_boottime" },
86 #define X_HZ		3
87 	{ "_hz" },
88 #define X_STATHZ	4
89 	{ "_stathz" },
90 #define X_NCHSTATS	5
91 	{ "_nchstats" },
92 #define	X_INTRNAMES	6
93 	{ "_intrnames" },
94 #define	X_EINTRNAMES	7
95 	{ "_eintrnames" },
96 #define	X_INTRCNT	8
97 	{ "_intrcnt" },
98 #define	X_EINTRCNT	9
99 	{ "_eintrcnt" },
100 #define	X_KMEMSTATISTICS	10
101 	{ "_kmemstatistics" },
102 #define	X_KMEMBUCKETS	11
103 	{ "_bucket" },
104 #define	X_ZLIST		12
105 	{ "_zlist" },
106 #ifdef notyet
107 #define	X_DEFICIT	13
108 	{ "_deficit" },
109 #define	X_FORKSTAT	14
110 	{ "_forkstat" },
111 #define X_REC		15
112 	{ "_rectime" },
113 #define X_PGIN		16
114 	{ "_pgintime" },
115 #define	X_XSTATS	17
116 	{ "_xstats" },
117 #define X_END		18
118 #else
119 #define X_END		13
120 #endif
121 	{ "" },
122 };
123 
124 struct statinfo cur, last;
125 int num_devices, maxshowdevs;
126 long generation;
127 struct device_selection *dev_select;
128 int num_selected;
129 struct devstat_match *matches;
130 int num_matches = 0;
131 int num_devices_specified, num_selections;
132 long select_generation;
133 char **specified_devices;
134 devstat_select_mode select_mode;
135 
136 struct	vmmeter sum, osum;
137 
138 int	winlines = 20;
139 int	nflag = 0;
140 
141 kvm_t *kd;
142 
143 #define	FORKSTAT	0x01
144 #define	INTRSTAT	0x02
145 #define	MEMSTAT		0x04
146 #define	SUMSTAT		0x08
147 #define	TIMESTAT	0x10
148 #define	VMSTAT		0x20
149 #define ZMEMSTAT	0x40
150 
151 void	cpustats(), dointr(), domem(), dosum(), dozmem();
152 void	dovmstat(), kread(), usage();
153 #ifdef notyet
154 void	dotimes(), doforkst();
155 #endif
156 void printhdr __P((void));
157 static void devstats();
158 
159 int
160 main(argc, argv)
161 	register int argc;
162 	register char **argv;
163 {
164 	register int c, todo;
165 	u_int interval;
166 	int reps;
167 	char *memf, *nlistf;
168         char errbuf[_POSIX2_LINE_MAX];
169 	char *err_str;
170 
171 	memf = nlistf = NULL;
172 	interval = reps = todo = 0;
173 	maxshowdevs = 2;
174 	while ((c = getopt(argc, argv, "c:fiM:mN:n:p:stw:z")) != -1) {
175 		switch (c) {
176 		case 'c':
177 			reps = atoi(optarg);
178 			break;
179 		case 'f':
180 #ifdef notyet
181 			todo |= FORKSTAT;
182 #else
183 			errx(EX_USAGE, "sorry, -f is not (re)implemented yet");
184 #endif
185 			break;
186 		case 'i':
187 			todo |= INTRSTAT;
188 			break;
189 		case 'M':
190 			memf = optarg;
191 			break;
192 		case 'm':
193 			todo |= MEMSTAT;
194 			break;
195 		case 'N':
196 			nlistf = optarg;
197 			break;
198 		case 'n':
199 			nflag = 1;
200 			maxshowdevs = atoi(optarg);
201 			if (maxshowdevs < 0)
202 				errx(1, "number of devices %d is < 0",
203 				     maxshowdevs);
204 			break;
205 		case 'p':
206 			if (buildmatch(optarg, &matches, &num_matches) != 0)
207 				errx(1, "%s", devstat_errbuf);
208 			break;
209 		case 's':
210 			todo |= SUMSTAT;
211 			break;
212 		case 't':
213 #ifdef notyet
214 			todo |= TIMESTAT;
215 #else
216 			errx(EX_USAGE, "sorry, -t is not (re)implemented yet");
217 #endif
218 			break;
219 		case 'w':
220 			interval = atoi(optarg);
221 			break;
222 		case 'z':
223 			todo |= ZMEMSTAT;
224 			break;
225 		case '?':
226 		default:
227 			usage();
228 		}
229 	}
230 	argc -= optind;
231 	argv += optind;
232 
233 	if (todo == 0)
234 		todo = VMSTAT;
235 
236 	/*
237 	 * Discard setgid privileges if not the running kernel so that bad
238 	 * guys can't print interesting stuff from kernel memory.
239 	 */
240 	if (nlistf != NULL || memf != NULL)
241 		setgid(getgid());
242 
243 	kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
244 	if (kd == 0)
245 		errx(1, "kvm_openfiles: %s", errbuf);
246 
247 	if ((c = kvm_nlist(kd, namelist)) != 0) {
248 		if (c > 0) {
249 			warnx("undefined symbols:");
250 			for (c = 0;
251 			    c < sizeof(namelist)/sizeof(namelist[0]); c++)
252 				if (namelist[c].n_type == 0)
253 					fprintf(stderr, " %s",
254 					    namelist[c].n_name);
255 			(void)fputc('\n', stderr);
256 		} else
257 			warnx("kvm_nlist: %s", kvm_geterr(kd));
258 		exit(1);
259 	}
260 
261 	if (todo & VMSTAT) {
262 		char **getdrivedata();
263 		struct winsize winsize;
264 
265 		/*
266 		 * Make sure that the userland devstat version matches the
267 		 * kernel devstat version.  If not, exit and print a
268 		 * message informing the user of his mistake.
269 		 */
270 		if (checkversion() < 0)
271 			errx(1, "%s", devstat_errbuf);
272 
273 
274 		argv = getdrivedata(argv);
275 		winsize.ws_row = 0;
276 		(void) ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&winsize);
277 		if (winsize.ws_row > 0)
278 			winlines = winsize.ws_row;
279 
280 	}
281 
282 #define	BACKWARD_COMPATIBILITY
283 #ifdef	BACKWARD_COMPATIBILITY
284 	if (*argv) {
285 		interval = atoi(*argv);
286 		if (*++argv)
287 			reps = atoi(*argv);
288 	}
289 #endif
290 
291 	if (interval) {
292 		if (!reps)
293 			reps = -1;
294 	} else if (reps)
295 		interval = 1;
296 
297 #ifdef notyet
298 	if (todo & FORKSTAT)
299 		doforkst();
300 #endif
301 	if (todo & MEMSTAT)
302 		domem();
303 	if (todo & ZMEMSTAT)
304 		dozmem();
305 	if (todo & SUMSTAT)
306 		dosum();
307 #ifdef notyet
308 	if (todo & TIMESTAT)
309 		dotimes();
310 #endif
311 	if (todo & INTRSTAT)
312 		dointr();
313 	if (todo & VMSTAT)
314 		dovmstat(interval, reps);
315 	exit(0);
316 }
317 
318 char **
319 getdrivedata(argv)
320 	char **argv;
321 {
322 	register int i;
323 	char buf[30];
324 
325 	if ((num_devices = getnumdevs()) < 0)
326 		errx(1, "%s", devstat_errbuf);
327 
328 	cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
329 	last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
330 	bzero(cur.dinfo, sizeof(struct devinfo));
331 	bzero(last.dinfo, sizeof(struct devinfo));
332 
333 	if (getdevs(&cur) == -1)
334 		errx(1, "%s", devstat_errbuf);
335 
336 	num_devices = cur.dinfo->numdevs;
337 	generation = cur.dinfo->generation;
338 
339 	specified_devices = (char **)malloc(sizeof(char *));
340 	for (num_devices_specified = 0; *argv; ++argv) {
341 		if (isdigit(**argv))
342 			break;
343 		num_devices_specified++;
344 		specified_devices = (char **)realloc(specified_devices,
345 						     sizeof(char *) *
346 						     num_devices_specified);
347 		specified_devices[num_devices_specified - 1] = *argv;
348 	}
349 	dev_select = NULL;
350 
351 	if (nflag == 0 && maxshowdevs < num_devices_specified)
352 			maxshowdevs = num_devices_specified;
353 
354 	/*
355 	 * People are generally only interested in disk statistics when
356 	 * they're running vmstat.  So, that's what we're going to give
357 	 * them if they don't specify anything by default.  We'll also give
358 	 * them any other random devices in the system so that we get to
359 	 * maxshowdevs devices, if that many devices exist.  If the user
360 	 * specifies devices on the command line, either through a pattern
361 	 * match or by naming them explicitly, we will give the user only
362 	 * those devices.
363 	 */
364 	if ((num_devices_specified == 0) && (num_matches == 0)) {
365 		if (buildmatch("da", &matches, &num_matches) != 0)
366 			errx(1, "%s", devstat_errbuf);
367 
368 		select_mode = DS_SELECT_ADD;
369 	} else
370 		select_mode = DS_SELECT_ONLY;
371 
372 	/*
373 	 * At this point, selectdevs will almost surely indicate that the
374 	 * device list has changed, so we don't look for return values of 0
375 	 * or 1.  If we get back -1, though, there is an error.
376 	 */
377 	if (selectdevs(&dev_select, &num_selected, &num_selections,
378 		       &select_generation, generation, cur.dinfo->devices,
379 		       num_devices, matches, num_matches, specified_devices,
380 		       num_devices_specified, select_mode,
381 		       maxshowdevs, 0) == -1)
382 		errx(1, "%s", devstat_errbuf);
383 
384 	return(argv);
385 }
386 
387 long
388 getuptime()
389 {
390 	static time_t now, boottime;
391 	time_t uptime;
392 
393 	if (boottime == 0)
394 		kread(X_BOOTTIME, &boottime, sizeof(boottime));
395 	(void)time(&now);
396 	uptime = now - boottime;
397 	if (uptime <= 0 || uptime > 60*60*24*365*10)
398 		errx(1, "time makes no sense; namelist must be wrong");
399 	return(uptime);
400 }
401 
402 int	hz, hdrcnt;
403 
404 void
405 dovmstat(interval, reps)
406 	u_int interval;
407 	int reps;
408 {
409 	struct vmtotal total;
410 	time_t uptime, halfuptime;
411 	struct devinfo *tmp_dinfo;
412 	void needhdr();
413 	int mib[2], size;
414 
415 	uptime = getuptime();
416 	halfuptime = uptime / 2;
417 	(void)signal(SIGCONT, needhdr);
418 
419 	if (namelist[X_STATHZ].n_type != 0 && namelist[X_STATHZ].n_value != 0)
420 		kread(X_STATHZ, &hz, sizeof(hz));
421 	if (!hz)
422 		kread(X_HZ, &hz, sizeof(hz));
423 
424 	for (hdrcnt = 1;;) {
425 		if (!--hdrcnt)
426 			printhdr();
427 		kread(X_CPTIME, cur.cp_time, sizeof(cur.cp_time));
428 
429 		tmp_dinfo = last.dinfo;
430 		last.dinfo = cur.dinfo;
431 		cur.dinfo = tmp_dinfo;
432 		last.busy_time = cur.busy_time;
433 
434 		/*
435 		 * Here what we want to do is refresh our device stats.
436 		 * getdevs() returns 1 when the device list has changed.
437 		 * If the device list has changed, we want to go through
438 		 * the selection process again, in case a device that we
439 		 * were previously displaying has gone away.
440 		 */
441 		switch (getdevs(&cur)) {
442 		case -1:
443 			errx(1, "%s", devstat_errbuf);
444 			break;
445 		case 1: {
446 			int retval;
447 
448 			num_devices = cur.dinfo->numdevs;
449 			generation = cur.dinfo->generation;
450 
451 			retval = selectdevs(&dev_select, &num_selected,
452 					    &num_selections, &select_generation,
453 					    generation, cur.dinfo->devices,
454 					    num_devices, matches, num_matches,
455 					    specified_devices,
456 					    num_devices_specified, select_mode,
457 					    maxshowdevs, 0);
458 			switch (retval) {
459 			case -1:
460 				errx(1, "%s", devstat_errbuf);
461 				break;
462 			case 1:
463 				printhdr();
464 				break;
465 			default:
466 				break;
467 			}
468 		}
469 		default:
470 			break;
471 		}
472 
473 		kread(X_SUM, &sum, sizeof(sum));
474 		size = sizeof(total);
475 		mib[0] = CTL_VM;
476 		mib[1] = VM_METER;
477 		if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
478 			printf("Can't get kerninfo: %s\n", strerror(errno));
479 			bzero(&total, sizeof(total));
480 		}
481 		(void)printf("%2d%2d%2d",
482 		    total.t_rq - 1, total.t_dw + total.t_pw, total.t_sw);
483 #define pgtok(a) ((a) * sum.v_page_size >> 10)
484 #define	rate(x)	(((x) + halfuptime) / uptime)	/* round */
485 		(void)printf("%8ld%6ld ",
486 		    (long)pgtok(total.t_avm), (long)pgtok(total.t_free));
487 		(void)printf("%4lu ",
488 		    (u_long)rate(sum.v_vm_faults - osum.v_vm_faults));
489 		(void)printf("%3lu ",
490 		    (u_long)rate(sum.v_reactivated - osum.v_reactivated));
491 		(void)printf("%3lu ",
492 		    (u_long)rate(sum.v_swapin + sum.v_vnodein -
493 		    (osum.v_swapin + osum.v_vnodein)));
494 		(void)printf("%3lu ",
495 		    (u_long)rate(sum.v_swapout + sum.v_vnodeout -
496 		    (osum.v_swapout + osum.v_vnodeout)));
497 		(void)printf("%3lu ",
498 		    (u_long)rate(sum.v_tfree - osum.v_tfree));
499 		(void)printf("%3lu ",
500 		    (u_long)rate(sum.v_pdpages - osum.v_pdpages));
501 		devstats();
502 		(void)printf("%4lu %4lu %3lu ",
503 		    (u_long)rate(sum.v_intr - osum.v_intr),
504 		    (u_long)rate(sum.v_syscall - osum.v_syscall),
505 		    (u_long)rate(sum.v_swtch - osum.v_swtch));
506 		cpustats();
507 		(void)printf("\n");
508 		(void)fflush(stdout);
509 		if (reps >= 0 && --reps <= 0)
510 			break;
511 		osum = sum;
512 		uptime = interval;
513 		/*
514 		 * We round upward to avoid losing low-frequency events
515 		 * (i.e., >= 1 per interval but < 1 per second).
516 		 */
517 		if (interval != 1)
518 			halfuptime = (uptime + 1) / 2;
519 		else
520 			halfuptime = 0;
521 		(void)sleep(interval);
522 	}
523 }
524 
525 void
526 printhdr()
527 {
528 	int i, num_shown;
529 
530 	num_shown = (num_selected < maxshowdevs) ? num_selected : maxshowdevs;
531 	(void)printf(" procs      memory     page%*s", 19, "");
532 	if (num_shown > 1)
533 		(void)printf(" disks %*s", num_shown * 4 - 7, "");
534 	else if (num_shown == 1)
535 		(void)printf("disk");
536 	(void)printf("   faults      cpu\n");
537 	(void)printf(" r b w     avm   fre  flt  re  pi  po  fr  sr ");
538 	for (i = 0; i < num_devices; i++)
539 		if ((dev_select[i].selected)
540 		 && (dev_select[i].selected <= maxshowdevs))
541 			(void)printf("%c%c%d ", dev_select[i].device_name[0],
542 				     dev_select[i].device_name[1],
543 				     dev_select[i].unit_number);
544 	(void)printf("  in   sy  cs us sy id\n");
545 	hdrcnt = winlines - 2;
546 }
547 
548 /*
549  * Force a header to be prepended to the next output.
550  */
551 void
552 needhdr()
553 {
554 
555 	hdrcnt = 1;
556 }
557 
558 #ifdef notyet
559 void
560 dotimes()
561 {
562 	u_int pgintime, rectime;
563 
564 	kread(X_REC, &rectime, sizeof(rectime));
565 	kread(X_PGIN, &pgintime, sizeof(pgintime));
566 	kread(X_SUM, &sum, sizeof(sum));
567 	(void)printf("%u reclaims, %u total time (usec)\n",
568 	    sum.v_pgrec, rectime);
569 	(void)printf("average: %u usec / reclaim\n", rectime / sum.v_pgrec);
570 	(void)printf("\n");
571 	(void)printf("%u page ins, %u total time (msec)\n",
572 	    sum.v_pgin, pgintime / 10);
573 	(void)printf("average: %8.1f msec / page in\n",
574 	    pgintime / (sum.v_pgin * 10.0));
575 }
576 #endif
577 
578 long
579 pct(top, bot)
580 	long top, bot;
581 {
582 	long ans;
583 
584 	if (bot == 0)
585 		return(0);
586 	ans = (quad_t)top * 100 / bot;
587 	return (ans);
588 }
589 
590 #define	PCT(top, bot) pct((long)(top), (long)(bot))
591 
592 void
593 dosum()
594 {
595 	struct nchstats nchstats;
596 	long nchtotal;
597 
598 	kread(X_SUM, &sum, sizeof(sum));
599 	(void)printf("%9u cpu context switches\n", sum.v_swtch);
600 	(void)printf("%9u device interrupts\n", sum.v_intr);
601 	(void)printf("%9u software interrupts\n", sum.v_soft);
602 	(void)printf("%9u traps\n", sum.v_trap);
603 	(void)printf("%9u system calls\n", sum.v_syscall);
604 	(void)printf("%9u swap pager pageins\n", sum.v_swapin);
605 	(void)printf("%9u swap pager pages paged in\n", sum.v_swappgsin);
606 	(void)printf("%9u swap pager pageouts\n", sum.v_swapout);
607 	(void)printf("%9u swap pager pages paged out\n", sum.v_swappgsout);
608 	(void)printf("%9u vnode pager pageins\n", sum.v_vnodein);
609 	(void)printf("%9u vnode pager pages paged in\n", sum.v_vnodepgsin);
610 	(void)printf("%9u vnode pager pageouts\n", sum.v_vnodeout);
611 	(void)printf("%9u vnode pager pages paged out\n", sum.v_vnodepgsout);
612 	(void)printf("%9u page daemon wakeups\n", sum.v_pdwakeups);
613 	(void)printf("%9u pages examined by the page daemon\n", sum.v_pdpages);
614 	(void)printf("%9u pages reactivated\n", sum.v_reactivated);
615 	(void)printf("%9u copy-on-write faults\n", sum.v_cow_faults);
616 	(void)printf("%9u copy-on-write optimized faults\n", sum.v_cow_optim);
617 	(void)printf("%9u zero fill pages zeroed\n", sum.v_zfod);
618 	(void)printf("%9u zero fill pages prezeroed\n", sum.v_ozfod);
619 	(void)printf("%9u intransit blocking page faults\n", sum.v_intrans);
620 	(void)printf("%9u total VM faults taken\n", sum.v_vm_faults);
621 	(void)printf("%9u pages freed\n", sum.v_tfree);
622 	(void)printf("%9u pages freed by daemon\n", sum.v_dfree);
623 	(void)printf("%9u pages freed by exiting processes\n", sum.v_pfree);
624 	(void)printf("%9u pages active\n", sum.v_active_count);
625 	(void)printf("%9u pages inactive\n", sum.v_inactive_count);
626 	(void)printf("%9u pages in VM cache\n", sum.v_cache_count);
627 	(void)printf("%9u pages wired down\n", sum.v_wire_count);
628 	(void)printf("%9u pages free\n", sum.v_free_count);
629 	(void)printf("%9u bytes per page\n", sum.v_page_size);
630 	kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
631 	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
632 	    nchstats.ncs_badhits + nchstats.ncs_falsehits +
633 	    nchstats.ncs_miss + nchstats.ncs_long;
634 	(void)printf("%9ld total name lookups\n", nchtotal);
635 	(void)printf(
636 	    "%9s cache hits (%ld%% pos + %ld%% neg) system %ld%% per-directory\n",
637 	    "", PCT(nchstats.ncs_goodhits, nchtotal),
638 	    PCT(nchstats.ncs_neghits, nchtotal),
639 	    PCT(nchstats.ncs_pass2, nchtotal));
640 	(void)printf("%9s deletions %ld%%, falsehits %ld%%, toolong %ld%%\n", "",
641 	    PCT(nchstats.ncs_badhits, nchtotal),
642 	    PCT(nchstats.ncs_falsehits, nchtotal),
643 	    PCT(nchstats.ncs_long, nchtotal));
644 }
645 
646 #ifdef notyet
647 void
648 doforkst()
649 {
650 	struct forkstat fks;
651 
652 	kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
653 	(void)printf("%d forks, %d pages, average %.2f\n",
654 	    fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
655 	(void)printf("%d vforks, %d pages, average %.2f\n",
656 	    fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork);
657 }
658 #endif
659 
660 static void
661 devstats()
662 {
663 	register int dn, state;
664 	long double transfers_per_second;
665 	long double busy_seconds;
666 	long tmp;
667 
668 	for (state = 0; state < CPUSTATES; ++state) {
669 		tmp = cur.cp_time[state];
670 		cur.cp_time[state] -= last.cp_time[state];
671 		last.cp_time[state] = tmp;
672 	}
673 
674 	busy_seconds = compute_etime(cur.busy_time, last.busy_time);
675 
676 	for (dn = 0; dn < num_devices; dn++) {
677 		int di;
678 
679 		if ((dev_select[dn].selected == 0)
680 		 || (dev_select[dn].selected > maxshowdevs))
681 			continue;
682 
683 		di = dev_select[dn].position;
684 
685 		if (compute_stats(&cur.dinfo->devices[di],
686 				  &last.dinfo->devices[di], busy_seconds,
687 				  NULL, NULL, NULL,
688 				  NULL, &transfers_per_second, NULL,
689 				  NULL, NULL) != 0)
690 			errx(1, "%s", devstat_errbuf);
691 
692 		printf("%3.0Lf ", transfers_per_second);
693 	}
694 }
695 
696 void
697 cpustats()
698 {
699 	register int state;
700 	double pct, total;
701 
702 	total = 0;
703 	for (state = 0; state < CPUSTATES; ++state)
704 		total += cur.cp_time[state];
705 	if (total)
706 		pct = 100 / total;
707 	else
708 		pct = 0;
709 	(void)printf("%2.0f ", (cur.cp_time[CP_USER] +
710 				cur.cp_time[CP_NICE]) * pct);
711 	(void)printf("%2.0f ", (cur.cp_time[CP_SYS] +
712 				cur.cp_time[CP_INTR]) * pct);
713 	(void)printf("%2.0f", cur.cp_time[CP_IDLE] * pct);
714 }
715 
716 void
717 dointr()
718 {
719 	register u_long *intrcnt, uptime;
720 	register u_int64_t inttotal;
721 	register int nintr, inamlen;
722 	register char *intrname;
723 
724 	uptime = getuptime();
725 	nintr = namelist[X_EINTRCNT].n_value - namelist[X_INTRCNT].n_value;
726 	inamlen =
727 	    namelist[X_EINTRNAMES].n_value - namelist[X_INTRNAMES].n_value;
728 	intrcnt = malloc((size_t)nintr);
729 	intrname = malloc((size_t)inamlen);
730 	if (intrcnt == NULL || intrname == NULL)
731 		errx(1, "malloc");
732 	kread(X_INTRCNT, intrcnt, (size_t)nintr);
733 	kread(X_INTRNAMES, intrname, (size_t)inamlen);
734 	(void)printf("interrupt      total      rate\n");
735 	inttotal = 0;
736 	nintr /= sizeof(long);
737 	while (--nintr >= 0) {
738 		if (*intrcnt)
739 			(void)printf("%-12s %8lu %8lu\n", intrname,
740 			    *intrcnt, *intrcnt / uptime);
741 		intrname += strlen(intrname) + 1;
742 		inttotal += *intrcnt++;
743 	}
744 	(void)printf("Total        %8llu %8llu\n", inttotal,
745 			inttotal / (u_int64_t) uptime);
746 }
747 
748 #define	MAX_KMSTATS	200
749 
750 void
751 domem()
752 {
753 	register struct kmembuckets *kp;
754 	register struct malloc_type *ks;
755 	register int i, j;
756 	int len, size, first, nkms;
757 	long totuse = 0, totfree = 0, totreq = 0;
758 	const char *name;
759 	struct malloc_type kmemstats[MAX_KMSTATS], *kmsp;
760 	char *kmemnames[MAX_KMSTATS];
761 	char buf[1024];
762 	struct kmembuckets buckets[MINBUCKET + 16];
763 
764 	kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
765 	kread(X_KMEMSTATISTICS, &kmsp, sizeof(kmsp));
766 	for (nkms = 0; nkms < MAX_KMSTATS && kmsp != NULL; nkms++) {
767 		if (sizeof(kmemstats[0]) != kvm_read(kd, (u_long)kmsp,
768 		    &kmemstats[nkms], sizeof(kmemstats[0])))
769 			err(1, "kvm_read(%p)", (void *)kmsp);
770 		if (sizeof(buf) !=  kvm_read(kd,
771 	            (u_long)kmemstats[nkms].ks_shortdesc, buf, sizeof(buf)))
772 			err(1, "kvm_read(%p)",
773 			    (void *)kmemstats[nkms].ks_shortdesc);
774 		buf[sizeof(buf) - 1] = '\0';
775 		kmemstats[nkms].ks_shortdesc = strdup(buf);
776 		kmsp = kmemstats[nkms].ks_next;
777 	}
778 	if (kmsp != NULL)
779 		warnx("truncated to the first %d memory types", nkms);
780 	(void)printf("Memory statistics by bucket size\n");
781 	(void)printf(
782 	    "Size   In Use   Free   Requests  HighWater  Couldfree\n");
783 	for (i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) {
784 		if (kp->kb_calls == 0)
785 			continue;
786 		size = 1 << i;
787 		if(size < 1024)
788 			(void)printf("%4d",size);
789 		else
790 			(void)printf("%3dK",size>>10);
791 		(void)printf(" %8ld %6ld %10ld %7ld %10ld\n",
792 			kp->kb_total - kp->kb_totalfree,
793 			kp->kb_totalfree, kp->kb_calls,
794 			kp->kb_highwat, kp->kb_couldfree);
795 		totfree += size * kp->kb_totalfree;
796 	}
797 
798 	(void)printf("\nMemory usage type by bucket size\n");
799 	(void)printf("Size  Type(s)\n");
800 	kp = &buckets[MINBUCKET];
801 	for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1, kp++) {
802 		if (kp->kb_calls == 0)
803 			continue;
804 		first = 1;
805 		len = 8;
806 		for (i = 0, ks = &kmemstats[0]; i < nkms; i++, ks++) {
807 			if (ks->ks_calls == 0)
808 				continue;
809 			if ((ks->ks_size & j) == 0)
810 				continue;
811 			name = ks->ks_shortdesc;
812 			len += 2 + strlen(name);
813 			if (first && j < 1024)
814 				printf("%4d  %s", j, name);
815 			else if (first)
816 				printf("%3dK  %s", j>>10, name);
817 			else
818 				printf(",");
819 			if (len >= 79) {
820 				printf("\n\t ");
821 				len = 10 + strlen(name);
822 			}
823 			if (!first)
824 				printf(" %s", name);
825 			first = 0;
826 		}
827 		printf("\n");
828 	}
829 
830 	(void)printf(
831 	    "\nMemory statistics by type                          Type  Kern\n");
832 	(void)printf(
833 "        Type  InUse MemUse HighUse  Limit Requests Limit Limit Size(s)\n");
834 	for (i = 0, ks = &kmemstats[0]; i < nkms; i++, ks++) {
835 		if (ks->ks_calls == 0)
836 			continue;
837 		(void)printf("%13s%6ld%6ldK%7ldK%6ldK%9ld%5u%6u",
838 		    ks->ks_shortdesc,
839 		    ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
840 		    (ks->ks_maxused + 1023) / 1024,
841 		    (ks->ks_limit + 1023) / 1024, ks->ks_calls,
842 		    ks->ks_limblocks, ks->ks_mapblocks);
843 		first = 1;
844 		for (j =  1 << MINBUCKET; j < 1 << (MINBUCKET + 16); j <<= 1) {
845 			if ((ks->ks_size & j) == 0)
846 				continue;
847 			if (first)
848 				printf("  ");
849 			else
850 				printf(",");
851 			if(j<1024)
852 				printf("%d",j);
853 			else
854 				printf("%dK",j>>10);
855 			first = 0;
856 		}
857 		printf("\n");
858 		totuse += ks->ks_memuse;
859 		totreq += ks->ks_calls;
860 	}
861 	(void)printf("\nMemory Totals:  In Use    Free    Requests\n");
862 	(void)printf("              %7ldK %6ldK    %8ld\n",
863 	     (totuse + 1023) / 1024, (totfree + 1023) / 1024, totreq);
864 }
865 
866 void
867 dozmem()
868 {
869 	vm_zone_t zonep;
870 	int nmax = 512;
871 	int zused_bytes = 0;
872 	int ztotal_bytes = 0;
873 
874 	printf(
875 	    "\n"
876 	    "%-16s%-8s%-8s%-8s\n",
877 	    "ZONE",
878 	    "used",
879 	    "total",
880 	    "mem-use"
881 	);
882 
883 	kread(X_ZLIST, &zonep, sizeof(zonep));
884 	while (zonep != NULL && nmax) {
885 		struct vm_zone zone;
886 		char buf[32];
887 		int n;
888 
889 		if (kvm_read(kd, (u_long)zonep, &zone, sizeof(zone)) != sizeof(zone))
890 			break;
891 		n = kvm_read(kd, (u_long)zone.zname, buf, sizeof(buf) - 1);
892 		if (n < 0)
893 		    n = 0;
894 		buf[n] = 0;
895 
896 		printf(
897 		    "%-15.15s %-7d %-7d %4d/%dK\n",
898 		    buf,
899 		    zone.ztotal - zone.zfreecnt,
900 		    zone.ztotal,
901 		    (zone.ztotal - zone.zfreecnt) * zone.zsize / 1024,
902 		    zone.ztotal * zone.zsize / 1024
903 		);
904 		zused_bytes += (zone.ztotal - zone.zfreecnt) * zone.zsize;
905 		ztotal_bytes += zone.ztotal * zone.zsize;
906 		--nmax;
907 		zonep = zone.znext;
908 	}
909 	printf(
910 	    "------------------------------------------\n"
911 	    "%-15.15s %-7s %-7s %4d/%dK\n\n",
912 	    "TOTAL",
913 	    "",
914 	    "",
915 	    zused_bytes / 1024,
916 	    ztotal_bytes / 1024
917 	);
918 }
919 
920 /*
921  * kread reads something from the kernel, given its nlist index.
922  */
923 void
924 kread(nlx, addr, size)
925 	int nlx;
926 	void *addr;
927 	size_t size;
928 {
929 	char *sym;
930 
931 	if (namelist[nlx].n_type == 0 || namelist[nlx].n_value == 0) {
932 		sym = namelist[nlx].n_name;
933 		if (*sym == '_')
934 			++sym;
935 		errx(1, "symbol %s not defined", sym);
936 	}
937 	if (kvm_read(kd, namelist[nlx].n_value, addr, size) != size) {
938 		sym = namelist[nlx].n_name;
939 		if (*sym == '_')
940 			++sym;
941 		errx(1, "%s: %s", sym, kvm_geterr(kd));
942 	}
943 }
944 
945 void
946 usage()
947 {
948 	(void)fprintf(stderr,
949 "usage: vmstat [-imsz] [-c count] [-M core] [-N system] [-w wait] [disks]\n");
950 	exit(1);
951 }
952