xref: /original-bsd/usr.bin/vmstat/vmstat.c (revision ba762ddc)
1 /*
2  * Copyright (c) 1980, 1986, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1980, 1986, 1991 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)vmstat.c	5.26 (Berkeley) 04/24/91";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/vm.h>
20 #include <sys/user.h>
21 #include <sys/dkstat.h>
22 #include <sys/buf.h>
23 #include <sys/namei.h>
24 #include <sys/text.h>
25 #include <sys/malloc.h>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #include <nlist.h>
30 #include <kvm.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <paths.h>
38 
39 struct nlist nl[] = {
40 #define	X_CPTIME	0
41 	{ "_cp_time" },
42 #define	X_RATE		1
43 	{ "_rate" },
44 #define X_TOTAL		2
45 	{ "_total" },
46 #define	X_DEFICIT	3
47 	{ "_deficit" },
48 #define	X_FORKSTAT	4
49 	{ "_forkstat" },
50 #define X_SUM		5
51 	{ "_sum" },
52 #define	X_BOOTTIME	6
53 	{ "_boottime" },
54 #define	X_DKXFER	7
55 	{ "_dk_xfer" },
56 #define X_REC		8
57 	{ "_rectime" },
58 #define X_PGIN		9
59 	{ "_pgintime" },
60 #define X_HZ		10
61 	{ "_hz" },
62 #define X_PHZ		11
63 	{ "_phz" },
64 #define X_NCHSTATS	12
65 	{ "_nchstats" },
66 #define	X_INTRNAMES	13
67 	{ "_intrnames" },
68 #define	X_EINTRNAMES	14
69 	{ "_eintrnames" },
70 #define	X_INTRCNT	15
71 	{ "_intrcnt" },
72 #define	X_EINTRCNT	16
73 	{ "_eintrcnt" },
74 #define	X_DK_NDRIVE	17
75 	{ "_dk_ndrive" },
76 #define	X_XSTATS	18
77 	{ "_xstats" },
78 #define	X_KMEMSTAT	19
79 	{ "_kmemstats" },
80 #define	X_KMEMBUCKETS	20
81 	{ "_bucket" },
82 #define X_END		20
83 #ifdef hp300
84 #define	X_HPDINIT	(X_END+1)
85 	{ "_hp_dinit" },
86 #endif
87 #ifdef tahoe
88 #define	X_VBDINIT	(X_END+1)
89 	{ "_vbdinit" },
90 #define	X_CKEYSTATS	(X_END+2)
91 	{ "_ckeystats" },
92 #define	X_DKEYSTATS	(X_END+3)
93 	{ "_dkeystats" },
94 #endif
95 #ifdef vax
96 #define X_MBDINIT	(X_END+1)
97 	{ "_mbdinit" },
98 #define X_UBDINIT	(X_END+2)
99 	{ "_ubdinit" },
100 #endif
101 	{ "" },
102 };
103 
104 struct _disk {
105 	long time[CPUSTATES];
106 	long *xfer;
107 } cur, last;
108 
109 struct vmmeter sum;
110 char *vmunix = _PATH_UNIX;
111 char **dr_name;
112 int *dr_select, dk_ndrive, ndrives;
113 
114 #define	FORKSTAT	0x01
115 #define	INTRSTAT	0x02
116 #define	MEMSTAT		0x04
117 #define	SUMSTAT		0x08
118 #define	TIMESTAT	0x10
119 #define	VMSTAT		0x20
120 #define	ZEROOUT		0x40
121 
122 #include "names.c"			/* disk names -- machine dependent */
123 
124 void cpustats(), dkstats(), doforkst(), dointr(), domem(), dosum();
125 void dotimes(), dovmstat(), kread(), usage(), zero();
126 
127 main(argc, argv)
128 	register int argc;
129 	register char **argv;
130 {
131 	extern int optind;
132 	extern char *optarg;
133 	register int c, todo;
134 	u_int interval;
135 	int reps;
136 	char *kmem;
137 
138 	kmem = NULL;
139 	interval = reps = todo = 0;
140 	while ((c = getopt(argc, argv, "c:fiM:mN:stw:z")) != EOF) {
141 		switch (c) {
142 		case 'c':
143 			reps = atoi(optarg);
144 			break;
145 		case 'f':
146 			todo |= FORKSTAT;
147 			break;
148 		case 'i':
149 			todo |= INTRSTAT;
150 			break;
151 		case 'M':
152 			kmem = optarg;
153 			break;
154 		case 'm':
155 			todo |= MEMSTAT;
156 			break;
157 		case 'N':
158 			vmunix = optarg;
159 			break;
160 		case 's':
161 			todo |= SUMSTAT;
162 			break;
163 		case 't':
164 			todo |= TIMESTAT;
165 			break;
166 		case 'w':
167 			interval = atoi(optarg);
168 			break;
169 		case 'z':
170 			todo |= ZEROOUT;
171 			break;
172 		case '?':
173 		default:
174 			usage();
175 		}
176 	}
177 	argc -= optind;
178 	argv += optind;
179 
180 	if (todo & ZEROOUT) {
181 		if (todo & ~ZEROOUT || kmem)
182 			usage();
183 		zero();
184 		exit(0);
185 	}
186 
187 	if (todo == 0)
188 		todo = VMSTAT;
189 
190 	if (kvm_openfiles(vmunix, kmem, NULL) < 0) {
191 		(void)fprintf(stderr,
192 		    "vmstat: kvm_openfiles: %s\n", kvm_geterr());
193 		exit(1);
194 	}
195 
196 	(void)kvm_nlist(nl);
197 	if (nl[0].n_type == 0) {
198 		(void)fprintf(stderr,
199 		    "vmstat: %s: no namelist\n", vmunix);
200 		exit(1);
201 	}
202 
203 	if (todo & VMSTAT) {
204 		char **getdrivedata();
205 
206 		argv = getdrivedata(argv);
207 	}
208 
209 #define	BACKWARD_COMPATIBILITY
210 #ifdef	BACKWARD_COMPATIBILITY
211 	if (*argv) {
212 		interval = atoi(*argv);
213 		if (*++argv)
214 			reps = atoi(*argv);
215 	}
216 #endif
217 
218 	if (interval) {
219 		if (!reps)
220 			reps = -1;
221 	} else
222 		if (reps)
223 			interval = 1;
224 
225 	if (todo & FORKSTAT)
226 		doforkst();
227 	if (todo & MEMSTAT)
228 		domem();
229 	if (todo & SUMSTAT)
230 		dosum();
231 	if (todo & TIMESTAT)
232 		dotimes();
233 	if (todo & INTRSTAT)
234 		dointr();
235 	if (todo & VMSTAT)
236 		dovmstat(interval, reps);
237 	exit(0);
238 }
239 
240 char **
241 getdrivedata(argv)
242 	char **argv;
243 {
244 	register int i;
245 	register char **cp;
246 	char buf[30];
247 
248 	kread(X_DK_NDRIVE, &dk_ndrive, sizeof(dk_ndrive));
249 	if (dk_ndrive <= 0) {
250 		(void)fprintf(stderr, "vmstat: dk_ndrive %d\n", dk_ndrive);
251 		exit(1);
252 	}
253 	dr_select = calloc((size_t)dk_ndrive, sizeof(int));
254 	dr_name = calloc((size_t)dk_ndrive, sizeof(char *));
255 	for (i = 0; i < dk_ndrive; i++)
256 		dr_name[i] = NULL;
257 	cur.xfer = calloc((size_t)dk_ndrive, sizeof(long));
258 	last.xfer = calloc((size_t)dk_ndrive, sizeof(long));
259 	read_names();
260 	for (i = 0; i < dk_ndrive; i++)
261 		if (dr_name[i] == NULL) {
262 			(void)sprintf(buf, "??%d", i);
263 			dr_name[i] = strdup(buf);
264 		}
265 
266 	/*
267 	 * Choose drives to be displayed.  Priority goes to (in order) drives
268 	 * supplied as arguments, default drives.  If everything isn't filled
269 	 * in and there are drives not taken care of, display the first few
270 	 * that fit.
271 	 */
272 #define BACKWARD_COMPATIBILITY
273 	for (ndrives = 0; *argv; ++argv) {
274 #ifdef	BACKWARD_COMPATIBILITY
275 		if (isdigit(**argv))
276 			break;
277 #endif
278 		for (i = 0; i < dk_ndrive; i++) {
279 			if (strcmp(dr_name[i], *argv))
280 				continue;
281 			dr_select[i] = 1;
282 			++ndrives;
283 			break;
284 		}
285 	}
286 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
287 		if (dr_select[i])
288 			continue;
289 		for (cp = defdrives; *cp; cp++)
290 			if (strcmp(dr_name[i], *cp) == 0) {
291 				dr_select[i] = 1;
292 				++ndrives;
293 				break;
294 			}
295 	}
296 	for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
297 		if (dr_select[i])
298 			continue;
299 		dr_select[i] = 1;
300 		++ndrives;
301 	}
302 	return(argv);
303 }
304 
305 long
306 getuptime()
307 {
308 	static time_t now, boottime;
309 	time_t uptime;
310 
311 	if (boottime == 0)
312 		kread(X_BOOTTIME, &boottime, sizeof(boottime));
313 	(void)time(&now);
314 	uptime = now - boottime;
315 	if (uptime <= 0 || uptime > 60*60*24*365*10) {
316 		(void)fprintf(stderr,
317 		    "vmstat: time makes no sense; namelist must be wrong.\n");
318 		exit(1);
319 	}
320 	return(uptime);
321 }
322 
323 int hz;
324 
325 void
326 dovmstat(interval, reps)
327 	u_int interval;
328 	int reps;
329 {
330 	struct vmmeter rate;
331 	struct vmtotal total;
332 	time_t uptime;
333 	int deficit, hdrcnt;
334 	void printhdr();
335 
336 	uptime = getuptime();
337 	(void)signal(SIGCONT, printhdr);
338 
339 	if (nl[X_PHZ].n_type != 0 && nl[X_PHZ].n_value != 0)
340 		kread(X_PHZ, &hz, sizeof(hz));
341 	if (!hz)
342 		kread(X_HZ, &hz, sizeof(hz));
343 
344 	for (hdrcnt = 1;;) {
345 		if (!--hdrcnt) {
346 			printhdr();
347 			hdrcnt = 20;
348 		}
349 		kread(X_CPTIME, cur.time, sizeof(cur.time));
350 		kread(X_DKXFER, cur.xfer, sizeof(*cur.xfer * dk_ndrive));
351 		if (uptime != 1)
352 			kread(X_SUM, &rate, sizeof(rate));
353 		else
354 			kread(X_RATE, &rate, sizeof(rate));
355 		kread(X_TOTAL, &total, sizeof(total));
356 		kread(X_SUM, &sum, sizeof(sum));
357 		kread(X_DEFICIT, &deficit, sizeof(deficit));
358 		(void)printf("%2d%2d%2d",
359 		    total.t_rq, total.t_dw + total.t_pw, total.t_sw);
360 #define pgtok(a) ((a)*NBPG >> 10)
361 		(void)printf("%6ld%6ld",
362 		    pgtok(total.t_avm), pgtok(total.t_free));
363 		(void)printf("%4lu%3lu",
364 		    (rate.v_pgrec - (rate.v_xsfrec+rate.v_xifrec)) / uptime,
365 		    (rate.v_xsfrec+rate.v_xifrec) / uptime);
366 		(void)printf("%4lu", pgtok(rate.v_pgpgin) / uptime);
367 		(void)printf("%4lu%4lu%4d%4lu", pgtok(rate.v_pgpgout) / uptime,
368 		    pgtok(rate.v_dfree) / uptime,
369 		    pgtok(deficit), rate.v_scan / uptime);
370 		dkstats();
371 		(void)printf("%4lu%4lu%4lu", rate.v_intr / uptime,
372 		    rate.v_syscall / uptime, rate.v_swtch / uptime);
373 		cpustats();
374 		(void)printf("\n");
375 		(void)fflush(stdout);
376 		uptime = 1;
377 		if (reps >= 0 && --reps <= 0)
378 			break;
379 		(void)sleep(interval);
380 	}
381 }
382 
383 void
384 printhdr()
385 {
386 	register int i;
387 
388 	(void)printf(" procs   memory     page%*s", 22, "");
389 	if (ndrives > 1)
390 		(void)printf("disks %*s faults     cpu\n",
391 		   ndrives * 3 - 6, "");
392 	else
393 		(void)printf("%*s faults     cpu\n", ndrives * 3, "");
394 	(void)printf(" r b w   avm   fre  re at  pi  po  fr  de  sr ");
395 	for (i = 0; i < dk_ndrive; i++)
396 		if (dr_select[i])
397 			(void)printf("%c%c ", dr_name[i][0],
398 			    dr_name[i][strlen(dr_name[i]) - 1]);
399 	(void)printf(" in  sy  cs us sy id\n");
400 }
401 
402 void
403 dotimes()
404 {
405 	u_int pgintime, rectime;
406 
407 	kread(X_REC, &rectime, sizeof(rectime));
408 	kread(X_PGIN, &pgintime, sizeof(pgintime));
409 	kread(X_SUM, &sum, sizeof(sum));
410 	(void)printf("%u reclaims, %u total time (usec)\n",
411 	    sum.v_pgrec, rectime);
412 	(void)printf("average: %u usec / reclaim\n", rectime / sum.v_pgrec);
413 	(void)printf("\n");
414 	(void)printf("%u page ins, %u total time (msec)\n",
415 	    sum.v_pgin, pgintime / 10);
416 	(void)printf("average: %8.1f msec / page in\n",
417 	    pgintime / (sum.v_pgin * 10.0));
418 }
419 
420 pct(top, bot)
421 	long top, bot;
422 {
423 	if (bot == 0)
424 		return(0);
425 	return((top * 100) / bot);
426 }
427 
428 #define	PCT(top, bot) pct((long)(top), (long)(bot))
429 
430 #if defined(tahoe)
431 #include <machine/cpu.h>
432 #endif
433 
434 void
435 dosum()
436 {
437 	struct nchstats nchstats;
438 	struct xstats xstats;
439 	long nchtotal;
440 #if defined(tahoe)
441 	struct keystats keystats;
442 #endif
443 
444 	kread(X_SUM, &sum, sizeof(sum));
445 	(void)printf("%9u swap ins\n", sum.v_swpin);
446 	(void)printf("%9u swap outs\n", sum.v_swpout);
447 	(void)printf("%9u pages swapped in\n", sum.v_pswpin / CLSIZE);
448 	(void)printf("%9u pages swapped out\n", sum.v_pswpout / CLSIZE);
449 	(void)printf("%9u total address trans. faults taken\n", sum.v_faults);
450 	(void)printf("%9u page ins\n", sum.v_pgin);
451 	(void)printf("%9u page outs\n", sum.v_pgout);
452 	(void)printf("%9u pages paged in\n", sum.v_pgpgin);
453 	(void)printf("%9u pages paged out\n", sum.v_pgpgout);
454 	(void)printf("%9u sequential process pages freed\n", sum.v_seqfree);
455 	(void)printf("%9u total reclaims (%d%% fast)\n", sum.v_pgrec,
456 	    PCT(sum.v_fastpgrec, sum.v_pgrec));
457 	(void)printf("%9u reclaims from free list\n", sum.v_pgfrec);
458 	(void)printf("%9u intransit blocking page faults\n", sum.v_intrans);
459 	(void)printf("%9u zero fill pages created\n", sum.v_nzfod / CLSIZE);
460 	(void)printf("%9u zero fill page faults\n", sum.v_zfod / CLSIZE);
461 	(void)printf("%9u executable fill pages created\n",
462 	    sum.v_nexfod / CLSIZE);
463 	(void)printf("%9u executable fill page faults\n",
464 	    sum.v_exfod / CLSIZE);
465 	(void)printf("%9u swap text pages found in free list\n",
466 	    sum.v_xsfrec);
467 	(void)printf("%9u inode text pages found in free list\n",
468 	    sum.v_xifrec);
469 	(void)printf("%9u file fill pages created\n", sum.v_nvrfod / CLSIZE);
470 	(void)printf("%9u file fill page faults\n", sum.v_vrfod / CLSIZE);
471 	(void)printf("%9u pages examined by the clock daemon\n", sum.v_scan);
472 	(void)printf("%9u revolutions of the clock hand\n", sum.v_rev);
473 	(void)printf("%9u pages freed by the clock daemon\n",
474 	    sum.v_dfree / CLSIZE);
475 	(void)printf("%9u cpu context switches\n", sum.v_swtch);
476 	(void)printf("%9u device interrupts\n", sum.v_intr);
477 	(void)printf("%9u software interrupts\n", sum.v_soft);
478 #ifdef vax
479 	(void)printf("%9u pseudo-dma dz interrupts\n", sum.v_pdma);
480 #endif
481 	(void)printf("%9u traps\n", sum.v_trap);
482 	(void)printf("%9u system calls\n", sum.v_syscall);
483 	kread(X_NCHSTATS, &nchstats, sizeof(nchstats));
484 	nchtotal = nchstats.ncs_goodhits + nchstats.ncs_neghits +
485 	    nchstats.ncs_badhits + nchstats.ncs_falsehits +
486 	    nchstats.ncs_miss + nchstats.ncs_long;
487 	(void)printf("%9ld total name lookups\n", nchtotal);
488 	(void)printf(
489 	    "%9s cache hits (%d%% pos + %d%% neg) system %d%% per-process\n",
490 	    "", PCT(nchstats.ncs_goodhits, nchtotal),
491 	    PCT(nchstats.ncs_neghits, nchtotal),
492 	    PCT(nchstats.ncs_pass2, nchtotal));
493 	(void)printf("%9s deletions %d%%, falsehits %d%%, toolong %d%%\n", "",
494 	    PCT(nchstats.ncs_badhits, nchtotal),
495 	    PCT(nchstats.ncs_falsehits, nchtotal),
496 	    PCT(nchstats.ncs_long, nchtotal));
497 	kread(X_XSTATS, &xstats, sizeof(xstats));
498 	(void)printf("%9lu total calls to xalloc (cache hits %d%%)\n",
499 	    xstats.alloc, PCT(xstats.alloc_cachehit, xstats.alloc));
500 	(void)printf("%9s sticky %lu flushed %lu unused %lu\n", "",
501 	    xstats.alloc_inuse, xstats.alloc_cacheflush, xstats.alloc_unused);
502 	(void)printf("%9lu total calls to xfree", xstats.free);
503 	(void)printf(" (sticky %lu cached %lu swapped %lu)\n",
504 	    xstats.free_inuse, xstats.free_cache, xstats.free_cacheswap);
505 #if defined(tahoe)
506 	kread(X_CKEYSTATS, &keystats, sizeof(keystats));
507 	(void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n",
508 	    keystats.ks_allocs, "code cache keys allocated",
509 	    PCT(keystats.ks_allocfree, keystats.ks_allocs),
510 	    PCT(keystats.ks_norefs, keystats.ks_allocs),
511 	    PCT(keystats.ks_taken, keystats.ks_allocs),
512 	    PCT(keystats.ks_shared, keystats.ks_allocs));
513 	kread(X_DKEYSTATS, &keystats, sizeof(keystats));
514 	(void)printf("%9d %s (free %d%% norefs %d%% taken %d%% shared %d%%)\n",
515 	    keystats.ks_allocs, "data cache keys allocated",
516 	    PCT(keystats.ks_allocfree, keystats.ks_allocs),
517 	    PCT(keystats.ks_norefs, keystats.ks_allocs),
518 	    PCT(keystats.ks_taken, keystats.ks_allocs),
519 	    PCT(keystats.ks_shared, keystats.ks_allocs));
520 #endif
521 }
522 
523 void
524 doforkst()
525 {
526 	struct forkstat fks;
527 
528 	kread(X_FORKSTAT, &fks, sizeof(struct forkstat));
529 	(void)printf("%d forks, %d pages, average %.2f\n",
530 	    fks.cntfork, fks.sizfork, (double)fks.sizfork / fks.cntfork);
531 	(void)printf("%d vforks, %d pages, average %.2f\n",
532 	    fks.cntvfork, fks.sizvfork, (double)fks.sizvfork / fks.cntvfork);
533 }
534 
535 void
536 dkstats()
537 {
538 	register int dn, state;
539 	double etime;
540 	long tmp;
541 
542 	for (dn = 0; dn < dk_ndrive; ++dn) {
543 		tmp = cur.xfer[dn];
544 		cur.xfer[dn] -= last.xfer[dn];
545 		last.xfer[dn] = tmp;
546 	}
547 	etime = 0;
548 	for (state = 0; state < CPUSTATES; ++state) {
549 		tmp = cur.time[state];
550 		cur.time[state] -= last.time[state];
551 		last.time[state] = tmp;
552 		etime += cur.time[state];
553 	}
554 	if (etime == 0)
555 		etime = 1;
556 	etime /= hz;
557 	for (dn = 0; dn < dk_ndrive; ++dn) {
558 		if (!dr_select[dn])
559 			continue;
560 		(void)printf("%3.0f", cur.xfer[dn] / etime);
561 	}
562 }
563 
564 void
565 cpustats()
566 {
567 	register int state;
568 	double pct, total;
569 
570 	total = 0;
571 	for (state = 0; state < CPUSTATES; ++state)
572 		total += cur.time[state];
573 	if (total)
574 		pct = 100 / total;
575 	else
576 		pct = 0;
577 	(void)printf("%3.0f",				/* user + nice */
578 	    (cur.time[0] + cur.time[1]) * pct);
579 	(void)printf("%3.0f", cur.time[2] * pct);	/* system */
580 	(void)printf("%3.0f", cur.time[3] * pct);	/* idle */
581 }
582 
583 void
584 dointr()
585 {
586 	register long *intrcnt, inttotal, uptime;
587 	register int nintr, inamlen;
588 	register char *intrname;
589 
590 	uptime = getuptime();
591 	nintr = nl[X_EINTRCNT].n_value - nl[X_INTRCNT].n_value;
592 	inamlen = nl[X_EINTRNAMES].n_value - nl[X_INTRNAMES].n_value;
593 	intrcnt = malloc((size_t)nintr);
594 	intrname = malloc((size_t)inamlen);
595 	if (intrcnt == NULL || intrname == NULL) {
596 		(void)fprintf(stderr, "vmstat: %s.\n", strerror(errno));
597 		exit(1);
598 	}
599 	kread(X_INTRCNT, intrcnt, (size_t)nintr);
600 	kread(X_INTRNAMES, intrname, (size_t)inamlen);
601 	(void)printf("interrupt      total      rate\n");
602 	inttotal = 0;
603 	nintr /= sizeof(long);
604 	while (--nintr >= 0) {
605 		if (*intrcnt)
606 			(void)printf("%-12s %8ld %8ld\n", intrname,
607 			    *intrcnt, *intrcnt / uptime);
608 		intrname += strlen(intrname) + 1;
609 		inttotal += *intrcnt++;
610 	}
611 	(void)printf("Total        %8ld %8ld\n", inttotal, inttotal / uptime);
612 }
613 
614 /*
615  * These names are defined in <sys/malloc.h>.
616  */
617 char *kmemnames[] = INITKMEMNAMES;
618 
619 void
620 domem()
621 {
622 	register struct kmembuckets *kp;
623 	register struct kmemstats *ks;
624 	register int i;
625 	struct kmemstats kmemstats[M_LAST];
626 	struct kmembuckets buckets[MINBUCKET + 16];
627 
628 	kread(X_KMEMBUCKETS, buckets, sizeof(buckets));
629 	(void)printf("Memory statistics by bucket size\n");
630 	(void)printf(
631 	    "    Size   In Use   Free   Requests  HighWater  Couldfree\n");
632 	for (i = MINBUCKET, kp = &buckets[i]; i < MINBUCKET + 16; i++, kp++) {
633 		if (kp->kb_calls == 0)
634 			continue;
635 		(void)printf("%8d%9ld%7ld%11ld%8ld%11ld\n", 1 << i,
636 			kp->kb_total - kp->kb_totalfree,
637 			kp->kb_totalfree, kp->kb_calls,
638 			kp->kb_highwat, kp->kb_couldfree);
639 
640 	}
641 	kread(X_KMEMSTAT, kmemstats, sizeof(kmemstats));
642 	(void)printf("\nMemory statistics by type\n");
643 	(void)printf(
644 "      Type  In Use  MemUse   HighUse  Limit Requests  TypeLimit KernLimit\n");
645 	for (i = 0, ks = &kmemstats[0]; i < M_LAST; i++, ks++) {
646 		if (ks->ks_calls == 0)
647 			continue;
648 		(void)printf("%10s%7ld%8ldK%9ldK%6ldK%9ld%7u%10u\n",
649 		    kmemnames[i] ? kmemnames[i] : "undefined",
650 		    ks->ks_inuse, (ks->ks_memuse + 1023) / 1024,
651 		    (ks->ks_maxused + 1023) / 1024,
652 		    (ks->ks_limit + 1023) / 1024, ks->ks_calls,
653 		    ks->ks_limblocks, ks->ks_mapblocks);
654 	}
655 }
656 
657 void
658 zero()
659 {
660 	struct nlist znl[] = {
661 #undef	X_SUM
662 #define X_SUM		0
663 		{ "_sum" },
664 		{ "" },
665 	};
666 	int fd;
667 	char *kmem;
668 
669 	if (geteuid()) {
670 		(void)fprintf(stderr, "vmstat: %s\n", strerror(EPERM));
671 		exit(1);
672 	}
673 	/*
674 	 * Zeroing the statistics is fundamentally different
675 	 * (and really belongs in a separate program).
676 	 */
677 	if (nlist(vmunix, znl) || nl[0].n_type == 0) {
678 		(void)fprintf(stderr, "vmstat: %s: symbol %s not found\n",
679 		    vmunix, nl[0].n_name);
680 		exit(1);
681 	}
682 
683 	kmem = _PATH_KMEM;
684 	if ((fd = open(kmem, O_RDWR)) < 0) {
685 		(void)fprintf(stderr,
686 		    "vmstat: %s: %s\n", kmem, strerror(errno));
687 		exit(1);
688 	}
689 	if (lseek(fd, (long)nl[0].n_value, L_SET) == -1 ||
690 	    write(fd, &sum, sizeof(sum)) != sizeof(sum)) {
691 		(void)fprintf(stderr,
692 		    "vmstat: %s: %s\n", kmem, strerror(errno));
693 		exit(1);
694 	}
695 }
696 
697 /*
698  * kread reads something from the kernel, given its nlist index.
699  */
700 void
701 kread(nlx, addr, size)
702 	int nlx;
703 	void *addr;
704 	size_t size;
705 {
706 	char *sym;
707 
708 	if (nl[nlx].n_type == 0 || nl[nlx].n_value == 0) {
709 		sym = nl[nlx].n_name;
710 		if (*sym == '_')
711 			++sym;
712 		(void)fprintf(stderr,
713 		    "vmstat: %s: symbol %s not defined\n", vmunix, sym);
714 		exit(1);
715 	}
716 	if (kvm_read((void *)nl[nlx].n_value, addr, size) != size) {
717 		sym = nl[nlx].n_name;
718 		if (*sym == '_')
719 			++sym;
720 		(void)fprintf(stderr, "vmstat: %s: %s\n", sym, kvm_geterr());
721 		exit(1);
722 	}
723 }
724 
725 void
726 usage()
727 {
728 	(void)fprintf(stderr,
729 	    "usage: vmstat [-fimst] [-c count] [-M core] \
730 [-N system] [-w wait] [disks]\n       vmstat -z\n");
731 	exit(1);
732 }
733