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