1 /* 2 * Copyright 2010 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 sys_updates, vm_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 sys_updates = denom(DELTA(s_sys.ss_sysinfo.updates)); 280 vm_updates = denom(DELTA(s_sys.ss_vminfo.updates)); 281 282 if (timestamp_fmt != NODATE) { 283 print_timestamp(timestamp_fmt); 284 lines--; 285 } 286 287 if (--lines <= 0) 288 printhdr(0); 289 290 adj = 0; 291 292 if (pflag) { 293 adjprintf(" %*u", 6, 294 pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail) 295 / vm_updates))); 296 adjprintf(" %*u", 5, 297 pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) / vm_updates))); 298 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "pgrec") 299 / etime); 300 adjprintf(" %*.0f", 3, (kstat_delta(oldvm, newvm, "hat_fault") + 301 kstat_delta(oldvm, newvm, "as_fault")) / etime); 302 adjprintf(" %*.0f", 3, pgtok(kstat_delta(oldvm, newvm, "dfree")) 303 / etime); 304 adjprintf(" %*ld", 3, pgtok(new->s_sys.ss_deficit)); 305 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "scan") 306 / etime); 307 adjprintf(" %*.0f", 4, 308 pgtok(kstat_delta(oldvm, newvm, "execpgin")) / etime); 309 adjprintf(" %*.0f", 4, 310 pgtok(kstat_delta(oldvm, newvm, "execpgout")) / etime); 311 adjprintf(" %*.0f", 4, 312 pgtok(kstat_delta(oldvm, newvm, "execfree")) / etime); 313 adjprintf(" %*.0f", 4, 314 pgtok(kstat_delta(oldvm, newvm, "anonpgin")) / etime); 315 adjprintf(" %*.0f", 4, 316 pgtok(kstat_delta(oldvm, newvm, "anonpgout")) / etime); 317 adjprintf(" %*.0f", 4, 318 pgtok(kstat_delta(oldvm, newvm, "anonfree")) / etime); 319 adjprintf(" %*.0f", 4, 320 pgtok(kstat_delta(oldvm, newvm, "fspgin")) / etime); 321 adjprintf(" %*.0f", 4, 322 pgtok(kstat_delta(oldvm, newvm, "fspgout")) / etime); 323 adjprintf(" %*.0f\n", 4, 324 pgtok(kstat_delta(oldvm, newvm, "fsfree")) / etime); 325 (void) fflush(stdout); 326 return; 327 } 328 329 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.runque) / sys_updates); 330 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.waiting) / sys_updates); 331 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.swpque) / sys_updates); 332 adjprintf(" %*u", 6, pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail) 333 / vm_updates))); 334 adjprintf(" %*u", 5, pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) 335 / vm_updates))); 336 adjprintf(" %*.0f", 3, swflag? 337 kstat_delta(oldvm, newvm, "swapin") / etime : 338 kstat_delta(oldvm, newvm, "pgrec") / etime); 339 adjprintf(" %*.0f", 3, swflag? 340 kstat_delta(oldvm, newvm, "swapout") / etime : 341 (kstat_delta(oldvm, newvm, "hat_fault") 342 + kstat_delta(oldvm, newvm, "as_fault")) 343 / etime); 344 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgin")) 345 / etime); 346 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgout")) 347 / etime); 348 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "dfree")) 349 / etime); 350 adjprintf(" %*ld", 2, pgtok(new->s_sys.ss_deficit)); 351 adjprintf(" %*.0f", 2, kstat_delta(oldvm, newvm, "scan") / etime); 352 353 (void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL); 354 355 count = df.if_max_iodevs - new->s_nr_iodevs; 356 while (count-- > 0) 357 adjprintf(" %*d", 2, 0); 358 359 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "intr") / etime); 360 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "syscall") / etime); 361 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "pswitch") / etime); 362 adjprintf(" %*.0f", 2, 363 kstat_delta(oldsys, newsys, "cpu_ticks_user") * percent_factor); 364 adjprintf(" %*.0f", 2, kstat_delta(oldsys, newsys, "cpu_ticks_kernel") 365 * percent_factor); 366 adjprintf(" %*.0f\n", 2, (kstat_delta(oldsys, newsys, "cpu_ticks_idle") 367 + kstat_delta(oldsys, newsys, "cpu_ticks_wait")) 368 * percent_factor); 369 (void) fflush(stdout); 370 } 371 372 /*ARGSUSED*/ 373 static void 374 print_disk(void *v, void *v2, void *d) 375 { 376 struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2; 377 378 if (iodev == NULL) 379 return; 380 381 (void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]); 382 } 383 384 /* ARGSUSED */ 385 static void 386 printhdr(int sig) 387 { 388 int i = df.if_max_iodevs - ss->s_nr_iodevs; 389 390 if (sig == SIGCONT) 391 caught_cont = 1; 392 393 if (pflag) { 394 (void) printf(" memory page "); 395 (void) printf("executable anonymous filesystem \n"); 396 (void) printf(" swap free re mf fr de sr "); 397 (void) printf("epi epo epf api apo apf fpi fpo fpf\n"); 398 lines = REPRINT; 399 return; 400 } 401 402 (void) printf(" kthr memory page "); 403 (void) printf("disk faults cpu\n"); 404 405 if (swflag) 406 (void) printf(" r b w swap free si so pi po fr de sr "); 407 else 408 (void) printf(" r b w swap free re mf pi po fr de sr "); 409 410 (void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL); 411 412 while (i-- > 0) 413 (void) printf("-- "); 414 415 (void) printf(" in sy cs us sy id\n"); 416 lines = REPRINT; 417 } 418 419 static void 420 sum_out(char const *pretty, kstat_t *ks, char *name) 421 { 422 kstat_named_t *ksn = kstat_data_lookup(ks, name); 423 if (ksn == NULL) { 424 fail(0, "kstat_data_lookup('%s', '%s') failed", 425 ks->ks_name, name); 426 } 427 428 (void) printf("%9llu %s\n", ksn->value.ui64, pretty); 429 } 430 431 static void 432 dosum(struct sys_snapshot *ss) 433 { 434 uint64_t total_faults; 435 kstat_named_t *ksn; 436 long double nchtotal; 437 uint64_t nchhits; 438 439 sum_out("swap ins", &ss->ss_agg_vm, "swapin"); 440 sum_out("swap outs", &ss->ss_agg_vm, "swapout"); 441 sum_out("pages swapped in", &ss->ss_agg_vm, "pgswapin"); 442 sum_out("pages swapped out", &ss->ss_agg_vm, "pgswapout"); 443 444 ksn = kstat_data_lookup(&ss->ss_agg_vm, "hat_fault"); 445 if (ksn == NULL) { 446 fail(0, "kstat_data_lookup('%s', 'hat_fault') failed", 447 ss->ss_agg_vm.ks_name); 448 } 449 total_faults = ksn->value.ui64; 450 ksn = kstat_data_lookup(&ss->ss_agg_vm, "as_fault"); 451 if (ksn == NULL) { 452 fail(0, "kstat_data_lookup('%s', 'as_fault') failed", 453 ss->ss_agg_vm.ks_name); 454 } 455 total_faults += ksn->value.ui64; 456 457 (void) printf("%9llu total address trans. faults taken\n", 458 total_faults); 459 460 sum_out("page ins", &ss->ss_agg_vm, "pgin"); 461 sum_out("page outs", &ss->ss_agg_vm, "pgout"); 462 sum_out("pages paged in", &ss->ss_agg_vm, "pgpgin"); 463 sum_out("pages paged out", &ss->ss_agg_vm, "pgpgout"); 464 sum_out("total reclaims", &ss->ss_agg_vm, "pgrec"); 465 sum_out("reclaims from free list", &ss->ss_agg_vm, "pgfrec"); 466 sum_out("micro (hat) faults", &ss->ss_agg_vm, "hat_fault"); 467 sum_out("minor (as) faults", &ss->ss_agg_vm, "as_fault"); 468 sum_out("major faults", &ss->ss_agg_vm, "maj_fault"); 469 sum_out("copy-on-write faults", &ss->ss_agg_vm, "cow_fault"); 470 sum_out("zero fill page faults", &ss->ss_agg_vm, "zfod"); 471 sum_out("pages examined by the clock daemon", &ss->ss_agg_vm, "scan"); 472 sum_out("revolutions of the clock hand", &ss->ss_agg_vm, "rev"); 473 sum_out("pages freed by the clock daemon", &ss->ss_agg_vm, "dfree"); 474 sum_out("forks", &ss->ss_agg_sys, "sysfork"); 475 sum_out("vforks", &ss->ss_agg_sys, "sysvfork"); 476 sum_out("execs", &ss->ss_agg_sys, "sysexec"); 477 sum_out("cpu context switches", &ss->ss_agg_sys, "pswitch"); 478 sum_out("device interrupts", &ss->ss_agg_sys, "intr"); 479 sum_out("traps", &ss->ss_agg_sys, "trap"); 480 sum_out("system calls", &ss->ss_agg_sys, "syscall"); 481 482 nchtotal = (long double) ss->ss_nc.ncs_hits.value.ui64 + 483 (long double) ss->ss_nc.ncs_misses.value.ui64; 484 nchhits = ss->ss_nc.ncs_hits.value.ui64; 485 (void) printf("%9.0Lf total name lookups (cache hits %.0Lf%%)\n", 486 nchtotal, nchhits / denom(nchtotal) * 100); 487 488 sum_out("user cpu", &ss->ss_agg_sys, "cpu_ticks_user"); 489 sum_out("system cpu", &ss->ss_agg_sys, "cpu_ticks_kernel"); 490 sum_out("idle cpu", &ss->ss_agg_sys, "cpu_ticks_idle"); 491 sum_out("wait cpu", &ss->ss_agg_sys, "cpu_ticks_wait"); 492 } 493 494 static void 495 dointr(struct snapshot *ss) 496 { 497 size_t i; 498 ulong_t total = 0; 499 500 (void) printf("interrupt total rate\n"); 501 (void) printf("--------------------------------\n"); 502 503 for (i = 0; i < ss->s_nr_intrs; i++) { 504 (void) printf("%-12.8s %10lu %8.0f\n", 505 ss->s_intrs[i].is_name, ss->s_intrs[i].is_total, 506 ss->s_intrs[i].is_total / etime); 507 total += ss->s_intrs[i].is_total; 508 } 509 510 (void) printf("--------------------------------\n"); 511 (void) printf("Total %10lu %8.0f\n", total, total / etime); 512 } 513 514 static void 515 docachestats(kstat_ctl_t *kc, hrtime_t interval, int forever) 516 { 517 struct snapshot *old; 518 struct snapshot *new; 519 int i; 520 hrtime_t start; 521 522 start = gethrtime(); 523 old = acquire_snapshot(kc, SNAP_FLUSHES, NULL); 524 525 if (iter == 0) { 526 (void) printf("flush statistics: (totals)\n"); 527 (void) printf("%8s%8s%8s%8s%8s%8s\n", 528 "usr", "ctx", "rgn", "seg", "pag", "par"); 529 (void) printf(" %7d %7d %7d %7d %7d %7d\n", 530 old->s_flushes.f_usr, old->s_flushes.f_ctx, 531 old->s_flushes.f_region, old->s_flushes.f_segment, 532 old->s_flushes.f_page, old->s_flushes.f_partial); 533 return; 534 } 535 536 (void) printf("flush statistics: (interval based)\n"); 537 for (i = 0; i < iter; i++) { 538 if (i % REPRINT == 0) 539 (void) printf("%8s%8s%8s%8s%8s%8s\n", 540 "usr", "ctx", "rgn", "seg", "pag", "par"); 541 542 /* Have a kip */ 543 sleep_until(&start, interval, forever, &caught_cont); 544 545 new = acquire_snapshot(kc, SNAP_FLUSHES, NULL); 546 547 (void) printf(" %7d %7d %7d %7d %7d %7d\n", 548 new->s_flushes.f_usr - old->s_flushes.f_usr, 549 new->s_flushes.f_ctx - old->s_flushes.f_ctx, 550 new->s_flushes.f_region - old->s_flushes.f_region, 551 new->s_flushes.f_segment - old->s_flushes.f_segment, 552 new->s_flushes.f_page - old->s_flushes.f_page, 553 new->s_flushes.f_partial- old->s_flushes.f_partial); 554 (void) fflush(stdout); 555 free_snapshot(old); 556 old = new; 557 } 558 } 559 560 static void 561 usage(void) 562 { 563 (void) fprintf(stderr, 564 "Usage: vmstat [-cipqsS] [-T d|u] [disk ...] " 565 "[interval [count]]\n"); 566 exit(1); 567 } 568