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