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 char *endptr; 163 164 errno = 0; 165 interval = strtol(argv[0], &endptr, 10); 166 167 if (errno > 0 || *endptr != '\0' || interval <= 0 || 168 interval > MAXINT) 169 usage(); 170 period_n = (hrtime_t)interval * NANOSEC; 171 if (period_n <= 0) 172 usage(); 173 iter = MAXLONG; 174 if (argc > 1) { 175 iter = strtol(argv[1], NULL, 10); 176 if (errno > 0 || *endptr != '\0' || iter <= 0) 177 usage(); 178 } else 179 forever = 1; 180 if (argc > 2) 181 usage(); 182 } 183 184 (void) sigset(SIGCONT, printhdr); 185 186 dovmstats(old, ss); 187 while (forever || --iter > 0) { 188 /* (void) poll(NULL, 0, poll_interval); */ 189 190 /* Have a kip */ 191 sleep_until(&start_n, period_n, forever, &caught_cont); 192 193 free_snapshot(old); 194 old = ss; 195 ss = acquire_snapshot(kc, types, &df); 196 197 if (!suppress_state) 198 snapshot_report_changes(old, ss); 199 200 /* if config changed, show stats from boot */ 201 if (snapshot_has_changed(old, ss)) { 202 free_snapshot(old); 203 old = NULL; 204 } 205 206 dovmstats(old, ss); 207 } 208 209 free_snapshot(old); 210 free_snapshot(ss); 211 free(df.if_names); 212 (void) kstat_close(kc); 213 return (0); 214 } 215 216 #define DELTA(v) (new->v - (old ? old->v : 0)) 217 #define ADJ(n) ((adj <= 0) ? n : (adj >= n) ? 1 : n - adj) 218 #define adjprintf(fmt, n, val) adj -= (n + 1) - printf(fmt, ADJ(n), val) 219 220 static int adj; /* number of excess columns */ 221 222 /*ARGSUSED*/ 223 static void 224 show_disk(void *v1, void *v2, void *d) 225 { 226 struct iodev_snapshot *old = (struct iodev_snapshot *)v1; 227 struct iodev_snapshot *new = (struct iodev_snapshot *)v2; 228 hrtime_t oldtime = new->is_crtime; 229 double hr_etime; 230 double reads, writes; 231 232 if (old) 233 oldtime = old->is_stats.wlastupdate; 234 hr_etime = new->is_stats.wlastupdate - oldtime; 235 if (hr_etime == 0.0) 236 hr_etime = NANOSEC; 237 reads = new->is_stats.reads - (old ? old->is_stats.reads : 0); 238 writes = new->is_stats.writes - (old ? old->is_stats.writes : 0); 239 adjprintf(" %*.0f", 2, (reads + writes) / hr_etime * NANOSEC); 240 } 241 242 static void 243 dovmstats(struct snapshot *old, struct snapshot *new) 244 { 245 kstat_t *oldsys = NULL; 246 kstat_t *newsys = &new->s_sys.ss_agg_sys; 247 kstat_t *oldvm = NULL; 248 kstat_t *newvm = &new->s_sys.ss_agg_vm; 249 double percent_factor; 250 ulong_t sys_updates, vm_updates; 251 int count; 252 253 adj = 0; 254 255 if (old) { 256 oldsys = &old->s_sys.ss_agg_sys; 257 oldvm = &old->s_sys.ss_agg_vm; 258 } 259 260 etime = cpu_ticks_delta(oldsys, newsys); 261 262 percent_factor = 100.0 / denom(etime); 263 /* 264 * If any time has passed, convert etime to seconds per CPU 265 */ 266 etime = etime >= 1.0 ? (etime / nr_active_cpus(new)) / hz : 1.0; 267 sys_updates = denom(DELTA(s_sys.ss_sysinfo.updates)); 268 vm_updates = denom(DELTA(s_sys.ss_vminfo.updates)); 269 270 if (timestamp_fmt != NODATE) { 271 print_timestamp(timestamp_fmt); 272 lines--; 273 } 274 275 if (--lines <= 0) 276 printhdr(0); 277 278 adj = 0; 279 280 if (pflag) { 281 adjprintf(" %*u", 6, 282 pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail) 283 / vm_updates))); 284 adjprintf(" %*u", 5, 285 pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) / vm_updates))); 286 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "pgrec") 287 / etime); 288 adjprintf(" %*.0f", 3, (kstat_delta(oldvm, newvm, "hat_fault") + 289 kstat_delta(oldvm, newvm, "as_fault")) / etime); 290 adjprintf(" %*.0f", 3, pgtok(kstat_delta(oldvm, newvm, "dfree")) 291 / etime); 292 adjprintf(" %*ld", 3, pgtok(new->s_sys.ss_deficit)); 293 adjprintf(" %*.0f", 3, kstat_delta(oldvm, newvm, "scan") 294 / etime); 295 adjprintf(" %*.0f", 4, 296 pgtok(kstat_delta(oldvm, newvm, "execpgin")) / etime); 297 adjprintf(" %*.0f", 4, 298 pgtok(kstat_delta(oldvm, newvm, "execpgout")) / etime); 299 adjprintf(" %*.0f", 4, 300 pgtok(kstat_delta(oldvm, newvm, "execfree")) / etime); 301 adjprintf(" %*.0f", 4, 302 pgtok(kstat_delta(oldvm, newvm, "anonpgin")) / etime); 303 adjprintf(" %*.0f", 4, 304 pgtok(kstat_delta(oldvm, newvm, "anonpgout")) / etime); 305 adjprintf(" %*.0f", 4, 306 pgtok(kstat_delta(oldvm, newvm, "anonfree")) / etime); 307 adjprintf(" %*.0f", 4, 308 pgtok(kstat_delta(oldvm, newvm, "fspgin")) / etime); 309 adjprintf(" %*.0f", 4, 310 pgtok(kstat_delta(oldvm, newvm, "fspgout")) / etime); 311 adjprintf(" %*.0f\n", 4, 312 pgtok(kstat_delta(oldvm, newvm, "fsfree")) / etime); 313 (void) fflush(stdout); 314 return; 315 } 316 317 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.runque) / sys_updates); 318 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.waiting) / sys_updates); 319 adjprintf(" %*lu", 1, DELTA(s_sys.ss_sysinfo.swpque) / sys_updates); 320 adjprintf(" %*u", 6, pgtok((int)(DELTA(s_sys.ss_vminfo.swap_avail) 321 / vm_updates))); 322 adjprintf(" %*u", 5, pgtok((int)(DELTA(s_sys.ss_vminfo.freemem) 323 / vm_updates))); 324 adjprintf(" %*.0f", 3, swflag? 325 kstat_delta(oldvm, newvm, "swapin") / etime : 326 kstat_delta(oldvm, newvm, "pgrec") / etime); 327 adjprintf(" %*.0f", 3, swflag? 328 kstat_delta(oldvm, newvm, "swapout") / etime : 329 (kstat_delta(oldvm, newvm, "hat_fault") 330 + kstat_delta(oldvm, newvm, "as_fault")) 331 / etime); 332 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgin")) 333 / etime); 334 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "pgpgout")) 335 / etime); 336 adjprintf(" %*.0f", 2, pgtok(kstat_delta(oldvm, newvm, "dfree")) 337 / etime); 338 adjprintf(" %*ld", 2, pgtok(new->s_sys.ss_deficit)); 339 adjprintf(" %*.0f", 2, kstat_delta(oldvm, newvm, "scan") / etime); 340 341 (void) snapshot_walk(SNAP_IODEVS, old, new, show_disk, NULL); 342 343 count = df.if_max_iodevs - new->s_nr_iodevs; 344 while (count-- > 0) 345 adjprintf(" %*d", 2, 0); 346 347 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "intr") / etime); 348 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "syscall") / etime); 349 adjprintf(" %*.0f", 4, kstat_delta(oldsys, newsys, "pswitch") / etime); 350 adjprintf(" %*.0f", 2, 351 kstat_delta(oldsys, newsys, "cpu_ticks_user") * percent_factor); 352 adjprintf(" %*.0f", 2, kstat_delta(oldsys, newsys, "cpu_ticks_kernel") 353 * percent_factor); 354 adjprintf(" %*.0f\n", 2, (kstat_delta(oldsys, newsys, "cpu_ticks_idle") 355 + kstat_delta(oldsys, newsys, "cpu_ticks_wait")) 356 * percent_factor); 357 (void) fflush(stdout); 358 } 359 360 /*ARGSUSED*/ 361 static void 362 print_disk(void *v, void *v2, void *d) 363 { 364 struct iodev_snapshot *iodev = (struct iodev_snapshot *)v2; 365 366 if (iodev == NULL) 367 return; 368 369 (void) printf("%c%c ", iodev->is_name[0], iodev->is_name[2]); 370 } 371 372 /* ARGSUSED */ 373 static void 374 printhdr(int sig) 375 { 376 int i = df.if_max_iodevs - ss->s_nr_iodevs; 377 378 if (sig == SIGCONT) 379 caught_cont = 1; 380 381 if (pflag) { 382 (void) printf(" memory page "); 383 (void) printf("executable anonymous filesystem \n"); 384 (void) printf(" swap free re mf fr de sr "); 385 (void) printf("epi epo epf api apo apf fpi fpo fpf\n"); 386 lines = REPRINT; 387 return; 388 } 389 390 (void) printf(" kthr memory page "); 391 (void) printf("disk faults cpu\n"); 392 393 if (swflag) 394 (void) printf(" r b w swap free si so pi po fr de sr "); 395 else 396 (void) printf(" r b w swap free re mf pi po fr de sr "); 397 398 (void) snapshot_walk(SNAP_IODEVS, NULL, ss, print_disk, NULL); 399 400 while (i-- > 0) 401 (void) printf("-- "); 402 403 (void) printf(" in sy cs us sy id\n"); 404 lines = REPRINT; 405 } 406 407 static void 408 sum_out(char const *pretty, kstat_t *ks, char *name) 409 { 410 kstat_named_t *ksn = kstat_data_lookup(ks, name); 411 if (ksn == NULL) { 412 fail(0, "kstat_data_lookup('%s', '%s') failed", 413 ks->ks_name, name); 414 } 415 416 (void) printf("%9llu %s\n", ksn->value.ui64, pretty); 417 } 418 419 static void 420 dosum(struct sys_snapshot *ss) 421 { 422 uint64_t total_faults; 423 kstat_named_t *ksn; 424 long double nchtotal; 425 uint64_t nchhits; 426 427 sum_out("swap ins", &ss->ss_agg_vm, "swapin"); 428 sum_out("swap outs", &ss->ss_agg_vm, "swapout"); 429 sum_out("pages swapped in", &ss->ss_agg_vm, "pgswapin"); 430 sum_out("pages swapped out", &ss->ss_agg_vm, "pgswapout"); 431 432 ksn = kstat_data_lookup(&ss->ss_agg_vm, "hat_fault"); 433 if (ksn == NULL) { 434 fail(0, "kstat_data_lookup('%s', 'hat_fault') failed", 435 ss->ss_agg_vm.ks_name); 436 } 437 total_faults = ksn->value.ui64; 438 ksn = kstat_data_lookup(&ss->ss_agg_vm, "as_fault"); 439 if (ksn == NULL) { 440 fail(0, "kstat_data_lookup('%s', 'as_fault') failed", 441 ss->ss_agg_vm.ks_name); 442 } 443 total_faults += ksn->value.ui64; 444 445 (void) printf("%9llu total address trans. faults taken\n", 446 total_faults); 447 448 sum_out("page ins", &ss->ss_agg_vm, "pgin"); 449 sum_out("page outs", &ss->ss_agg_vm, "pgout"); 450 sum_out("pages paged in", &ss->ss_agg_vm, "pgpgin"); 451 sum_out("pages paged out", &ss->ss_agg_vm, "pgpgout"); 452 sum_out("total reclaims", &ss->ss_agg_vm, "pgrec"); 453 sum_out("reclaims from free list", &ss->ss_agg_vm, "pgfrec"); 454 sum_out("micro (hat) faults", &ss->ss_agg_vm, "hat_fault"); 455 sum_out("minor (as) faults", &ss->ss_agg_vm, "as_fault"); 456 sum_out("major faults", &ss->ss_agg_vm, "maj_fault"); 457 sum_out("copy-on-write faults", &ss->ss_agg_vm, "cow_fault"); 458 sum_out("zero fill page faults", &ss->ss_agg_vm, "zfod"); 459 sum_out("pages examined by the clock daemon", &ss->ss_agg_vm, "scan"); 460 sum_out("revolutions of the clock hand", &ss->ss_agg_vm, "rev"); 461 sum_out("pages freed by the clock daemon", &ss->ss_agg_vm, "dfree"); 462 sum_out("forks", &ss->ss_agg_sys, "sysfork"); 463 sum_out("vforks", &ss->ss_agg_sys, "sysvfork"); 464 sum_out("execs", &ss->ss_agg_sys, "sysexec"); 465 sum_out("cpu context switches", &ss->ss_agg_sys, "pswitch"); 466 sum_out("device interrupts", &ss->ss_agg_sys, "intr"); 467 sum_out("traps", &ss->ss_agg_sys, "trap"); 468 sum_out("system calls", &ss->ss_agg_sys, "syscall"); 469 470 nchtotal = (long double) ss->ss_nc.ncs_hits.value.ui64 + 471 (long double) ss->ss_nc.ncs_misses.value.ui64; 472 nchhits = ss->ss_nc.ncs_hits.value.ui64; 473 (void) printf("%9.0Lf total name lookups (cache hits %.0Lf%%)\n", 474 nchtotal, nchhits / denom(nchtotal) * 100); 475 476 sum_out("user cpu", &ss->ss_agg_sys, "cpu_ticks_user"); 477 sum_out("system cpu", &ss->ss_agg_sys, "cpu_ticks_kernel"); 478 sum_out("idle cpu", &ss->ss_agg_sys, "cpu_ticks_idle"); 479 sum_out("wait cpu", &ss->ss_agg_sys, "cpu_ticks_wait"); 480 } 481 482 static void 483 dointr(struct snapshot *ss) 484 { 485 size_t i; 486 ulong_t total = 0; 487 488 (void) printf("interrupt total rate\n"); 489 (void) printf("--------------------------------\n"); 490 491 for (i = 0; i < ss->s_nr_intrs; i++) { 492 (void) printf("%-12.8s %10lu %8.0f\n", 493 ss->s_intrs[i].is_name, ss->s_intrs[i].is_total, 494 ss->s_intrs[i].is_total / etime); 495 total += ss->s_intrs[i].is_total; 496 } 497 498 (void) printf("--------------------------------\n"); 499 (void) printf("Total %10lu %8.0f\n", total, total / etime); 500 } 501 502 static void 503 usage(void) 504 { 505 (void) fprintf(stderr, 506 "Usage: vmstat [-ipqsS] [-T d|u] [disk ...] " 507 "[interval [count]]\n"); 508 exit(1); 509 }