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