PSARC/2009/105 Time stamp option for xxstat commands
[unleashed.git] / usr / src / cmd / stat / vmstat / vmstat.c
blob0b576d1eceee279672ea3965f64a5d5e477b51c0
1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
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.
12 /* from UCB 5.4 5/17/86 */
13 /* from SunOS 4.1, SID 1.31 */
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>
29 #include "statcommon.h"
31 char *cmdname = "vmstat";
32 int caught_cont = 0;
34 extern uint_t timestamp_fmt;
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;
46 struct iodev_filter df;
48 #define pgtok(a) ((a) * (pagesize >> 10))
49 #define denom(x) ((x) ? (x) : 1)
50 #define REPRINT 19
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);
59 int
60 main(int argc, char **argv)
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;
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);
77 pagesize = sysconf(_SC_PAGESIZE);
78 hz = sysconf(_SC_CLK_TCK);
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();
111 break;
112 default:
113 usage();
116 argc -= optind;
117 argv += optind;
119 /* consistency with iostat */
120 types |= SNAP_CPUS;
122 if (intr)
123 types |= SNAP_INTERRUPTS;
124 if (cflag)
125 types |= SNAP_FLUSHES;
126 if (!intr)
127 types |= SNAP_IODEVS;
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 *));
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++;
143 kc = open_kstat();
145 start_n = gethrtime();
147 ss = acquire_snapshot(kc, types, &df);
149 /* time, in seconds, since boot */
150 etime = ss->s_sys.ss_ticks / hz;
152 if (intr) {
153 dointr(ss);
154 free_snapshot(ss);
155 exit(0);
157 if (summary) {
158 dosum(&ss->s_sys);
159 free_snapshot(ss);
160 exit(0);
163 if (argc > 0) {
164 long interval;
165 char *endptr;
167 errno = 0;
168 interval = strtol(argv[0], &endptr, 10);
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();
187 if (cflag) {
188 free_snapshot(ss);
189 docachestats(kc, period_n, forever);
190 exit(0);
193 (void) sigset(SIGCONT, printhdr);
195 dovmstats(old, ss);
196 while (forever || --iter > 0) {
197 /* (void) poll(NULL, 0, poll_interval); */
199 /* Have a kip */
200 sleep_until(&start_n, period_n, forever, &caught_cont);
202 free_snapshot(old);
203 old = ss;
204 ss = acquire_snapshot(kc, types, &df);
206 if (!suppress_state)
207 snapshot_report_changes(old, ss);
209 /* if config changed, show stats from boot */
210 if (snapshot_has_changed(old, ss)) {
211 free_snapshot(old);
212 old = NULL;
215 dovmstats(old, ss);
218 free_snapshot(old);
219 free_snapshot(ss);
220 free(df.if_names);
221 (void) kstat_close(kc);
222 return (0);
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)
229 static int adj; /* number of excess columns */
231 /*ARGSUSED*/
232 static void
233 show_disk(void *v1, void *v2, void *d)
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;
241 if (new == NULL)
242 return;
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);
254 static void
255 dovmstats(struct snapshot *old, struct snapshot *new)
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 updates;
263 int count;
265 adj = 0;
267 if (old) {
268 oldsys = &old->s_sys.ss_agg_sys;
269 oldvm = &old->s_sys.ss_agg_vm;
272 etime = cpu_ticks_delta(oldsys, newsys);
274 percent_factor = 100.0 / denom(etime);
276 * If any time has passed, convert etime to seconds per CPU
278 etime = etime >= 1.0 ? (etime / nr_active_cpus(new)) / hz : 1.0;
279 updates = denom(DELTA(s_sys.ss_sysinfo.updates));
281 if (timestamp_fmt != NODATE) {
282 print_timestamp();
283 lines--;
286 if (--lines <= 0)
287 printhdr(0);
289 adj = 0;
291 if (pflag) {
292 adjprintf(" %*u", 6,
293 pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail) / updates)));
294 adjprintf(" %*u", 5,
295 pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) / updates)));
296 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "pgrec")
297 / etime);
298 adjprintf(" %*.0f", 3, (kstat_delta(oldvm, newvm, "hat_fault") +
299 kstat_delta(oldvm, newvm, "as_fault")) / etime);
300 adjprintf(" %*.0f", 3, pgtok(kstat_delta(oldvm, newvm, "dfree"))
301 / etime);
302 adjprintf(" %*ld", 3, pgtok(new->s_sys.ss_deficit));
303 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "scan")
304 / etime);
305 adjprintf(" %*.0f", 4,
306 pgtok(kstat_delta(oldvm, newvm, "execpgin")) / etime);
307 adjprintf(" %*.0f", 4,
308 pgtok(kstat_delta(oldvm, newvm, "execpgout")) / etime);
309 adjprintf(" %*.0f", 4,
310 pgtok(kstat_delta(oldvm, newvm, "execfree")) / etime);
311 adjprintf(" %*.0f", 4,
312 pgtok(kstat_delta(oldvm, newvm, "anonpgin")) / etime);
313 adjprintf(" %*.0f", 4,
314 pgtok(kstat_delta(oldvm, newvm, "anonpgout")) / etime);
315 adjprintf(" %*.0f", 4,
316 pgtok(kstat_delta(oldvm, newvm, "anonfree")) / etime);
317 adjprintf(" %*.0f", 4,
318 pgtok(kstat_delta(oldvm, newvm, "fspgin")) / etime);
319 adjprintf(" %*.0f", 4,
320 pgtok(kstat_delta(oldvm, newvm, "fspgout")) / etime);
321 adjprintf(" %*.0f\n", 4,
322 pgtok(kstat_delta(oldvm, newvm, "fsfree")) / etime);
323 (void) fflush(stdout);
324 return;
327 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.runque) / updates);
328 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.waiting) / updates);
329 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.swpque) / updates);
330 adjprintf(" %*u", 6, pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail)
331 / updates)));
332 adjprintf(" %*u", 5, pgtok((int)(DELTA(s_sys.ss_vminfo.freemem)
333 / updates)));
334 adjprintf(" %*.0f", 3, swflag?
335 kstat_delta(oldvm, newvm, "swapin") / etime :
336 kstat_delta(oldvm, newvm, "pgrec") / etime);
337 adjprintf(" %*.0f", 3, swflag?
338 kstat_delta(oldvm, newvm, "swapout") / etime :
339 (kstat_delta(oldvm, newvm, "hat_fault")
340 + kstat_delta(oldvm, newvm, "as_fault"))
341 / etime);
342 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgin"))
343 / etime);
344 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgout"))
345 / etime);
346 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "dfree"))
347 / etime);
348 adjprintf(" %*ld", 2, pgtok(new->s_sys.ss_deficit));
349 adjprintf(" %*.0f", 2, kstat_delta(oldvm, newvm, "scan") / etime);
351 (void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL);
353 count = df.if_max_iodevs - new->s_nr_iodevs;
354 while (count-- > 0)
355 adjprintf(" %*d", 2, 0);
357 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "intr") / etime);
358 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "syscall") / etime);
359 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "pswitch") / etime);
360 adjprintf(" %*.0f", 2,
361 kstat_delta(oldsys, newsys, "cpu_ticks_user") * percent_factor);
362 adjprintf(" %*.0f", 2, kstat_delta(oldsys, newsys, "cpu_ticks_kernel")
363 * percent_factor);
364 adjprintf(" %*.0f\n", 2, (kstat_delta(oldsys, newsys, "cpu_ticks_idle")
365 + kstat_delta(oldsys, newsys, "cpu_ticks_wait"))
366 * percent_factor);
367 (void) fflush(stdout);
370 /*ARGSUSED*/
371 static void
372 print_disk(void *v, void *v2, void *d)
374 struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2;
376 if (iodev == NULL)
377 return;
379 (void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]);
382 /* ARGSUSED */
383 static void
384 printhdr(int sig)
386 int i = df.if_max_iodevs - ss->s_nr_iodevs;
388 if (sig == SIGCONT)
389 caught_cont = 1;
391 if (pflag) {
392 (void) printf(" memory page ");
393 (void) printf("executable anonymous filesystem \n");
394 (void) printf(" swap free re mf fr de sr ");
395 (void) printf("epi epo epf api apo apf fpi fpo fpf\n");
396 lines = REPRINT;
397 return;
400 (void) printf(" kthr memory page ");
401 (void) printf("disk faults cpu\n");
403 if (swflag)
404 (void) printf(" r b w swap free si so pi po fr de sr ");
405 else
406 (void) printf(" r b w swap free re mf pi po fr de sr ");
408 (void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL);
410 while (i-- > 0)
411 (void) printf("-- ");
413 (void) printf(" in sy cs us sy id\n");
414 lines = REPRINT;
417 static void
418 sum_out(char const *pretty, kstat_t *ks, char *name)
420 kstat_named_t *ksn = kstat_data_lookup(ks, name);
421 if (ksn == NULL) {
422 fail(0, "kstat_data_lookup('%s', '%s') failed",
423 ks->ks_name, name);
426 (void) printf("%9llu %s\n", ksn->value.ui64, pretty);
429 static void
430 dosum(struct sys_snapshot *ss)
432 uint64_t total_faults;
433 kstat_named_t *ksn;
434 long double nchtotal;
435 uint64_t nchhits;
437 sum_out("swap ins", &ss->ss_agg_vm, "swapin");
438 sum_out("swap outs", &ss->ss_agg_vm, "swapout");
439 sum_out("pages swapped in", &ss->ss_agg_vm, "pgswapin");
440 sum_out("pages swapped out", &ss->ss_agg_vm, "pgswapout");
442 ksn = kstat_data_lookup(&ss->ss_agg_vm, "hat_fault");
443 if (ksn == NULL) {
444 fail(0, "kstat_data_lookup('%s', 'hat_fault') failed",
445 ss->ss_agg_vm.ks_name);
447 total_faults = ksn->value.ui64;
448 ksn = kstat_data_lookup(&ss->ss_agg_vm, "as_fault");
449 if (ksn == NULL) {
450 fail(0, "kstat_data_lookup('%s', 'as_fault') failed",
451 ss->ss_agg_vm.ks_name);
453 total_faults += ksn->value.ui64;
455 (void) printf("%9llu total address trans. faults taken\n",
456 total_faults);
458 sum_out("page ins", &ss->ss_agg_vm, "pgin");
459 sum_out("page outs", &ss->ss_agg_vm, "pgout");
460 sum_out("pages paged in", &ss->ss_agg_vm, "pgpgin");
461 sum_out("pages paged out", &ss->ss_agg_vm, "pgpgout");
462 sum_out("total reclaims", &ss->ss_agg_vm, "pgrec");
463 sum_out("reclaims from free list", &ss->ss_agg_vm, "pgfrec");
464 sum_out("micro (hat) faults", &ss->ss_agg_vm, "hat_fault");
465 sum_out("minor (as) faults", &ss->ss_agg_vm, "as_fault");
466 sum_out("major faults", &ss->ss_agg_vm, "maj_fault");
467 sum_out("copy-on-write faults", &ss->ss_agg_vm, "cow_fault");
468 sum_out("zero fill page faults", &ss->ss_agg_vm, "zfod");
469 sum_out("pages examined by the clock daemon", &ss->ss_agg_vm, "scan");
470 sum_out("revolutions of the clock hand", &ss->ss_agg_vm, "rev");
471 sum_out("pages freed by the clock daemon", &ss->ss_agg_vm, "dfree");
472 sum_out("forks", &ss->ss_agg_sys, "sysfork");
473 sum_out("vforks", &ss->ss_agg_sys, "sysvfork");
474 sum_out("execs", &ss->ss_agg_sys, "sysexec");
475 sum_out("cpu context switches", &ss->ss_agg_sys, "pswitch");
476 sum_out("device interrupts", &ss->ss_agg_sys, "intr");
477 sum_out("traps", &ss->ss_agg_sys, "trap");
478 sum_out("system calls", &ss->ss_agg_sys, "syscall");
480 nchtotal = (long double) ss->ss_nc.ncs_hits.value.ui64 +
481 (long double) ss->ss_nc.ncs_misses.value.ui64;
482 nchhits = ss->ss_nc.ncs_hits.value.ui64;
483 (void) printf("%9.0Lf total name lookups (cache hits %.0Lf%%)\n",
484 nchtotal, nchhits / denom(nchtotal) * 100);
486 sum_out("user cpu", &ss->ss_agg_sys, "cpu_ticks_user");
487 sum_out("system cpu", &ss->ss_agg_sys, "cpu_ticks_kernel");
488 sum_out("idle cpu", &ss->ss_agg_sys, "cpu_ticks_idle");
489 sum_out("wait cpu", &ss->ss_agg_sys, "cpu_ticks_wait");
492 static void
493 dointr(struct snapshot *ss)
495 size_t i;
496 ulong_t total = 0;
498 (void) printf("interrupt total rate\n");
499 (void) printf("--------------------------------\n");
501 for (i = 0; i < ss->s_nr_intrs; i++) {
502 (void) printf("%-12.8s %10lu %8.0f\n",
503 ss->s_intrs[i].is_name, ss->s_intrs[i].is_total,
504 ss->s_intrs[i].is_total / etime);
505 total += ss->s_intrs[i].is_total;
508 (void) printf("--------------------------------\n");
509 (void) printf("Total %10lu %8.0f\n", total, total / etime);
512 static void
513 docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever)
515 struct snapshot *old;
516 struct snapshot *new;
517 int i;
518 hrtime_t start;
520 start = gethrtime();
521 old = acquire_snapshot(kc, SNAP_FLUSHES, NULL);
523 if (iter == 0) {
524 (void) printf("flush statistics: (totals)\n");
525 (void) printf("%8s%8s%8s%8s%8s%8s\n",
526 "usr", "ctx", "rgn", "seg", "pag", "par");
527 (void) printf(" %7d %7d %7d %7d %7d %7d\n",
528 old->s_flushes.f_usr, old->s_flushes.f_ctx,
529 old->s_flushes.f_region, old->s_flushes.f_segment,
530 old->s_flushes.f_page, old->s_flushes.f_partial);
531 return;
534 (void) printf("flush statistics: (interval based)\n");
535 for (i = 0; i < iter; i++) {
536 if (i % REPRINT == 0)
537 (void) printf("%8s%8s%8s%8s%8s%8s\n",
538 "usr", "ctx", "rgn", "seg", "pag", "par");
540 /* Have a kip */
541 sleep_until(&start, interval, forever, &caught_cont);
543 new = acquire_snapshot(kc, SNAP_FLUSHES, NULL);
545 (void) printf(" %7d %7d %7d %7d %7d %7d\n",
546 new->s_flushes.f_usr - old->s_flushes.f_usr,
547 new->s_flushes.f_ctx - old->s_flushes.f_ctx,
548 new->s_flushes.f_region - old->s_flushes.f_region,
549 new->s_flushes.f_segment - old->s_flushes.f_segment,
550 new->s_flushes.f_page - old->s_flushes.f_page,
551 new->s_flushes.f_partial- old->s_flushes.f_partial);
552 (void) fflush(stdout);
553 free_snapshot(old);
554 old = new;
558 static void
559 usage(void)
561 (void) fprintf(stderr,
562 "Usage: vmstat [-cipqsS] [-T d|u] [disk ...] "
563 "[interval [count]]\n");
564 exit(1);