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