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