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 }