xref: /illumos-gate/usr/src/cmd/stat/vmstat/vmstat.c (revision e79c98e6c943cb3032f272714ff4ce6137d40394)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1980 Regents of the University of California.
8  * All rights reserved.  The Berkeley software License Agreement
9  * specifies the terms and conditions for redistribution.
10  */
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 /* from UCB 5.4 5/17/86 */
15 /* from SunOS 4.1, SID 1.31 */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <ctype.h>
21 #include <unistd.h>
22 #include <memory.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <signal.h>
27 #include <values.h>
28 #include <poll.h>
29 
30 #include "statcommon.h"
31 
32 char *cmdname = "vmstat";
33 int caught_cont = 0;
34 
35 static	int	hz;
36 static	int	pagesize;
37 static	double	etime;
38 static	int	lines = 1;
39 static	int	swflag = 0, cflag = 0, pflag = 0;
40 static	int	suppress_state;
41 static	long	iter = 0;
42 static	hrtime_t period_n = 0;
43 static  struct	snapshot *ss;
44 
45 struct iodev_filter df;
46 
47 #define	pgtok(a) ((a) * (pagesize >> 10))
48 #define	denom(x) ((x) ? (x) : 1)
49 #define	REPRINT	19
50 
51 static	void	dovmstats(struct snapshot *old, struct snapshot *new);
52 static	void	printhdr(int);
53 static	void	dosum(struct sys_snapshot *ss);
54 static	void	dointr(struct snapshot *ss);
55 static	void	docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever);
56 static	void	usage(void);
57 
58 int
59 main(int argc, char **argv)
60 {
61 	struct snapshot *old = NULL;
62 	enum snapshot_types types = SNAP_SYSTEM;
63 	int summary = 0;
64 	int intr = 0;
65 	kstat_ctl_t *kc;
66 	int forever = 0;
67 	hrtime_t start_n;
68 
69 	pagesize = sysconf(_SC_PAGESIZE);
70 	hz = sysconf(_SC_CLK_TCK);
71 
72 	argc--, argv++;
73 	while (argc > 0 && argv[0][0] == '-') {
74 		char *cp = *argv++;
75 		argc--;
76 		while (*++cp) {
77 			switch (*cp) {
78 
79 			case 'S':
80 				swflag = !swflag;
81 				break;
82 			case 's':
83 				summary = 1;
84 				break;
85 			case 'i':
86 				intr = 1;
87 				break;
88 			case 'c':
89 				cflag++;
90 				break;
91 			case 'q':
92 				suppress_state = 1;
93 				break;
94 			case 'p':
95 				pflag++;	/* detailed paging info */
96 				break;
97 			default:
98 				usage();
99 			}
100 		}
101 	}
102 
103 	/* consistency with iostat */
104 	types |= SNAP_CPUS;
105 
106 	if (intr)
107 		types |= SNAP_INTERRUPTS;
108 	if (cflag)
109 		types |= SNAP_FLUSHES;
110 	if (!intr)
111 		types |= SNAP_IODEVS;
112 
113 	/* max to fit in less than 80 characters */
114 	df.if_max_iodevs = 4;
115 	df.if_allowed_types = IODEV_DISK;
116 	df.if_nr_names = 0;
117 	df.if_names = safe_alloc(df.if_max_iodevs * sizeof (char *));
118 	(void) memset(df.if_names, 0, df.if_max_iodevs * sizeof (char *));
119 
120 	while (argc > 0 && !isdigit(argv[0][0]) &&
121 		df.if_nr_names < df.if_max_iodevs) {
122 		df.if_names[df.if_nr_names] = *argv;
123 		df.if_nr_names++;
124 		argc--, argv++;
125 	}
126 
127 	kc = open_kstat();
128 
129 	start_n = gethrtime();
130 
131 	ss = acquire_snapshot(kc, types, &df);
132 
133 	/* time, in seconds, since boot */
134 	etime = ss->s_sys.ss_ticks / hz;
135 
136 	if (intr) {
137 		dointr(ss);
138 		free_snapshot(ss);
139 		exit(0);
140 	}
141 	if (summary) {
142 		dosum(&ss->s_sys);
143 		free_snapshot(ss);
144 		exit(0);
145 	}
146 
147 	if (argc > 0) {
148 		long interval;
149 		char *endptr;
150 
151 		errno = 0;
152 		interval = strtol(argv[0], &endptr, 10);
153 
154 		if (errno > 0 || *endptr != '\0' || interval <= 0 ||
155 		    interval > MAXINT)
156 			usage();
157 		period_n = (hrtime_t)interval * NANOSEC;
158 		if (period_n <= 0)
159 			usage();
160 		iter = MAXLONG;
161 		if (argc > 1) {
162 			iter = strtol(argv[1], NULL, 10);
163 			if (errno > 0 || *endptr != '\0' || iter <= 0)
164 				usage();
165 		} else
166 			forever = 1;
167 		if (argc > 2)
168 			usage();
169 	}
170 
171 	if (cflag) {
172 		free_snapshot(ss);
173 		docachestats(kc, period_n, forever);
174 		exit(0);
175 	}
176 
177 	(void) sigset(SIGCONT, printhdr);
178 
179 	printhdr(0);
180 	dovmstats(old, ss);
181 	while (forever || --iter > 0) {
182 		/* (void) poll(NULL, 0, poll_interval); */
183 
184 		/* Have a kip */
185 		sleep_until(&start_n, period_n, forever, &caught_cont);
186 
187 		free_snapshot(old);
188 		old = ss;
189 		ss = acquire_snapshot(kc, types, &df);
190 
191 		if (!suppress_state)
192 			snapshot_report_changes(old, ss);
193 
194 		/* if config changed, show stats from boot */
195 		if (snapshot_has_changed(old, ss)) {
196 			free_snapshot(old);
197 			old = NULL;
198 		}
199 
200 		dovmstats(old, ss);
201 	}
202 
203 	free_snapshot(old);
204 	free_snapshot(ss);
205 	free(df.if_names);
206 	(void) kstat_close(kc);
207 	return (0);
208 }
209 
210 #define	DELTA(v) (new->v - (old ? old->v : 0))
211 #define	ADJ(n)	((adj <= 0) ? n : (adj >= n) ? 1 : n - adj)
212 #define	adjprintf(fmt, n, val)	adj -= (n + 1) - printf(fmt, ADJ(n), val)
213 
214 static int adj;	/* number of excess columns */
215 
216 /*ARGSUSED*/
217 static void
218 show_disk(void *v1, void *v2, void *d)
219 {
220 	struct iodev_snapshot *old = (struct iodev_snapshot *)v1;
221 	struct iodev_snapshot *new = (struct iodev_snapshot *)v2;
222 	hrtime_t oldtime = new->is_crtime;
223 	double hr_etime;
224 	double reads, writes;
225 
226 	if (new == NULL)
227 		return;
228 
229 	if (old)
230 		oldtime = old->is_stats.wlastupdate;
231 	hr_etime = new->is_stats.wlastupdate - oldtime;
232 	if (hr_etime == 0.0)
233 		hr_etime = NANOSEC;
234 	reads = new->is_stats.reads - (old ? old->is_stats.reads : 0);
235 	writes = new->is_stats.writes - (old ? old->is_stats.writes : 0);
236 	adjprintf(" %*.0f", 2, (reads + writes) / hr_etime * NANOSEC);
237 }
238 
239 static void
240 dovmstats(struct snapshot *old, struct snapshot *new)
241 {
242 	kstat_t *oldsys = NULL;
243 	kstat_t *newsys = &new->s_sys.ss_agg_sys;
244 	kstat_t *oldvm = NULL;
245 	kstat_t *newvm = &new->s_sys.ss_agg_vm;
246 	double percent_factor;
247 	ulong_t updates;
248 	int count;
249 
250 	adj = 0;
251 
252 	if (old) {
253 		oldsys = &old->s_sys.ss_agg_sys;
254 		oldvm = &old->s_sys.ss_agg_vm;
255 	}
256 
257 	etime = cpu_ticks_delta(oldsys, newsys);
258 
259 	percent_factor = 100.0 / denom(etime);
260 	/*
261 	 * If any time has passed, convert etime to seconds per CPU
262 	 */
263 	etime = etime >= 1.0 ? (etime / nr_active_cpus(new)) / hz : 1.0;
264 	updates = denom(DELTA(s_sys.ss_sysinfo.updates));
265 
266 	if (--lines == 0)
267 		printhdr(0);
268 
269 	adj = 0;
270 
271 	if (pflag) {
272 		adjprintf(" %*u", 6,
273 		    pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail) / updates)));
274 		adjprintf(" %*u", 5,
275 		    pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) / updates)));
276 		adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "pgrec")
277 		    / etime);
278 		adjprintf(" %*.0f", 3, (kstat_delta(oldvm, newvm, "hat_fault") +
279 		    kstat_delta(oldvm, newvm, "as_fault")) / etime);
280 		adjprintf(" %*.0f", 3, pgtok(kstat_delta(oldvm, newvm, "dfree"))
281 		    / etime);
282 		adjprintf(" %*ld", 3, pgtok(new->s_sys.ss_deficit));
283 		adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "scan")
284 		    / etime);
285 		adjprintf(" %*.0f", 4,
286 		    pgtok(kstat_delta(oldvm, newvm, "execpgin")) / etime);
287 		adjprintf(" %*.0f", 4,
288 		    pgtok(kstat_delta(oldvm, newvm, "execpgout")) / etime);
289 		adjprintf(" %*.0f", 4,
290 		    pgtok(kstat_delta(oldvm, newvm, "execfree")) / etime);
291 		adjprintf(" %*.0f", 4,
292 		    pgtok(kstat_delta(oldvm, newvm, "anonpgin")) / etime);
293 		adjprintf(" %*.0f", 4,
294 		    pgtok(kstat_delta(oldvm, newvm, "anonpgout")) / etime);
295 		adjprintf(" %*.0f", 4,
296 		    pgtok(kstat_delta(oldvm, newvm, "anonfree")) / etime);
297 		adjprintf(" %*.0f", 4,
298 		    pgtok(kstat_delta(oldvm, newvm, "fspgin")) / etime);
299 		adjprintf(" %*.0f", 4,
300 		    pgtok(kstat_delta(oldvm, newvm, "fspgout")) / etime);
301 		adjprintf(" %*.0f\n", 4,
302 		    pgtok(kstat_delta(oldvm, newvm, "fsfree")) / etime);
303 		(void) fflush(stdout);
304 		return;
305 	}
306 
307 	adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.runque) / updates);
308 	adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.waiting) / updates);
309 	adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.swpque) / updates);
310 	adjprintf(" %*u", 6, pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail)
311 	    / updates)));
312 	adjprintf(" %*u", 5, pgtok((int)(DELTA(s_sys.ss_vminfo.freemem)
313 						/ updates)));
314 	adjprintf(" %*.0f", 3, swflag?
315 	    kstat_delta(oldvm, newvm, "swapin") / etime :
316 	    kstat_delta(oldvm, newvm, "pgrec") / etime);
317 	adjprintf(" %*.0f", 3, swflag?
318 	    kstat_delta(oldvm, newvm, "swapout") / etime :
319 	    (kstat_delta(oldvm, newvm, "hat_fault")
320 	    + kstat_delta(oldvm, newvm, "as_fault"))
321 	    / etime);
322 	adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgin"))
323 	    / etime);
324 	adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgout"))
325 	    / etime);
326 	adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "dfree"))
327 	    / etime);
328 	adjprintf(" %*ld", 2, pgtok(new->s_sys.ss_deficit));
329 	adjprintf(" %*.0f", 2, kstat_delta(oldvm, newvm, "scan") / etime);
330 
331 	(void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL);
332 
333 	count = df.if_max_iodevs - new->s_nr_iodevs;
334 	while (count-- > 0)
335 		adjprintf(" %*d", 2, 0);
336 
337 	adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "intr") / etime);
338 	adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "syscall") / etime);
339 	adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "pswitch") / etime);
340 	adjprintf(" %*.0f", 2,
341 	    kstat_delta(oldsys, newsys, "cpu_ticks_user") * percent_factor);
342 	adjprintf(" %*.0f", 2, kstat_delta(oldsys, newsys, "cpu_ticks_kernel")
343 	    * percent_factor);
344 	adjprintf(" %*.0f\n", 2, (kstat_delta(oldsys, newsys, "cpu_ticks_idle")
345 	    + kstat_delta(oldsys, newsys, "cpu_ticks_wait"))
346 	    * percent_factor);
347 	(void) fflush(stdout);
348 }
349 
350 /*ARGSUSED*/
351 static void
352 print_disk(void *v, void *v2, void *d)
353 {
354 	struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2;
355 
356 	if (iodev == NULL)
357 		return;
358 
359 	(void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]);
360 }
361 
362 /* ARGSUSED */
363 static void
364 printhdr(int sig)
365 {
366 	int i = df.if_max_iodevs - ss->s_nr_iodevs;
367 
368 	if (sig == SIGCONT)
369 		caught_cont = 1;
370 
371 	if (pflag) {
372 		(void) printf("     memory           page          ");
373 		(void) printf("executable      anonymous      filesystem \n");
374 		(void) printf("   swap  free  re  mf  fr  de  sr  ");
375 		(void) printf("epi  epo  epf  api  apo  apf  fpi  fpo  fpf\n");
376 		lines = REPRINT;
377 		return;
378 	}
379 
380 	(void) printf(" kthr      memory            page            ");
381 	(void) printf("disk          faults      cpu\n");
382 
383 	if (swflag)
384 		(void) printf(" r b w   swap  free  si  so pi po fr de sr ");
385 	else
386 		(void) printf(" r b w   swap  free  re  mf pi po fr de sr ");
387 
388 	(void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL);
389 
390 	while (i-- > 0)
391 		(void) printf("-- ");
392 
393 	(void) printf("  in   sy   cs us sy id\n");
394 	lines = REPRINT;
395 }
396 
397 static void
398 sum_out(char const *pretty, kstat_t *ks, char *name)
399 {
400 	kstat_named_t *ksn = kstat_data_lookup(ks, name);
401 	if (ksn == NULL) {
402 		fail(0, "kstat_data_lookup('%s', '%s') failed",
403 			ks->ks_name, name);
404 	}
405 
406 	(void) printf("%9llu %s\n", ksn->value.ui64, pretty);
407 }
408 
409 static void
410 dosum(struct sys_snapshot *ss)
411 {
412 	uint64_t total_faults;
413 	kstat_named_t *ksn;
414 	long double nchtotal;
415 	uint64_t nchhits;
416 
417 	sum_out("swap ins", &ss->ss_agg_vm, "swapin");
418 	sum_out("swap outs", &ss->ss_agg_vm, "swapout");
419 	sum_out("pages swapped in", &ss->ss_agg_vm, "pgswapin");
420 	sum_out("pages swapped out", &ss->ss_agg_vm, "pgswapout");
421 
422 	ksn = kstat_data_lookup(&ss->ss_agg_vm, "hat_fault");
423 	if (ksn == NULL) {
424 		fail(0, "kstat_data_lookup('%s', 'hat_fault') failed",
425 			ss->ss_agg_vm.ks_name);
426 	}
427 	total_faults = ksn->value.ui64;
428 	ksn = kstat_data_lookup(&ss->ss_agg_vm, "as_fault");
429 	if (ksn == NULL) {
430 		fail(0, "kstat_data_lookup('%s', 'as_fault') failed",
431 			ss->ss_agg_vm.ks_name);
432 	}
433 	total_faults += ksn->value.ui64;
434 
435 	(void) printf("%9llu total address trans. faults taken\n",
436 		total_faults);
437 
438 	sum_out("page ins", &ss->ss_agg_vm, "pgin");
439 	sum_out("page outs", &ss->ss_agg_vm, "pgout");
440 	sum_out("pages paged in", &ss->ss_agg_vm, "pgpgin");
441 	sum_out("pages paged out", &ss->ss_agg_vm, "pgpgout");
442 	sum_out("total reclaims", &ss->ss_agg_vm, "pgrec");
443 	sum_out("reclaims from free list", &ss->ss_agg_vm, "pgfrec");
444 	sum_out("micro (hat) faults", &ss->ss_agg_vm, "hat_fault");
445 	sum_out("minor (as) faults", &ss->ss_agg_vm, "as_fault");
446 	sum_out("major faults", &ss->ss_agg_vm, "maj_fault");
447 	sum_out("copy-on-write faults", &ss->ss_agg_vm, "cow_fault");
448 	sum_out("zero fill page faults", &ss->ss_agg_vm, "zfod");
449 	sum_out("pages examined by the clock daemon", &ss->ss_agg_vm, "scan");
450 	sum_out("revolutions of the clock hand", &ss->ss_agg_vm, "rev");
451 	sum_out("pages freed by the clock daemon", &ss->ss_agg_vm, "dfree");
452 	sum_out("forks", &ss->ss_agg_sys, "sysfork");
453 	sum_out("vforks", &ss->ss_agg_sys, "sysvfork");
454 	sum_out("execs", &ss->ss_agg_sys, "sysexec");
455 	sum_out("cpu context switches", &ss->ss_agg_sys, "pswitch");
456 	sum_out("device interrupts", &ss->ss_agg_sys, "intr");
457 	sum_out("traps", &ss->ss_agg_sys, "trap");
458 	sum_out("system calls", &ss->ss_agg_sys, "syscall");
459 
460 	nchtotal = (long double) ss->ss_nc.ncs_hits.value.ui64 +
461 	    (long double) ss->ss_nc.ncs_misses.value.ui64;
462 	nchhits = ss->ss_nc.ncs_hits.value.ui64;
463 	(void) printf("%9.0Lf total name lookups (cache hits %.0Lf%%)\n",
464 	    nchtotal, nchhits / denom(nchtotal) * 100);
465 
466 	sum_out("user   cpu", &ss->ss_agg_sys, "cpu_ticks_user");
467 	sum_out("system cpu", &ss->ss_agg_sys, "cpu_ticks_kernel");
468 	sum_out("idle   cpu", &ss->ss_agg_sys, "cpu_ticks_idle");
469 	sum_out("wait   cpu", &ss->ss_agg_sys, "cpu_ticks_wait");
470 }
471 
472 static void
473 dointr(struct snapshot *ss)
474 {
475 	size_t i;
476 	ulong_t total = 0;
477 
478 	(void) printf("interrupt         total     rate\n");
479 	(void) printf("--------------------------------\n");
480 
481 	for (i = 0; i < ss->s_nr_intrs; i++) {
482 		(void) printf("%-12.8s %10lu %8.0f\n",
483 			ss->s_intrs[i].is_name, ss->s_intrs[i].is_total,
484 			ss->s_intrs[i].is_total / etime);
485 		total += ss->s_intrs[i].is_total;
486 	}
487 
488 	(void) printf("--------------------------------\n");
489 	(void) printf("Total        %10lu %8.0f\n", total, total / etime);
490 }
491 
492 static void
493 docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever)
494 {
495 	struct snapshot *old;
496 	struct snapshot *new;
497 	int i;
498 	hrtime_t start;
499 
500 	start = gethrtime();
501 	old = acquire_snapshot(kc, SNAP_FLUSHES, NULL);
502 
503 	if (iter == 0) {
504 		(void) printf("flush statistics: (totals)\n");
505 		(void) printf("%8s%8s%8s%8s%8s%8s\n",
506 			"usr", "ctx", "rgn", "seg", "pag", "par");
507 		(void) printf(" %7d %7d %7d %7d %7d %7d\n",
508 			old->s_flushes.f_usr, old->s_flushes.f_ctx,
509 			old->s_flushes.f_region, old->s_flushes.f_segment,
510 			old->s_flushes.f_page, old->s_flushes.f_partial);
511 		return;
512 	}
513 
514 	(void) printf("flush statistics: (interval based)\n");
515 	for (i = 0; i < iter; i++) {
516 		if (i % REPRINT == 0)
517 			(void) printf("%8s%8s%8s%8s%8s%8s\n",
518 			    "usr", "ctx", "rgn", "seg", "pag", "par");
519 
520 		/* Have a kip */
521 		sleep_until(&start, interval, forever, &caught_cont);
522 
523 		new = acquire_snapshot(kc, SNAP_FLUSHES, NULL);
524 
525 		(void) printf(" %7d %7d %7d %7d %7d %7d\n",
526 			new->s_flushes.f_usr - old->s_flushes.f_usr,
527 			new->s_flushes.f_ctx - old->s_flushes.f_ctx,
528 			new->s_flushes.f_region - old->s_flushes.f_region,
529 			new->s_flushes.f_segment - old->s_flushes.f_segment,
530 			new->s_flushes.f_page - old->s_flushes.f_page,
531 			new->s_flushes.f_partial- old->s_flushes.f_partial);
532 		(void) fflush(stdout);
533 		free_snapshot(old);
534 		old = new;
535 	}
536 }
537 
538 static void
539 usage(void)
540 {
541 	(void) fprintf(stderr,
542 		"Usage: vmstat [-cipqsS] [disk ...] [interval [count]]\n");
543 	exit(1);
544 }
545