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 }