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