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