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
main(int argc,char ** argv)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 char *endptr;
163
164 errno = 0;
165 interval = strtol(argv[0], &endptr, 10);
166
167 if (errno > 0 || *endptr != '\0' || interval <= 0 ||
168 interval > MAXINT)
169 usage();
170 period_n = (hrtime_t)interval * NANOSEC;
171 if (period_n <= 0)
172 usage();
173 iter = MAXLONG;
174 if (argc > 1) {
175 iter = strtol(argv[1], NULL, 10);
176 if (errno > 0 || *endptr != '\0' || iter <= 0)
177 usage();
178 } else
179 forever = 1;
180 if (argc > 2)
181 usage();
182 }
183
184 (void) sigset(SIGCONT, printhdr);
185
186 dovmstats(old, ss);
187 while (forever || --iter > 0) {
188 /* (void) poll(NULL, 0, poll_interval); */
189
190 /* Have a kip */
191 sleep_until(&start_n, period_n, forever, &caught_cont);
192
193 free_snapshot(old);
194 old = ss;
195 ss = acquire_snapshot(kc, types, &df);
196
197 if (!suppress_state)
198 snapshot_report_changes(old, ss);
199
200 /* if config changed, show stats from boot */
201 if (snapshot_has_changed(old, ss)) {
202 free_snapshot(old);
203 old = NULL;
204 }
205
206 dovmstats(old, ss);
207 }
208
209 free_snapshot(old);
210 free_snapshot(ss);
211 free(df.if_names);
212 (void) kstat_close(kc);
213 return (0);
214 }
215
216 #define DELTA(v) (new->v - (old ? old->v : 0))
217 #define ADJ(n) ((adj <= 0) ? n : (adj >= n) ? 1 : n - adj)
218 #define adjprintf(fmt, n, val) adj -= (n + 1) - printf(fmt, ADJ(n), val)
219
220 static int adj; /* number of excess columns */
221
222 /*ARGSUSED*/
223 static void
show_disk(void * v1,void * v2,void * d)224 show_disk(void *v1, void *v2, void *d)
225 {
226 struct iodev_snapshot *old = (struct iodev_snapshot *)v1;
227 struct iodev_snapshot *new = (struct iodev_snapshot *)v2;
228 hrtime_t oldtime = new->is_crtime;
229 double hr_etime;
230 double reads, writes;
231
232 if (old)
233 oldtime = old->is_stats.wlastupdate;
234 hr_etime = new->is_stats.wlastupdate - oldtime;
235 if (hr_etime == 0.0)
236 hr_etime = NANOSEC;
237 reads = new->is_stats.reads - (old ? old->is_stats.reads : 0);
238 writes = new->is_stats.writes - (old ? old->is_stats.writes : 0);
239 adjprintf(" %*.0f", 2, (reads + writes) / hr_etime * NANOSEC);
240 }
241
242 static void
dovmstats(struct snapshot * old,struct snapshot * new)243 dovmstats(struct snapshot *old, struct snapshot *new)
244 {
245 kstat_t *oldsys = NULL;
246 kstat_t *newsys = &new->s_sys.ss_agg_sys;
247 kstat_t *oldvm = NULL;
248 kstat_t *newvm = &new->s_sys.ss_agg_vm;
249 double percent_factor;
250 ulong_t sys_updates, vm_updates;
251 int count;
252
253 adj = 0;
254
255 if (old) {
256 oldsys = &old->s_sys.ss_agg_sys;
257 oldvm = &old->s_sys.ss_agg_vm;
258 }
259
260 etime = cpu_ticks_delta(oldsys, newsys);
261
262 percent_factor = 100.0 / denom(etime);
263 /*
264 * If any time has passed, convert etime to seconds per CPU
265 */
266 etime = etime >= 1.0 ? (etime / nr_active_cpus(new)) / hz : 1.0;
267 sys_updates = denom(DELTA(s_sys.ss_sysinfo.updates));
268 vm_updates = denom(DELTA(s_sys.ss_vminfo.updates));
269
270 if (timestamp_fmt != NODATE) {
271 print_timestamp(timestamp_fmt);
272 lines--;
273 }
274
275 if (--lines <= 0)
276 printhdr(0);
277
278 adj = 0;
279
280 if (pflag) {
281 adjprintf(" %*u", 6,
282 pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail)
283 / vm_updates)));
284 adjprintf(" %*u", 5,
285 pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) / vm_updates)));
286 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "pgrec")
287 / etime);
288 adjprintf(" %*.0f", 3, (kstat_delta(oldvm, newvm, "hat_fault") +
289 kstat_delta(oldvm, newvm, "as_fault")) / etime);
290 adjprintf(" %*.0f", 3, pgtok(kstat_delta(oldvm, newvm, "dfree"))
291 / etime);
292 adjprintf(" %*ld", 3, pgtok(new->s_sys.ss_deficit));
293 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "scan")
294 / etime);
295 adjprintf(" %*.0f", 4,
296 pgtok(kstat_delta(oldvm, newvm, "execpgin")) / etime);
297 adjprintf(" %*.0f", 4,
298 pgtok(kstat_delta(oldvm, newvm, "execpgout")) / etime);
299 adjprintf(" %*.0f", 4,
300 pgtok(kstat_delta(oldvm, newvm, "execfree")) / etime);
301 adjprintf(" %*.0f", 4,
302 pgtok(kstat_delta(oldvm, newvm, "anonpgin")) / etime);
303 adjprintf(" %*.0f", 4,
304 pgtok(kstat_delta(oldvm, newvm, "anonpgout")) / etime);
305 adjprintf(" %*.0f", 4,
306 pgtok(kstat_delta(oldvm, newvm, "anonfree")) / etime);
307 adjprintf(" %*.0f", 4,
308 pgtok(kstat_delta(oldvm, newvm, "fspgin")) / etime);
309 adjprintf(" %*.0f", 4,
310 pgtok(kstat_delta(oldvm, newvm, "fspgout")) / etime);
311 adjprintf(" %*.0f\n", 4,
312 pgtok(kstat_delta(oldvm, newvm, "fsfree")) / etime);
313 (void) fflush(stdout);
314 return;
315 }
316
317 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.runque) / sys_updates);
318 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.waiting) / sys_updates);
319 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.swpque) / sys_updates);
320 adjprintf(" %*u", 6, pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail)
321 / vm_updates)));
322 adjprintf(" %*u", 5, pgtok((int)(DELTA(s_sys.ss_vminfo.freemem)
323 / vm_updates)));
324 adjprintf(" %*.0f", 3, swflag?
325 kstat_delta(oldvm, newvm, "swapin") / etime :
326 kstat_delta(oldvm, newvm, "pgrec") / etime);
327 adjprintf(" %*.0f", 3, swflag?
328 kstat_delta(oldvm, newvm, "swapout") / etime :
329 (kstat_delta(oldvm, newvm, "hat_fault")
330 + kstat_delta(oldvm, newvm, "as_fault"))
331 / etime);
332 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgin"))
333 / etime);
334 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgout"))
335 / etime);
336 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "dfree"))
337 / etime);
338 adjprintf(" %*ld", 2, pgtok(new->s_sys.ss_deficit));
339 adjprintf(" %*.0f", 2, kstat_delta(oldvm, newvm, "scan") / etime);
340
341 (void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL);
342
343 count = df.if_max_iodevs - new->s_nr_iodevs;
344 while (count-- > 0)
345 adjprintf(" %*d", 2, 0);
346
347 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "intr") / etime);
348 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "syscall") / etime);
349 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "pswitch") / etime);
350 adjprintf(" %*.0f", 2,
351 kstat_delta(oldsys, newsys, "cpu_ticks_user") * percent_factor);
352 adjprintf(" %*.0f", 2, kstat_delta(oldsys, newsys, "cpu_ticks_kernel")
353 * percent_factor);
354 adjprintf(" %*.0f\n", 2, (kstat_delta(oldsys, newsys, "cpu_ticks_idle")
355 + kstat_delta(oldsys, newsys, "cpu_ticks_wait"))
356 * percent_factor);
357 (void) fflush(stdout);
358 }
359
360 /*ARGSUSED*/
361 static void
print_disk(void * v,void * v2,void * d)362 print_disk(void *v, void *v2, void *d)
363 {
364 struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2;
365
366 if (iodev == NULL)
367 return;
368
369 (void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]);
370 }
371
372 /* ARGSUSED */
373 static void
printhdr(int sig)374 printhdr(int sig)
375 {
376 int i = df.if_max_iodevs - ss->s_nr_iodevs;
377
378 if (sig == SIGCONT)
379 caught_cont = 1;
380
381 if (pflag) {
382 (void) printf(" memory page ");
383 (void) printf("executable anonymous filesystem \n");
384 (void) printf(" swap free re mf fr de sr ");
385 (void) printf("epi epo epf api apo apf fpi fpo fpf\n");
386 lines = REPRINT;
387 return;
388 }
389
390 (void) printf(" kthr memory page ");
391 (void) printf("disk faults cpu\n");
392
393 if (swflag)
394 (void) printf(" r b w swap free si so pi po fr de sr ");
395 else
396 (void) printf(" r b w swap free re mf pi po fr de sr ");
397
398 (void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL);
399
400 while (i-- > 0)
401 (void) printf("-- ");
402
403 (void) printf(" in sy cs us sy id\n");
404 lines = REPRINT;
405 }
406
407 static void
sum_out(char const * pretty,kstat_t * ks,char * name)408 sum_out(char const *pretty, kstat_t *ks, char *name)
409 {
410 kstat_named_t *ksn = kstat_data_lookup(ks, name);
411 if (ksn == NULL) {
412 fail(0, "kstat_data_lookup('%s', '%s') failed",
413 ks->ks_name, name);
414 }
415
416 (void) printf("%9llu %s\n", ksn->value.ui64, pretty);
417 }
418
419 static void
dosum(struct sys_snapshot * ss)420 dosum(struct sys_snapshot *ss)
421 {
422 uint64_t total_faults;
423 kstat_named_t *ksn;
424 long double nchtotal;
425 uint64_t nchhits;
426
427 sum_out("swap ins", &ss->ss_agg_vm, "swapin");
428 sum_out("swap outs", &ss->ss_agg_vm, "swapout");
429 sum_out("pages swapped in", &ss->ss_agg_vm, "pgswapin");
430 sum_out("pages swapped out", &ss->ss_agg_vm, "pgswapout");
431
432 ksn = kstat_data_lookup(&ss->ss_agg_vm, "hat_fault");
433 if (ksn == NULL) {
434 fail(0, "kstat_data_lookup('%s', 'hat_fault') failed",
435 ss->ss_agg_vm.ks_name);
436 }
437 total_faults = ksn->value.ui64;
438 ksn = kstat_data_lookup(&ss->ss_agg_vm, "as_fault");
439 if (ksn == NULL) {
440 fail(0, "kstat_data_lookup('%s', 'as_fault') failed",
441 ss->ss_agg_vm.ks_name);
442 }
443 total_faults += ksn->value.ui64;
444
445 (void) printf("%9llu total address trans. faults taken\n",
446 total_faults);
447
448 sum_out("page ins", &ss->ss_agg_vm, "pgin");
449 sum_out("page outs", &ss->ss_agg_vm, "pgout");
450 sum_out("pages paged in", &ss->ss_agg_vm, "pgpgin");
451 sum_out("pages paged out", &ss->ss_agg_vm, "pgpgout");
452 sum_out("total reclaims", &ss->ss_agg_vm, "pgrec");
453 sum_out("reclaims from free list", &ss->ss_agg_vm, "pgfrec");
454 sum_out("micro (hat) faults", &ss->ss_agg_vm, "hat_fault");
455 sum_out("minor (as) faults", &ss->ss_agg_vm, "as_fault");
456 sum_out("major faults", &ss->ss_agg_vm, "maj_fault");
457 sum_out("copy-on-write faults", &ss->ss_agg_vm, "cow_fault");
458 sum_out("zero fill page faults", &ss->ss_agg_vm, "zfod");
459 sum_out("pages examined by the clock daemon", &ss->ss_agg_vm, "scan");
460 sum_out("revolutions of the clock hand", &ss->ss_agg_vm, "rev");
461 sum_out("pages freed by the clock daemon", &ss->ss_agg_vm, "dfree");
462 sum_out("forks", &ss->ss_agg_sys, "sysfork");
463 sum_out("vforks", &ss->ss_agg_sys, "sysvfork");
464 sum_out("execs", &ss->ss_agg_sys, "sysexec");
465 sum_out("cpu context switches", &ss->ss_agg_sys, "pswitch");
466 sum_out("device interrupts", &ss->ss_agg_sys, "intr");
467 sum_out("traps", &ss->ss_agg_sys, "trap");
468 sum_out("system calls", &ss->ss_agg_sys, "syscall");
469
470 nchtotal = (long double) ss->ss_nc.ncs_hits.value.ui64 +
471 (long double) ss->ss_nc.ncs_misses.value.ui64;
472 nchhits = ss->ss_nc.ncs_hits.value.ui64;
473 (void) printf("%9.0Lf total name lookups (cache hits %.0Lf%%)\n",
474 nchtotal, nchhits / denom(nchtotal) * 100);
475
476 sum_out("user cpu", &ss->ss_agg_sys, "cpu_ticks_user");
477 sum_out("system cpu", &ss->ss_agg_sys, "cpu_ticks_kernel");
478 sum_out("idle cpu", &ss->ss_agg_sys, "cpu_ticks_idle");
479 sum_out("wait cpu", &ss->ss_agg_sys, "cpu_ticks_wait");
480 }
481
482 static void
dointr(struct snapshot * ss)483 dointr(struct snapshot *ss)
484 {
485 size_t i;
486 ulong_t total = 0;
487
488 (void) printf("interrupt total rate\n");
489 (void) printf("--------------------------------\n");
490
491 for (i = 0; i < ss->s_nr_intrs; i++) {
492 (void) printf("%-12.8s %10lu %8.0f\n",
493 ss->s_intrs[i].is_name, ss->s_intrs[i].is_total,
494 ss->s_intrs[i].is_total / etime);
495 total += ss->s_intrs[i].is_total;
496 }
497
498 (void) printf("--------------------------------\n");
499 (void) printf("Total %10lu %8.0f\n", total, total / etime);
500 }
501
502 static void
usage(void)503 usage(void)
504 {
505 (void) fprintf(stderr,
506 "Usage: vmstat [-ipqsS] [-T d|u] [disk ...] "
507 "[interval [count]]\n");
508 exit(1);
509 }
510