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