1 /* 2 * Copyright 2009 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 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 updates = denom(DELTA(s_sys.ss_sysinfo.updates)); 280 281 if (timestamp_fmt != NODATE) { 282 print_timestamp(timestamp_fmt); 283 lines--; 284 } 285 286 if (--lines <= 0) 287 printhdr(0); 288 289 adj = 0; 290 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; 325 } 326 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); 350 351 (void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL); 352 353 count = df.if_max_iodevs - new->s_nr_iodevs; 354 while (count-- > 0) 355 adjprintf(" %*d", 2, 0); 356 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); 368 } 369 370 /*ARGSUSED*/ 371 static void 372 print_disk(void *v, void *v2, void *d) 373 { 374 struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2; 375 376 if (iodev == NULL) 377 return; 378 379 (void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]); 380 } 381 382 /* ARGSUSED */ 383 static void 384 printhdr(int sig) 385 { 386 int i = df.if_max_iodevs - ss->s_nr_iodevs; 387 388 if (sig == SIGCONT) 389 caught_cont = 1; 390 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; 398 } 399 400 (void) printf(" kthr memory page "); 401 (void) printf("disk faults cpu\n"); 402 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 "); 407 408 (void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL); 409 410 while (i-- > 0) 411 (void) printf("-- "); 412 413 (void) printf(" in sy cs us sy id\n"); 414 lines = REPRINT; 415 } 416 417 static void 418 sum_out(char const *pretty, kstat_t *ks, char *name) 419 { 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); 424 } 425 426 (void) printf("%9llu %s\n", ksn->value.ui64, pretty); 427 } 428 429 static void 430 dosum(struct sys_snapshot *ss) 431 { 432 uint64_t total_faults; 433 kstat_named_t *ksn; 434 long double nchtotal; 435 uint64_t nchhits; 436 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"); 441 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); 446 } 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); 452 } 453 total_faults += ksn->value.ui64; 454 455 (void) printf("%9llu total address trans. faults taken\n", 456 total_faults); 457 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"); 479 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); 485 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"); 490 } 491 492 static void 493 dointr(struct snapshot *ss) 494 { 495 size_t i; 496 ulong_t total = 0; 497 498 (void) printf("interrupt total rate\n"); 499 (void) printf("--------------------------------\n"); 500 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; 506 } 507 508 (void) printf("--------------------------------\n"); 509 (void) printf("Total %10lu %8.0f\n", total, total / etime); 510 } 511 512 static void 513 docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever) 514 { 515 struct snapshot *old; 516 struct snapshot *new; 517 int i; 518 hrtime_t start; 519 520 start = gethrtime(); 521 old = acquire_snapshot(kc, SNAP_FLUSHES, NULL); 522 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; 532 } 533 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"); 539 540 /* Have a kip */ 541 sleep_until(&start, interval, forever, &caught_cont); 542 543 new = acquire_snapshot(kc, SNAP_FLUSHES, NULL); 544 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; 555 } 556 } 557 558 static void 559 usage(void) 560 { 561 (void) fprintf(stderr, 562 "Usage: vmstat [-cipqsS] [-T d|u] [disk ...] " 563 "[interval [count]]\n"); 564 exit(1); 565 } 566