Print this page
    
NEX-3729 KRRP changes mess up iostat(1M)
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-4426 iostat(1M) should be able to handle KSTAT_DATA_STRING
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/stat/iostat/iostat.c
          +++ new/usr/src/cmd/stat/iostat/iostat.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
  25   25   *
  26   26   * rewritten from UCB 4.13 83/09/25
  27   27   * rewritten from SunOS 4.1 SID 1.18 89/10/06
  28   28   */
  29   29  /*
  30   30   * Copyright (c) 2012 by Delphix. All rights reserved.
  31   31   * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  32   32   * Copyright 2016 James S. Blachly, MD. All rights reserved.
  33   33   */
  34   34  
  35   35  #include <stdio.h>
  36   36  #include <stdlib.h>
  37   37  #include <stdarg.h>
  38   38  #include <ctype.h>
  39   39  #include <unistd.h>
  40   40  #include <memory.h>
  41   41  #include <errno.h>
  42   42  #include <string.h>
  43   43  #include <signal.h>
  44   44  #include <sys/types.h>
  45   45  #include <time.h>
  46   46  #include <sys/time.h>
  47   47  #include <sys/sysinfo.h>
  48   48  #include <inttypes.h>
  49   49  #include <strings.h>
  50   50  #include <sys/systeminfo.h>
  51   51  #include <kstat.h>
  52   52  #include <locale.h>
  53   53  
  54   54  #include "dsr.h"
  55   55  #include "statcommon.h"
  56   56  
  57   57  #define DISK_OLD                0x0001
  58   58  #define DISK_NEW                0x0002
  59   59  #define DISK_EXTENDED           0x0004
  60   60  #define DISK_ERRORS             0x0008
  61   61  #define DISK_EXTENDED_ERRORS    0x0010
  62   62  #define DISK_IOPATH_LI          0x0020  /* LunInitiator */
  63   63  #define DISK_IOPATH_LTI         0x0040  /* LunTargetInitiator */
  64   64  
  65   65  #define DISK_NORMAL             (DISK_OLD | DISK_NEW)
  66   66  #define DISK_IO_MASK            (DISK_OLD | DISK_NEW | DISK_EXTENDED)
  67   67  #define DISK_ERROR_MASK         (DISK_ERRORS | DISK_EXTENDED_ERRORS)
  68   68  #define PRINT_VERTICAL          (DISK_ERROR_MASK | DISK_EXTENDED)
  69   69  
  70   70  #define REPRINT 19
  71   71  
  72   72  #define NUMBER_OF_ERR_COUNTERS  3
  73   73  
  74   74  /*
  75   75   * It's really a pseudo-gigabyte. We use 1000000000 bytes so that the disk
  76   76   * labels don't look bad. 1GB is really 1073741824 bytes.
  77   77   */
  78   78  #define DISK_GIGABYTE   1000000000.0
  79   79  
  80   80  /*
  81   81   * Function desciptor to be called when extended
  82   82   * headers are used.
  83   83   */
  84   84  typedef struct formatter {
  85   85          void (*nfunc)(void);
  86   86          struct formatter *next;
  87   87  } format_t;
  88   88  
  89   89  /*
  90   90   * Used to get formatting right when printing tty/cpu
  91   91   * data to the right of disk data
  92   92   */
  93   93  enum show_disk_mode {
  94   94          SHOW_FIRST_ONLY,
  95   95          SHOW_SECOND_ONWARDS,
  96   96          SHOW_ALL
  97   97  };
  98   98  
  99   99  enum show_disk_mode show_disk_mode = SHOW_ALL;
 100  100  
 101  101  char *cmdname = "iostat";
 102  102  int caught_cont = 0;
 103  103  
 104  104  static char one_blank[] = " ";
 105  105  static char two_blanks[] = "  ";
 106  106  
 107  107  /*
 108  108   * count for number of lines to be emitted before a header is
 109  109   * shown again. Only used for the basic format.
 110  110   */
 111  111  static  uint_t  tohdr = 1;
 112  112  
 113  113  /*
 114  114   * If we're in raw format, have we printed a header? We only do it
 115  115   * once for raw but we emit it every REPRINT lines in non-raw format.
 116  116   * This applies only for the basic header. The extended header is
 117  117   * done only once in both formats.
 118  118   */
 119  119  static  uint_t  hdr_out;
 120  120  
  
    | 
      ↓ open down ↓ | 
    120 lines elided | 
    
      ↑ open up ↑ | 
  
 121  121  /*
 122  122   * Flags representing arguments from command line
 123  123   */
 124  124  static  uint_t  do_tty;                 /* show tty info (-t) */
 125  125  static  uint_t  do_disk;                /* show disk info per selected */
 126  126                                          /* format (-d, -D, -e, -E, -x -X -Y) */
 127  127  static  uint_t  do_cpu;                 /* show cpu info (-c) */
 128  128  static  uint_t  do_interval;            /* do intervals (-I) */
 129  129  static  int     do_partitions;          /* per-partition stats (-p) */
 130  130  static  int     do_partitions_only;     /* per-partition stats only (-P) */
      131 +static  int     do_zfs;
      132 +static  int     do_zfs_only;
 131  133                                          /* no per-device stats for disks */
 132  134  static  uint_t  do_conversions;         /* display disks as cXtYdZ (-n) */
 133  135  static  uint_t  do_megabytes;           /* display data in MB/sec (-M) */
 134  136  static  uint_t  do_controller;          /* display controller info (-C) */
 135  137  static  uint_t  do_raw;                 /* emit raw format (-r) */
 136  138  static  uint_t  timestamp_fmt = NODATE; /* timestamp  each display (-T) */
 137  139  static  uint_t  do_devid;               /* -E should show devid */
 138  140  
 139  141  /*
 140  142   * Default number of disk drives to be displayed in basic format
 141  143   */
 142  144  #define DEFAULT_LIMIT   4
 143  145  
 144  146  struct iodev_filter df;
 145  147  
  
    | 
      ↓ open down ↓ | 
    5 lines elided | 
    
      ↑ open up ↑ | 
  
 146  148  static  uint_t  suppress_state;         /* skip state change messages */
 147  149  static  uint_t  suppress_zero;          /* skip zero valued lines */
 148  150  static  uint_t  show_mountpts;          /* show mount points */
 149  151  static  int     interval;               /* interval (seconds) to output */
 150  152  static  int     iter;                   /* iterations from command line */
 151  153  
 152  154  #define SMALL_SCRATCH_BUFLEN    MAXNAMELEN
 153  155  
 154  156  static int      iodevs_nl;              /* name field width */
 155  157  #define IODEVS_NL_MIN           6       /* not too thin for "device" */
 156      -#define IODEVS_NL_MAX           24      /* but keep full width under 80 */
      158 +#define IODEVS_NL_MAX           64      /* but keep full width under 80 */
 157  159  
 158  160  static  char    disk_header[132];
 159  161  static  uint_t  dh_len;                 /* disk header length for centering */
 160  162  static  int     lineout;                /* data waiting to be printed? */
 161  163  
 162  164  static struct snapshot *newss;
 163  165  static struct snapshot *oldss;
 164  166  static  double  getime;                 /* elapsed time */
 165  167  static  double  percent;                /* 100 / etime */
 166  168  
 167  169  /*
 168  170   * List of functions to be called which will construct the desired output
 169  171   */
 170  172  static format_t *formatter_list;
 171  173  static format_t *formatter_end;
 172  174  
 173  175  static u_longlong_t     ull_delta(u_longlong_t, u_longlong_t);
 174  176  static uint_t   u32_delta(uint_t, uint_t);
 175  177  static void setup(void (*nfunc)(void));
 176  178  static void print_tty_hdr1(void);
 177  179  static void print_tty_hdr2(void);
 178  180  static void print_cpu_hdr1(void);
 179  181  static void print_cpu_hdr2(void);
 180  182  static void print_tty_data(void);
 181  183  static void print_cpu_data(void);
 182  184  static void print_err_hdr(void);
 183  185  static void print_disk_header(void);
 184  186  static void hdrout(void);
 185  187  static void disk_errors(void);
 186  188  static void do_newline(void);
 187  189  static void push_out(const char *, ...);
 188  190  static void printhdr(int);
 189  191  static void printxhdr(void);
 190  192  static void usage(void);
 191  193  static void do_args(int, char **);
 192  194  static void do_format(void);
 193  195  static void show_all_disks(void);
 194  196  static void show_first_disk(void);
 195  197  static void show_other_disks(void);
 196  198  static void show_disk_errors(void *, void *, void *);
 197  199  static void write_core_header(void);
 198  200  static int  fzero(double value);
 199  201  static int  safe_strtoi(char const *val, char *errmsg);
 200  202  
 201  203  int
 202  204  main(int argc, char **argv)
 203  205  {
 204  206          enum snapshot_types types = SNAP_SYSTEM;
 205  207          kstat_ctl_t *kc;
 206  208          long hz;
 207  209          int forever;
 208  210          hrtime_t start_n;
 209  211          hrtime_t period_n;
 210  212  
 211  213          (void) setlocale(LC_ALL, "");
 212  214  #if !defined(TEXT_DOMAIN)               /* Should be defined by cc -D */
 213  215  #define TEXT_DOMAIN "SYS_TEST"          /* Use this only if it weren't */
 214  216  #endif
 215  217          (void) textdomain(TEXT_DOMAIN);
 216  218  
 217  219          do_args(argc, argv);
  
    | 
      ↓ open down ↓ | 
    51 lines elided | 
    
      ↑ open up ↑ | 
  
 218  220  
 219  221          /*
 220  222           * iostat historically showed CPU changes, even though
 221  223           * it doesn't provide much useful information
 222  224           */
 223  225          types |= SNAP_CPUS;
 224  226  
 225  227          if (do_disk)
 226  228                  types |= SNAP_IODEVS;
 227  229  
 228      -        if (do_disk && !do_partitions_only)
      230 +        if (do_disk && !do_partitions_only && !do_zfs_only)
 229  231                  df.if_allowed_types |= IODEV_DISK;
 230  232          if (do_disk & DISK_IOPATH_LI) {
 231  233                  df.if_allowed_types |= IODEV_IOPATH_LTI;
 232  234                  types |= SNAP_IOPATHS_LI;
 233  235          }
 234  236          if (do_disk & DISK_IOPATH_LTI) {
 235  237                  df.if_allowed_types |= IODEV_IOPATH_LTI;
 236  238                  types |= SNAP_IOPATHS_LTI;
 237  239          }
 238  240          if (do_disk & DISK_ERROR_MASK)
 239  241                  types |= SNAP_IODEV_ERRORS;
 240  242          if (do_partitions || do_partitions_only)
 241  243                  df.if_allowed_types |= IODEV_PARTITION;
      244 +        if (do_zfs || do_zfs_only)
      245 +                df.if_allowed_types |= IODEV_ZFS;
 242  246          if (do_conversions)
 243  247                  types |= SNAP_IODEV_PRETTY;
 244  248          if (do_devid)
 245  249                  types |= SNAP_IODEV_DEVID;
 246  250          if (do_controller) {
 247  251                  if (!(do_disk & PRINT_VERTICAL) ||
 248  252                      (do_disk & DISK_EXTENDED_ERRORS))
 249  253                          fail(0, "-C can only be used with -e or -x.");
 250  254                  types |= SNAP_CONTROLLERS;
 251  255                  df.if_allowed_types |= IODEV_CONTROLLER;
 252  256          }
 253  257  
 254  258          hz = sysconf(_SC_CLK_TCK);
 255  259  
 256  260          /*
 257  261           * Undocumented behavior - sending a SIGCONT will result
 258  262           * in a new header being emitted. Used only if we're not
 259  263           * doing extended headers. This is a historical
 260  264           * artifact.
 261  265           */
 262  266          if (!(do_disk & PRINT_VERTICAL))
 263  267                  (void) signal(SIGCONT, printhdr);
 264  268  
 265  269          if (interval)
 266  270                  period_n = (hrtime_t)interval * NANOSEC;
 267  271  
 268  272          kc = open_kstat();
 269  273          if (interval)
 270  274                  start_n = gethrtime();
 271  275          newss = acquire_snapshot(kc, types, &df);
 272  276  
 273  277          /* compute width of "device" field */
 274  278          iodevs_nl = newss->s_iodevs_is_name_maxlen;
 275  279          iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ?
 276  280              IODEVS_NL_MIN : iodevs_nl;
 277  281          iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ?
 278  282              IODEVS_NL_MAX : iodevs_nl;
 279  283  
 280  284          do_format();
 281  285  
 282  286          forever = (iter == 0);
 283  287          do {
 284  288                  if (do_conversions && show_mountpts)
 285  289                          do_mnttab();
 286  290  
 287  291                  if (do_tty || do_cpu) {
 288  292                          kstat_t *oldks;
 289  293                          oldks = oldss ? &oldss->s_sys.ss_agg_sys : NULL;
 290  294                          getime = cpu_ticks_delta(oldks,
 291  295                              &newss->s_sys.ss_agg_sys);
 292  296                          percent = (getime > 0.0) ? 100.0 / getime : 0.0;
 293  297                          getime = (getime / nr_active_cpus(newss)) / hz;
 294  298                          if (getime == 0.0)
 295  299                                  getime = (double)interval;
 296  300                          if (getime == 0.0 || do_interval)
 297  301                                  getime = 1.0;
 298  302                  }
 299  303  
 300  304                  if (formatter_list) {
 301  305                          format_t *tmp;
 302  306                          tmp = formatter_list;
 303  307  
 304  308                          if (timestamp_fmt != NODATE)
 305  309                                  print_timestamp(timestamp_fmt);
 306  310  
 307  311                          while (tmp) {
 308  312                                  (tmp->nfunc)();
 309  313                                  tmp = tmp->next;
 310  314                          }
 311  315                          (void) fflush(stdout);
 312  316                  }
 313  317  
 314  318                  /* only remaining/doing a single iteration, we are done */
 315  319                  if (iter == 1)
 316  320                          continue;
 317  321  
 318  322                  if (interval > 0)
 319  323                          /* Have a kip */
 320  324                          sleep_until(&start_n, period_n, forever, &caught_cont);
 321  325  
 322  326                  free_snapshot(oldss);
 323  327                  oldss = newss;
 324  328                  newss = acquire_snapshot(kc, types, &df);
 325  329                  iodevs_nl = (newss->s_iodevs_is_name_maxlen > iodevs_nl) ?
 326  330                      newss->s_iodevs_is_name_maxlen : iodevs_nl;
 327  331                  iodevs_nl = (iodevs_nl < IODEVS_NL_MIN) ?
 328  332                      IODEVS_NL_MIN : iodevs_nl;
 329  333                  iodevs_nl = (iodevs_nl > IODEVS_NL_MAX) ?
 330  334                      IODEVS_NL_MAX : iodevs_nl;
 331  335  
 332  336                  if (!suppress_state)
 333  337                          snapshot_report_changes(oldss, newss);
 334  338  
 335  339                  /* if config changed, show stats from boot */
 336  340                  if (snapshot_has_changed(oldss, newss)) {
 337  341                          free_snapshot(oldss);
 338  342                          oldss = NULL;
 339  343                  }
 340  344  
 341  345          } while (--iter);
 342  346  
 343  347          free_snapshot(oldss);
 344  348          free_snapshot(newss);
 345  349          (void) kstat_close(kc);
 346  350          free(df.if_names);
 347  351          return (0);
 348  352  }
 349  353  
 350  354  /*
 351  355   * Some magic numbers used in header formatting.
 352  356   *
 353  357   * DISK_LEN = length of either "kps tps serv" or "wps rps util"
 354  358   *            using 0 as the first position
 355  359   *
 356  360   * DISK_ERROR_LEN = length of "s/w h/w trn tot" with one space on
 357  361   *              either side. Does not use zero as first pos.
 358  362   *
 359  363   * DEVICE_LEN = length of "device" + 1 character.
 360  364   */
 361  365  
 362  366  #define DISK_LEN        11
 363  367  #define DISK_ERROR_LEN  16
 364  368  #define DEVICE_LEN      7
 365  369  
 366  370  /*ARGSUSED*/
 367  371  static void
 368  372  show_disk_name(void *v1, void *v2, void *data)
 369  373  {
 370  374          struct iodev_snapshot *dev = (struct iodev_snapshot *)v2;
 371  375          size_t slen;
 372  376          char *name;
 373  377          char fbuf[SMALL_SCRATCH_BUFLEN];
 374  378  
 375  379          if (dev == NULL)
 376  380                  return;
 377  381  
 378  382          name = do_conversions ? dev->is_pretty : dev->is_name;
 379  383          name = name ? name : dev->is_name;
 380  384  
 381  385          if (!do_raw) {
 382  386                  uint_t width;
 383  387  
 384  388                  slen = strlen(name);
 385  389                  /*
 386  390                   * The length is less
 387  391                   * than the section
 388  392                   * which will be displayed
 389  393                   * on the next line.
 390  394                   * Center the entry.
 391  395                   */
 392  396  
 393  397                  width = (DISK_LEN + 1)/2 + (slen / 2);
 394  398                  (void) snprintf(fbuf, sizeof (fbuf),
 395  399                      "%*s", width, name);
 396  400                  name = fbuf;
 397  401                  push_out("%-13.13s ", name);
 398  402          } else {
 399  403                  push_out(name);
 400  404          }
 401  405  }
 402  406  
 403  407  /*ARGSUSED*/
 404  408  static void
 405  409  show_disk_header(void *v1, void *v2, void *data)
 406  410  {
 407  411          push_out(disk_header);
 408  412  }
 409  413  
 410  414  /*
 411  415   * Write out a two line header. What is written out depends on the flags
 412  416   * selected but in the worst case consists of a tty header, a disk header
 413  417   * providing information for 4 disks and a cpu header.
 414  418   *
 415  419   * The tty header consists of the word "tty" on the first line above the
 416  420   * words "tin tout" on the next line. If present the tty portion consumes
 417  421   * the first 10 characters of each line since "tin tout" is surrounded
 418  422   * by single spaces.
 419  423   *
 420  424   * Each of the disk sections is a 14 character "block" in which the name of
 421  425   * the disk is centered in the first 12 characters of the first line.
 422  426   *
 423  427   * The cpu section is an 11 character block with "cpu" centered over the
 424  428   * section.
 425  429   *
 426  430   * The worst case should look as follows:
 427  431   *
 428  432   * 0---------1--------2---------3---------4---------5---------6---------7-------
 429  433   *    tty        sd0           sd1           sd2           sd3           cpu
 430  434   *  tin tout kps tps serv  kps tps serv  kps tps serv  kps tps serv  us sy dt id
 431  435   *  NNN NNNN NNN NNN NNNN  NNN NNN NNNN  NNN NNN NNNN  NNN NNN NNNN  NN NN NN NN
 432  436   *
 433  437   * When -D is specified, the disk header looks as follows (worst case):
 434  438   *
 435  439   * 0---------1--------2---------3---------4---------5---------6---------7-------
 436  440   *     tty        sd0           sd1             sd2          sd3          cpu
 437  441   *   tin tout rps wps util  rps wps util  rps wps util  rps wps util us sy dt id
 438  442   *   NNN NNNN NNN NNN NNNN  NNN NNN NNNN  NNN NNN NNNN  NNN NNN NNNN NN NN NN NN
 439  443   */
 440  444  static void
 441  445  printhdr(int sig)
 442  446  {
 443  447          /*
 444  448           * If we're here because a signal fired, reenable the
 445  449           * signal.
 446  450           */
 447  451          if (sig)
 448  452                  (void) signal(SIGCONT, printhdr);
 449  453          if (sig == SIGCONT)
 450  454                  caught_cont = 1;
 451  455          /*
 452  456           * Horizontal mode headers
 453  457           *
 454  458           * First line
 455  459           */
 456  460          if (do_tty)
 457  461                  print_tty_hdr1();
 458  462  
 459  463          if (do_disk & DISK_NORMAL) {
 460  464                  (void) snapshot_walk(SNAP_IODEVS, NULL, newss,
 461  465                      show_disk_name, NULL);
 462  466          }
 463  467  
 464  468          if (do_cpu)
 465  469                  print_cpu_hdr1();
 466  470          do_newline();
 467  471  
 468  472          /*
 469  473           * Second line
 470  474           */
 471  475          if (do_tty)
 472  476                  print_tty_hdr2();
 473  477  
 474  478          if (do_disk & DISK_NORMAL) {
 475  479                  (void) snapshot_walk(SNAP_IODEVS, NULL, newss,
 476  480                      show_disk_header, NULL);
 477  481          }
 478  482  
 479  483          if (do_cpu)
 480  484                  print_cpu_hdr2();
 481  485          do_newline();
 482  486  
 483  487          tohdr = REPRINT;
 484  488  }
 485  489  
 486  490  /*
 487  491   * Write out the extended header centered over the core information.
 488  492   */
 489  493  static void
 490  494  write_core_header(void)
 491  495  {
 492  496          char *edev = "extended device statistics";
 493  497          uint_t lead_space_ct;
 494  498          uint_t follow_space_ct;
 495  499          size_t edevlen;
 496  500  
 497  501          if (do_raw == 0) {
 498  502                  /*
 499  503                   * The things we do to look nice...
 500  504                   *
 501  505                   * Center the core output header. Make sure we have the
 502  506                   * right number of trailing spaces for follow-on headers
 503  507                   * (i.e., cpu and/or tty and/or errors).
 504  508                   */
 505  509                  edevlen = strlen(edev);
 506  510                  lead_space_ct = dh_len - edevlen;
 507  511                  lead_space_ct /= 2;
 508  512                  if (lead_space_ct > 0) {
 509  513                          follow_space_ct = dh_len - (lead_space_ct + edevlen);
 510  514                          if (do_disk & DISK_ERRORS)
 511  515                                  follow_space_ct -= DISK_ERROR_LEN;
 512  516                          if ((do_disk & DISK_EXTENDED) && do_conversions)
 513  517                                  follow_space_ct -= DEVICE_LEN;
 514  518  
 515  519                          push_out("%1$*2$.*2$s%3$s%4$*5$.*5$s", one_blank,
 516  520                              lead_space_ct, edev, one_blank, follow_space_ct);
 517  521                  } else
 518  522                          push_out("%56s", edev);
 519  523          } else
 520  524                  push_out(edev);
 521  525  }
 522  526  
 523  527  /*
 524  528   * In extended mode headers, we don't want to reprint the header on
 525  529   * signals as they are printed every time anyways.
 526  530   */
 527  531  static void
 528  532  printxhdr(void)
 529  533  {
 530  534  
 531  535          /*
 532  536           * Vertical mode headers
 533  537           */
 534  538          if (do_disk & DISK_EXTENDED)
 535  539                  setup(write_core_header);
 536  540          if (do_disk & DISK_ERRORS)
 537  541                  setup(print_err_hdr);
 538  542  
 539  543          if (do_conversions) {
 540  544                  setup(do_newline);
 541  545                  if (do_disk & (DISK_EXTENDED | DISK_ERRORS))
 542  546                          setup(print_disk_header);
 543  547                  setup(do_newline);
 544  548          } else {
 545  549                  if (do_tty)
 546  550                          setup(print_tty_hdr1);
 547  551                  if (do_cpu)
 548  552                          setup(print_cpu_hdr1);
 549  553                  setup(do_newline);
 550  554  
 551  555                  if (do_disk & (DISK_EXTENDED | DISK_ERRORS))
 552  556                          setup(print_disk_header);
 553  557                  if (do_tty)
 554  558                          setup(print_tty_hdr2);
 555  559                  if (do_cpu)
 556  560                          setup(print_cpu_hdr2);
 557  561                  setup(do_newline);
 558  562          }
 559  563  }
 560  564  
 561  565  /*
 562  566   * Write out a line for this disk - note that show_disk writes out
 563  567   * full lines or blocks for each selected disk.
 564  568   */
 565  569  static void
 566  570  show_disk(void *v1, void *v2, void *data)
 567  571  {
 568  572          uint32_t err_counters[NUMBER_OF_ERR_COUNTERS];
 569  573          boolean_t display_err_counters = do_disk & DISK_ERRORS;
 570  574          struct iodev_snapshot *old = (struct iodev_snapshot *)v1;
 571  575          struct iodev_snapshot *new = (struct iodev_snapshot *)v2;
 572  576          int *count = (int *)data;
 573  577          double rps, wps, tps, mtps, krps, kwps, kps, avw, avr, w_pct, r_pct;
 574  578          double wserv, rserv, serv;
 575  579          double iosize;  /* kb/sec or MB/sec */
 576  580          double etime, hr_etime;
 577  581          char *disk_name;
 578  582          u_longlong_t ldeltas;
 579  583          uint_t udeltas;
 580  584          uint64_t t_delta;
 581  585          uint64_t w_delta;
 582  586          uint64_t r_delta;
 583  587          int doit = 1;
 584  588          uint_t toterrs;
 585  589          char *fstr;
 586  590  
 587  591          if (new == NULL)
 588  592                  return;
 589  593  
 590  594          switch (show_disk_mode) {
 591  595          case SHOW_FIRST_ONLY:
 592  596                  if (count != NULL && *count)
 593  597                          return;
 594  598                  break;
 595  599  
 596  600          case SHOW_SECOND_ONWARDS:
 597  601                  if (count != NULL && !*count) {
 598  602                          (*count)++;
 599  603                          return;
 600  604                  }
 601  605                  break;
 602  606  
 603  607          default:
 604  608                  break;
 605  609          }
 606  610  
 607  611          disk_name = do_conversions ? new->is_pretty : new->is_name;
 608  612          disk_name = disk_name ? disk_name : new->is_name;
 609  613  
 610  614          /*
 611  615           * Only do if we want IO stats - Avoids errors traveling this
 612  616           * section if that's all we want to see.
 613  617           */
 614  618          if (do_disk & DISK_IO_MASK) {
 615  619                  if (old) {
 616  620                          t_delta = hrtime_delta(old->is_snaptime,
 617  621                              new->is_snaptime);
 618  622                  } else {
 619  623                          t_delta = hrtime_delta(new->is_crtime,
 620  624                              new->is_snaptime);
 621  625                  }
 622  626  
 623  627                  if (new->is_nr_children) {
 624  628                          if (new->is_type == IODEV_CONTROLLER) {
 625  629                                  t_delta /= new->is_nr_children;
 626  630                          } else if ((new->is_type == IODEV_IOPATH_LT) ||
 627  631                              (new->is_type == IODEV_IOPATH_LI)) {
 628  632                                  /* synthetic path */
 629  633                                  if (!old) {
 630  634                                          t_delta = new->is_crtime;
 631  635                                  }
 632  636                                  t_delta /= new->is_nr_children;
 633  637                          }
 634  638                  }
 635  639  
 636  640                  hr_etime = (double)t_delta;
 637  641                  if (hr_etime == 0.0)
 638  642                          hr_etime = (double)NANOSEC;
 639  643                  etime = hr_etime / (double)NANOSEC;
 640  644  
 641  645                  /* reads per second */
 642  646                  udeltas = u32_delta(old ? old->is_stats.reads : 0,
 643  647                      new->is_stats.reads);
 644  648                  rps = (double)udeltas;
 645  649                  rps /= etime;
 646  650  
 647  651                  /* writes per second */
 648  652                  udeltas = u32_delta(old ? old->is_stats.writes : 0,
 649  653                      new->is_stats.writes);
 650  654                  wps = (double)udeltas;
 651  655                  wps /= etime;
 652  656  
 653  657                  tps = rps + wps;
 654  658                          /* transactions per second */
 655  659  
 656  660                  /*
 657  661                   * report throughput as either kb/sec or MB/sec
 658  662                   */
 659  663  
 660  664                  if (!do_megabytes)
 661  665                          iosize = 1024.0;
 662  666                  else
 663  667                          iosize = 1048576.0;
 664  668  
 665  669                  ldeltas = ull_delta(old ? old->is_stats.nread : 0,
 666  670                      new->is_stats.nread);
 667  671                  if (ldeltas) {
 668  672                          krps = (double)ldeltas;
 669  673                          krps /= etime;
 670  674                          krps /= iosize;
 671  675                  } else
 672  676                          krps = 0.0;
 673  677  
 674  678                  ldeltas = ull_delta(old ? old->is_stats.nwritten : 0,
 675  679                      new->is_stats.nwritten);
 676  680                  if (ldeltas) {
 677  681                          kwps = (double)ldeltas;
 678  682                          kwps /= etime;
 679  683                          kwps /= iosize;
 680  684                  } else
 681  685                          kwps = 0.0;
 682  686  
 683  687                  /*
 684  688                   * Blocks transferred per second
 685  689                   */
 686  690                  kps = krps + kwps;
 687  691  
 688  692                  /*
 689  693                   * Average number of wait transactions waiting
 690  694                   */
 691  695                  w_delta = hrtime_delta((u_longlong_t)
 692  696                      (old ? old->is_stats.wlentime : 0),
 693  697                      new->is_stats.wlentime);
 694  698                  if (w_delta) {
 695  699                          avw = (double)w_delta;
 696  700                          avw /= hr_etime;
 697  701                  } else
 698  702                          avw = 0.0;
 699  703  
 700  704                  /*
 701  705                   * Average number of run transactions waiting
 702  706                   */
 703  707                  r_delta = hrtime_delta(old ? old->is_stats.rlentime : 0,
 704  708                      new->is_stats.rlentime);
 705  709                  if (r_delta) {
 706  710                          avr = (double)r_delta;
 707  711                          avr /= hr_etime;
 708  712                  } else
 709  713                          avr = 0.0;
 710  714  
 711  715                  /*
 712  716                   * Average wait service time in milliseconds
 713  717                   */
 714  718                  if (tps > 0.0 && (avw != 0.0 || avr != 0.0)) {
 715  719                          mtps = 1000.0 / tps;
 716  720                          if (avw != 0.0)
 717  721                                  wserv = avw * mtps;
 718  722                          else
 719  723                                  wserv = 0.0;
 720  724  
 721  725                          if (avr != 0.0)
 722  726                                  rserv = avr * mtps;
 723  727                          else
 724  728                                  rserv = 0.0;
 725  729                          serv = rserv + wserv;
 726  730                  } else {
 727  731                          rserv = 0.0;
 728  732                          wserv = 0.0;
 729  733                          serv = 0.0;
 730  734                  }
 731  735  
 732  736                  /* % of time there is a transaction waiting for service */
 733  737                  t_delta = hrtime_delta(old ? old->is_stats.wtime : 0,
 734  738                      new->is_stats.wtime);
 735  739                  if (t_delta) {
 736  740                          w_pct = (double)t_delta;
 737  741                          w_pct /= hr_etime;
 738  742                          w_pct *= 100.0;
 739  743  
 740  744                          /*
 741  745                           * Average the wait queue utilization over the
 742  746                           * the controller's devices, if this is a controller.
 743  747                           */
 744  748                          if (new->is_type == IODEV_CONTROLLER)
 745  749                                  w_pct /= new->is_nr_children;
 746  750                  } else
 747  751                          w_pct = 0.0;
 748  752  
 749  753                  /* % of time there is a transaction running */
 750  754                  t_delta = hrtime_delta(old ? old->is_stats.rtime : 0,
 751  755                      new->is_stats.rtime);
 752  756                  if (t_delta) {
 753  757                          r_pct = (double)t_delta;
 754  758                          r_pct /= hr_etime;
 755  759                          r_pct *= 100.0;
 756  760  
 757  761                          /*
 758  762                           * Average the percent busy over the controller's
 759  763                           * devices, if this is a controller.
 760  764                           */
 761  765                          if (new->is_type == IODEV_CONTROLLER)
 762  766                                  w_pct /= new->is_nr_children;
 763  767                  } else {
 764  768                          r_pct = 0.0;
 765  769                  }
 766  770  
 767  771                  /* % of time there is a transaction running */
 768  772                  if (do_interval) {
 769  773                          rps     *= etime;
 770  774                          wps     *= etime;
 771  775                          tps     *= etime;
 772  776                          krps    *= etime;
 773  777                          kwps    *= etime;
 774  778                          kps     *= etime;
 775  779                  }
 776  780          }
 777  781  
 778  782          if (do_disk & (DISK_EXTENDED | DISK_ERRORS)) {
 779  783                  if ((!do_conversions) && ((suppress_zero == 0) ||
 780  784                      ((do_disk & DISK_EXTENDED) == 0))) {
 781  785                          if (do_raw == 0) {
 782  786                                  push_out("%-*.*s",
 783  787                                      iodevs_nl, iodevs_nl, disk_name);
 784  788                          } else {
 785  789                                  push_out(disk_name);
 786  790                          }
 787  791                  }
 788  792          }
 789  793  
 790  794          /*
 791  795           * The error counters are read first (if asked for and if they are
 792  796           * available).
 793  797           */
 794  798          bzero(err_counters, sizeof (err_counters));
 795  799          toterrs = 0;
 796  800          if (display_err_counters && (new->is_errors.ks_data != NULL)) {
 797  801                  kstat_named_t   *knp;
 798  802                  int             i;
 799  803  
 800  804                  knp = KSTAT_NAMED_PTR(&new->is_errors);
 801  805                  for (i = 0; i < NUMBER_OF_ERR_COUNTERS; i++) {
 802  806                          switch (knp[i].data_type) {
 803  807                                  case KSTAT_DATA_ULONG:
 804  808                                  case KSTAT_DATA_ULONGLONG:
 805  809                                          err_counters[i] = knp[i].value.ui32;
 806  810                                          toterrs += knp[i].value.ui32;
 807  811                                          break;
 808  812                                  default:
 809  813                                          break;
 810  814                          }
 811  815                  }
 812  816          }
 813  817  
 814  818          switch (do_disk & DISK_IO_MASK) {
 815  819          case DISK_OLD:
 816  820                  if (do_raw == 0)
 817  821                          fstr = "%3.0f %3.0f %4.0f  ";
 818  822                  else
 819  823                          fstr = "%.0f,%.0f,%.0f";
 820  824                  push_out(fstr, kps, tps, serv);
 821  825                  break;
 822  826          case DISK_NEW:
 823  827                  if (do_raw == 0)
 824  828                          fstr = "%3.0f %3.0f %4.1f  ";
 825  829                  else
 826  830                          fstr = "%.0f,%.0f,%.1f";
 827  831                  push_out(fstr, rps, wps, r_pct);
 828  832                  break;
 829  833          case DISK_EXTENDED:
 830  834                  if (suppress_zero) {
 831  835                          if (fzero(rps) && fzero(wps) && fzero(krps) &&
 832  836                              fzero(kwps) && fzero(avw) && fzero(avr) &&
 833  837                              fzero(serv) && fzero(w_pct) && fzero(r_pct) &&
 834  838                              (toterrs == 0)) {
 835  839                                  doit = 0;
 836  840                                  display_err_counters = B_FALSE;
 837  841                          } else if (do_conversions == 0) {
 838  842                                  if (do_raw == 0) {
 839  843                                          push_out("%-*.*s",
 840  844                                              iodevs_nl, iodevs_nl, disk_name);
 841  845                                  } else {
 842  846                                          push_out(disk_name);
 843  847                                  }
 844  848                          }
 845  849                  }
 846  850                  if (doit) {
 847  851                          if (!do_conversions) {
 848  852                                  if (do_raw == 0) {
 849  853                                          fstr = " %6.1f %6.1f %6.1f %6.1f "
 850  854                                              "%4.1f %4.1f %6.1f %3.0f "
 851  855                                              "%3.0f ";
 852  856                                  } else {
 853  857                                          fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
 854  858                                              "%.1f,%.0f,%.0f";
 855  859                                  }
 856  860                                  push_out(fstr, rps, wps, krps, kwps, avw, avr,
 857  861                                      serv, w_pct, r_pct);
 858  862                          } else {
 859  863                                  if (do_raw == 0) {
 860  864                                          fstr = " %6.1f %6.1f %6.1f %6.1f "
 861  865                                              "%4.1f %4.1f %6.1f %6.1f "
 862  866                                              "%3.0f %3.0f ";
 863  867                                  } else {
 864  868                                          fstr = "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,"
 865  869                                              "%.1f,%.1f,%.0f,%.0f";
 866  870                                  }
 867  871                                  push_out(fstr, rps, wps, krps, kwps, avw, avr,
 868  872                                      wserv, rserv, w_pct, r_pct);
 869  873                          }
 870  874                  }
 871  875                  break;
 872  876          }
 873  877  
 874  878          if (display_err_counters) {
 875  879                  char    *efstr;
 876  880                  int     i;
 877  881  
 878  882                  if (do_raw == 0) {
 879  883                          if (do_disk == DISK_ERRORS)
 880  884                                  push_out(two_blanks);
 881  885                          efstr = "%3u ";
 882  886                  } else {
 883  887                          efstr = "%u";
 884  888                  }
 885  889  
 886  890                  for (i = 0; i < NUMBER_OF_ERR_COUNTERS; i++)
 887  891                          push_out(efstr, err_counters[i]);
 888  892  
 889  893                  push_out(efstr, toterrs);
 890  894          }
 891  895  
 892  896          if (suppress_zero == 0 || doit == 1) {
 893  897                  if ((do_disk & (DISK_EXTENDED | DISK_ERRORS)) &&
 894  898                      do_conversions) {
 895  899                          push_out("%s", disk_name);
 896  900                          if (show_mountpts && new->is_dname) {
 897  901                                  mnt_t *mount_pt;
 898  902                                  char *lu;
 899  903                                  char *dnlu;
 900  904                                  char lub[SMALL_SCRATCH_BUFLEN];
 901  905  
 902  906                                  lu = strrchr(new->is_dname, '/');
 903  907                                  if (lu) {
 904  908                                          /* only the part after a possible '/' */
 905  909                                          dnlu = strrchr(disk_name, '/');
 906  910                                          if (dnlu != NULL &&
 907  911                                              strcmp(dnlu, lu) == 0)
 908  912                                                  lu = new->is_dname;
 909  913                                          else {
 910  914                                                  *lu = 0;
 911  915                                                  (void) strcpy(lub,
 912  916                                                      new->is_dname);
 913  917                                                  *lu = '/';
 914  918                                                  (void) strcat(lub, "/");
 915  919                                                  (void) strcat(lub,
 916  920                                                      disk_name);
 917  921                                                  lu = lub;
 918  922                                          }
 919  923                                  } else
 920  924                                          lu = disk_name;
 921  925                                  mount_pt = lookup_mntent_byname(lu);
 922  926                                  if (mount_pt) {
 923  927                                          if (do_raw == 0)
 924  928                                                  push_out(" (%s)",
 925  929                                                      mount_pt->mount_point);
 926  930                                          else
 927  931                                                  push_out("(%s)",
 928  932                                                      mount_pt->mount_point);
 929  933                                  }
 930  934                          }
 931  935                  }
 932  936          }
 933  937  
 934  938          if ((do_disk & PRINT_VERTICAL) && show_disk_mode != SHOW_FIRST_ONLY)
 935  939                  do_newline();
 936  940  
 937  941          if (count != NULL)
 938  942                  (*count)++;
 939  943  }
 940  944  
 941  945  static void
 942  946  usage(void)
 943  947  {
 944  948          (void) fprintf(stderr,
 945  949              "Usage: iostat [-cCdDeEiImMnpPrstxXYz] "
 946  950              " [-l n] [-T d|u] [disk ...] [interval [count]]\n"
 947  951              "\t\t-c:    report percentage of time system has spent\n"
 948  952              "\t\t\tin user/system/dtrace/idle mode\n"
 949  953              "\t\t-C:    report disk statistics by controller\n"
 950  954              "\t\t-d:    display disk Kb/sec, transfers/sec, avg. \n"
 951  955              "\t\t\tservice time in milliseconds  \n"
 952  956              "\t\t-D:    display disk reads/sec, writes/sec, \n"
 953  957              "\t\t\tpercentage disk utilization \n"
 954  958              "\t\t-e:    report device error summary statistics\n"
 955  959              "\t\t-E:    report extended device error statistics\n"
 956  960              "\t\t-i:    show device IDs for -E output\n"
  
    | 
      ↓ open down ↓ | 
    705 lines elided | 
    
      ↑ open up ↑ | 
  
 957  961              "\t\t-I:    report the counts in each interval,\n"
 958  962              "\t\t\tinstead of rates, where applicable\n"
 959  963              "\t\t-l n:  Limit the number of disks to n\n"
 960  964              "\t\t-m:    Display mount points (most useful with -p)\n"
 961  965              "\t\t-M:    Display data throughput in MB/sec "
 962  966              "instead of Kb/sec\n"
 963  967              "\t\t-n:    convert device names to cXdYtZ format\n"
 964  968              "\t\t-p:    report per-partition disk statistics\n"
 965  969              "\t\t-P:    report per-partition disk statistics only,\n"
 966  970              "\t\t\tno per-device disk statistics\n"
      971 +            "\t\t-f:    report ZFS-level statistics for ZFS pool and\n"
      972 +            "\t\t\tindividual vdevs\n"
      973 +            "\t\t-F:    report ZFS pool and individual physical vdevs\n"
      974 +            "\t\t\tstatistics only, no per-device statistics\n"
 967  975              "\t\t-r:    Display data in comma separated format\n"
 968  976              "\t\t-s:    Suppress state change messages\n"
 969  977              "\t\t-T d|u Display a timestamp in date (d) or unix "
 970  978              "time_t (u)\n"
 971  979              "\t\t-t:    display chars read/written to terminals\n"
 972  980              "\t\t-x:    display extended disk statistics\n"
 973  981              "\t\t-X:    display I/O path statistics\n"
 974  982              "\t\t-Y:    display I/O path (I/T/L) statistics\n"
 975  983              "\t\t-z:    Suppress entries with all zero values\n");
 976  984          exit(1);
 977  985  }
 978  986  
 979  987  /*ARGSUSED*/
 980  988  static void
 981  989  show_disk_errors(void *v1, void *v2, void *d)
 982  990  {
 983  991          struct iodev_snapshot *disk = (struct iodev_snapshot *)v2;
 984  992          kstat_named_t *knp;
 985  993          size_t  col;
 986  994          int     i, len;
 987  995          char    *dev_name;
 988  996  
 989  997          if (disk->is_errors.ks_ndata == 0)
 990  998                  return;
 991  999          if (disk->is_type == IODEV_CONTROLLER)
 992 1000                  return;
 993 1001  
 994 1002          dev_name = do_conversions ? disk->is_pretty : disk->is_name;
 995 1003          dev_name = dev_name ? dev_name : disk->is_name;
 996 1004  
 997 1005          len = strlen(dev_name);
 998 1006          if (len > 20)
 999 1007                  push_out("%s ", dev_name);
1000 1008          else if (len > 16)
1001 1009                  push_out("%-20.20s ", dev_name);
1002 1010          else {
1003 1011                  if (do_conversions)
1004 1012                          push_out("%-16.16s ", dev_name);
1005 1013                  else
1006 1014                          push_out("%-9.9s ", dev_name);
1007 1015          }
1008 1016          col = 0;
1009 1017  
1010 1018          knp = KSTAT_NAMED_PTR(&disk->is_errors);
1011 1019          for (i = 0; i < disk->is_errors.ks_ndata; i++) {
1012 1020                  /* skip kstats that the driver did not kstat_named_init */
1013 1021                  if (knp[i].name[0] == 0)
1014 1022                          continue;
1015 1023  
1016 1024                  col += strlen(knp[i].name);
1017 1025  
1018 1026                  switch (knp[i].data_type) {
1019 1027                          case KSTAT_DATA_CHAR:
1020 1028                          case KSTAT_DATA_STRING:
1021 1029                                  if ((strcmp(knp[i].name, "Serial No") == 0) &&
1022 1030                                      do_devid) {
1023 1031                                          if (disk->is_devid) {
1024 1032                                                  push_out("Device Id: %s ",
1025 1033                                                      disk->is_devid);
1026 1034                                                  col += strlen(disk->is_devid);
1027 1035                                          } else {
1028 1036                                                  push_out("Device Id: ");
1029 1037                                          }
1030 1038  
1031 1039                                          break;
1032 1040                                  }
1033 1041                                  if (knp[i].data_type == KSTAT_DATA_CHAR) {
1034 1042                                          push_out("%s: %-.16s ", knp[i].name,
1035 1043                                              &knp[i].value.c[0]);
1036 1044                                          col += strnlen(&knp[i].value.c[0], 16);
1037 1045                                  } else {
1038 1046                                          push_out("%s: %s ", knp[i].name,
1039 1047                                              KSTAT_NAMED_STR_PTR(&knp[i]));
1040 1048                                          col +=
1041 1049                                              KSTAT_NAMED_STR_BUFLEN(&knp[i]) - 1;
1042 1050                                  }
1043 1051                                  break;
1044 1052                          case KSTAT_DATA_ULONG:
1045 1053                                  push_out("%s: %u ", knp[i].name,
1046 1054                                      knp[i].value.ui32);
1047 1055                                  col += 4;
1048 1056                                  break;
1049 1057                          case KSTAT_DATA_ULONGLONG:
1050 1058                                  if (strcmp(knp[i].name, "Size") == 0) {
1051 1059                                          do_newline();
1052 1060                                          push_out("%s: %2.2fGB <%llu bytes>",
1053 1061                                              knp[i].name,
1054 1062                                              (float)knp[i].value.ui64 /
1055 1063                                              DISK_GIGABYTE,
1056 1064                                              knp[i].value.ui64);
1057 1065                                          do_newline();
1058 1066                                          col = 0;
1059 1067                                          break;
1060 1068                                  }
1061 1069                                  push_out("%s: %u ", knp[i].name,
1062 1070                                      knp[i].value.ui32);
1063 1071                                  col += 4;
1064 1072                                  break;
1065 1073                          }
1066 1074                  if ((col >= 62) || (i == 2)) {
1067 1075                          do_newline();
1068 1076                          col = 0;
1069 1077                  }
1070 1078          }
1071 1079          if (col > 0) {
1072 1080                  do_newline();
1073 1081          }
1074 1082          do_newline();
  
    | 
      ↓ open down ↓ | 
    98 lines elided | 
    
      ↑ open up ↑ | 
  
1075 1083  }
1076 1084  
1077 1085  void
1078 1086  do_args(int argc, char **argv)
1079 1087  {
1080 1088          int             c;
1081 1089          int             errflg = 0;
1082 1090          extern char     *optarg;
1083 1091          extern int      optind;
1084 1092  
1085      -        while ((c = getopt(argc, argv, "tdDxXYCciIpPnmMeEszrT:l:")) != EOF)
     1093 +        while ((c = getopt(argc, argv, "tdDxXYCciIpPfFnmMeEszrT:l:")) != EOF)
1086 1094                  switch (c) {
1087 1095                  case 't':
1088 1096                          do_tty++;
1089 1097                          break;
1090 1098                  case 'd':
1091 1099                          do_disk |= DISK_OLD;
1092 1100                          break;
1093 1101                  case 'D':
1094 1102                          do_disk |= DISK_NEW;
1095 1103                          break;
1096 1104                  case 'x':
1097 1105                          do_disk |= DISK_EXTENDED;
1098 1106                          break;
1099 1107                  case 'X':
1100 1108                          if (do_disk & DISK_IOPATH_LTI)
1101 1109                                  errflg++;       /* -Y already used */
1102 1110                          else
1103 1111                                  do_disk |= DISK_IOPATH_LI;
1104 1112                          break;
1105 1113                  case 'Y':
1106 1114                          if (do_disk & DISK_IOPATH_LI)
1107 1115                                  errflg++;       /* -X already used */
1108 1116                          else
1109 1117                                  do_disk |= DISK_IOPATH_LTI;
1110 1118                          break;
1111 1119                  case 'C':
1112 1120                          do_controller++;
1113 1121                          break;
1114 1122                  case 'c':
1115 1123                          do_cpu++;
  
    | 
      ↓ open down ↓ | 
    20 lines elided | 
    
      ↑ open up ↑ | 
  
1116 1124                          break;
1117 1125                  case 'I':
1118 1126                          do_interval++;
1119 1127                          break;
1120 1128                  case 'p':
1121 1129                          do_partitions++;
1122 1130                          break;
1123 1131                  case 'P':
1124 1132                          do_partitions_only++;
1125 1133                          break;
     1134 +                case 'f':
     1135 +                        do_zfs++;
     1136 +                        break;
     1137 +                case 'F':
     1138 +                        do_zfs_only++;
     1139 +                        break;
1126 1140                  case 'n':
1127 1141                          do_conversions++;
1128 1142                          break;
1129 1143                  case 'M':
1130 1144                          do_megabytes++;
1131 1145                          break;
1132 1146                  case 'e':
1133 1147                          do_disk |= DISK_ERRORS;
1134 1148                          break;
1135 1149                  case 'E':
1136 1150                          do_disk |= DISK_EXTENDED_ERRORS;
1137 1151                          break;
1138 1152                  case 'i':
1139 1153                          do_devid = 1;
1140 1154                          break;
1141 1155                  case 's':
1142 1156                          suppress_state = 1;
1143 1157                          break;
1144 1158                  case 'z':
1145 1159                          suppress_zero = 1;
1146 1160                          break;
1147 1161                  case 'm':
1148 1162                          show_mountpts = 1;
1149 1163                          break;
1150 1164                  case 'T':
1151 1165                          if (optarg) {
1152 1166                                  if (*optarg == 'u')
1153 1167                                          timestamp_fmt = UDATE;
1154 1168                                  else if (*optarg == 'd')
1155 1169                                          timestamp_fmt = DDATE;
1156 1170                                  else
1157 1171                                          errflg++;
1158 1172                          } else {
1159 1173                                  errflg++;
1160 1174                          }
1161 1175                          break;
1162 1176                  case 'r':
1163 1177                          do_raw = 1;
1164 1178                          break;
1165 1179                  case 'l':
1166 1180                          df.if_max_iodevs = safe_strtoi(optarg, "invalid limit");
1167 1181                          if (df.if_max_iodevs < 1)
1168 1182                                  usage();
1169 1183                          break;
1170 1184                  case '?':
1171 1185                          errflg++;
1172 1186          }
1173 1187  
1174 1188          if ((do_disk & DISK_OLD) && (do_disk & DISK_NEW)) {
1175 1189                  (void) fprintf(stderr, "-d and -D are incompatible.\n");
1176 1190                  usage();
1177 1191          }
1178 1192  
1179 1193          if (errflg) {
1180 1194                  usage();
1181 1195          }
1182 1196  
1183 1197          /* if no output classes explicity specified, use defaults */
1184 1198          if (do_tty == 0 && do_disk == 0 && do_cpu == 0)
1185 1199                  do_tty = do_cpu = 1, do_disk = DISK_OLD;
1186 1200  
1187 1201          /*
1188 1202           * multi-path options (-X, -Y) without a specific vertical
1189 1203           * output format (-x, -e, -E) imply extended -x format
1190 1204           */
1191 1205          if ((do_disk & (DISK_IOPATH_LI | DISK_IOPATH_LTI)) &&
1192 1206              !(do_disk & PRINT_VERTICAL))
1193 1207                  do_disk |= DISK_EXTENDED;
1194 1208  
1195 1209          /*
1196 1210           * If conflicting options take the preferred
1197 1211           * -D and -x result in -x
1198 1212           * -d or -D and -e or -E gives only whatever -d or -D was specified
1199 1213           */
1200 1214          if ((do_disk & DISK_EXTENDED) && (do_disk & DISK_NORMAL))
1201 1215                  do_disk &= ~DISK_NORMAL;
1202 1216          if ((do_disk & DISK_NORMAL) && (do_disk & DISK_ERROR_MASK))
1203 1217                  do_disk &= ~DISK_ERROR_MASK;
1204 1218  
1205 1219          /* nfs, tape, always shown */
1206 1220          df.if_allowed_types = IODEV_NFS | IODEV_TAPE;
1207 1221  
1208 1222          /*
1209 1223           * If limit == 0 then no command line limit was set, else if any of
1210 1224           * the flags that cause unlimited disks were not set,
1211 1225           * use the default of 4
1212 1226           */
1213 1227          if (df.if_max_iodevs == 0) {
1214 1228                  df.if_max_iodevs = DEFAULT_LIMIT;
1215 1229                  df.if_skip_floppy = 1;
1216 1230                  if (do_disk & (DISK_EXTENDED | DISK_ERRORS |
1217 1231                      DISK_EXTENDED_ERRORS)) {
1218 1232                          df.if_max_iodevs = UNLIMITED_IODEVS;
1219 1233                          df.if_skip_floppy = 0;
1220 1234                  }
1221 1235          }
1222 1236          if (do_disk) {
1223 1237                  size_t count = 0;
1224 1238                  size_t i = optind;
1225 1239  
1226 1240                  while (i < argc && !isdigit(argv[i][0])) {
1227 1241                          count++;
1228 1242                          i++;
1229 1243                  }
1230 1244  
1231 1245                  /*
1232 1246                   * "Note:  disks  explicitly  requested
1233 1247                   * are not subject to this disk limit"
1234 1248                   */
1235 1249                  if ((count > df.if_max_iodevs) ||
1236 1250                      (count && (df.if_max_iodevs == UNLIMITED_IODEVS)))
1237 1251                          df.if_max_iodevs = count;
1238 1252  
1239 1253                  df.if_names = safe_alloc(count * sizeof (char *));
1240 1254                  (void) memset(df.if_names, 0, count * sizeof (char *));
1241 1255  
1242 1256                  df.if_nr_names = 0;
1243 1257                  while (optind < argc && !isdigit(argv[optind][0]))
1244 1258                          df.if_names[df.if_nr_names++] = argv[optind++];
1245 1259          }
1246 1260          if (optind < argc) {
1247 1261                  interval = safe_strtoi(argv[optind], "invalid interval");
1248 1262                  if (interval < 1)
1249 1263                          fail(0, "invalid interval");
1250 1264                  optind++;
1251 1265  
1252 1266                  if (optind < argc) {
1253 1267                          iter = safe_strtoi(argv[optind], "invalid count");
1254 1268                          if (iter < 1)
1255 1269                                  fail(0, "invalid count");
1256 1270                          optind++;
1257 1271                  }
1258 1272          }
1259 1273          if (interval == 0)
1260 1274                  iter = 1;
1261 1275          if (optind < argc)
1262 1276                  usage();
1263 1277  }
1264 1278  
1265 1279  /*
1266 1280   * Driver for doing the extended header formatting. Will produce
1267 1281   * the function stack needed to output an extended header based
1268 1282   * on the options selected.
1269 1283   */
1270 1284  
1271 1285  void
1272 1286  do_format(void)
1273 1287  {
1274 1288          char    header[SMALL_SCRATCH_BUFLEN] = {0};
1275 1289          char    ch;
1276 1290          char    iosz;
1277 1291          const char    *fstr;
1278 1292  
1279 1293          disk_header[0] = 0;
1280 1294          ch = (do_interval ? 'i' : 's');
1281 1295          iosz = (do_megabytes ? 'M' : 'k');
1282 1296          if (do_disk & DISK_ERRORS) {
1283 1297                  if (do_raw == 0) {
1284 1298                          (void) sprintf(header, "s/w h/w trn tot ");
1285 1299                  } else
1286 1300                          (void) sprintf(header, "s/w,h/w,trn,tot");
1287 1301          }
1288 1302          switch (do_disk & DISK_IO_MASK) {
1289 1303                  case DISK_OLD:
1290 1304                          if (do_raw == 0)
1291 1305                                  fstr = "%cp%c tp%c serv  ";
1292 1306                          else
1293 1307                                  fstr = "%cp%c,tp%c,serv";
1294 1308                          (void) snprintf(disk_header, sizeof (disk_header),
1295 1309                              fstr, iosz, ch, ch);
1296 1310                          break;
1297 1311                  case DISK_NEW:
1298 1312                          if (do_raw == 0)
1299 1313                                  fstr = "rp%c wp%c util  ";
1300 1314                          else
1301 1315                                  fstr = "%rp%c,wp%c,util";
1302 1316                          (void) snprintf(disk_header, sizeof (disk_header),
1303 1317                              fstr, ch, ch);
1304 1318                          break;
1305 1319                  case DISK_EXTENDED:
1306 1320                          /* This is -x option */
1307 1321                          if (!do_conversions) {
1308 1322                                  /* without -n option */
1309 1323                                  if (do_raw == 0) {
1310 1324                                          /* without -r option */
1311 1325                                          (void) snprintf(disk_header,
1312 1326                                              sizeof (disk_header),
1313 1327                                              "%-*.*s    r/%c    w/%c   "
1314 1328                                              "%cr/%c   %cw/%c wait actv  "
1315 1329                                              "svc_t  %%%%w  %%%%b %s",
1316 1330                                              iodevs_nl, iodevs_nl, "device",
1317 1331                                              ch, ch, iosz, ch, iosz, ch, header);
1318 1332                                  } else {
1319 1333                                          /* with -r option */
1320 1334                                          (void) snprintf(disk_header,
1321 1335                                              sizeof (disk_header),
1322 1336                                              "device,r/%c,w/%c,%cr/%c,%cw/%c,"
1323 1337                                              "wait,actv,svc_t,%%%%w,"
1324 1338                                              "%%%%b%s%s",
1325 1339                                              ch, ch, iosz, ch, iosz, ch,
1326 1340                                              *header == '\0' ? "" : ",",
1327 1341                                              header);
1328 1342                                          /*
1329 1343                                           * if no -e flag, header == '\0...'
1330 1344                                           * Ternary operator above is to prevent
1331 1345                                           * trailing comma in full disk_header
1332 1346                                           */
1333 1347                                  }
1334 1348                          } else {
1335 1349                                  /* with -n option */
1336 1350                                  if (do_raw == 0) {
1337 1351                                          fstr = "    r/%c    w/%c   %cr/%c   "
1338 1352                                              "%cw/%c wait actv wsvc_t asvc_t  "
1339 1353                                              "%%%%w  %%%%b %sdevice";
1340 1354                                  } else {
1341 1355                                          fstr = "r/%c,w/%c,%cr/%c,%cw/%c,"
1342 1356                                              "wait,actv,wsvc_t,asvc_t,"
1343 1357                                              "%%%%w,%%%%b,%sdevice";
1344 1358                                          /*
1345 1359                                           * if -rnxe, "tot" (from -e) and
1346 1360                                           * "device" are run together
1347 1361                                           * due to lack of trailing comma
1348 1362                                           * in 'header'. However, adding
1349 1363                                           * trailing comma to header at
1350 1364                                           * its definition leads to prob-
1351 1365                                           * lems elsewhere so it's added
1352 1366                                           * here in this edge case -rnxe
1353 1367                                           */
1354 1368                                          if (*header != '\0')
1355 1369                                                  (void) strcat(header, ",");
1356 1370                                  }
1357 1371                                  (void) snprintf(disk_header,
1358 1372                                      sizeof (disk_header),
1359 1373                                      fstr, ch, ch, iosz, ch, iosz,
1360 1374                                      ch, header);
1361 1375                          }
1362 1376                          break;
1363 1377                  default:
1364 1378                          break;
1365 1379          }
1366 1380  
1367 1381          /* do DISK_ERRORS header (already added above for DISK_EXTENDED) */
1368 1382          if ((do_disk & DISK_ERRORS) &&
1369 1383              ((do_disk & DISK_IO_MASK) != DISK_EXTENDED)) {
1370 1384                  if (!do_conversions) {
1371 1385                          if (do_raw == 0)
1372 1386                                  (void) snprintf(disk_header,
1373 1387                                      sizeof (disk_header), "%-*.*s  %s",
1374 1388                                      iodevs_nl, iodevs_nl, "device", header);
1375 1389                          else
1376 1390                                  (void) snprintf(disk_header,
1377 1391                                      sizeof (disk_header), "device,%s", header);
1378 1392                  } else {
1379 1393                          if (do_raw == 0) {
1380 1394                                  (void) snprintf(disk_header,
1381 1395                                      sizeof (disk_header),
1382 1396                                      "  %sdevice", header);
1383 1397                          } else {
1384 1398                                  (void) snprintf(disk_header,
1385 1399                                      sizeof (disk_header),
1386 1400                                      "%s,device", header);
1387 1401                          }
1388 1402                  }
1389 1403          } else {
1390 1404                  /*
1391 1405                   * Need to subtract two characters for the % escape in
1392 1406                   * the string.
1393 1407                   */
1394 1408                  dh_len = strlen(disk_header) - 2;
1395 1409          }
1396 1410  
1397 1411          /*
1398 1412           * -n *and* (-E *or* -e *or* -x)
1399 1413           */
1400 1414          if (do_conversions && (do_disk & PRINT_VERTICAL)) {
1401 1415                  if (do_tty)
1402 1416                          setup(print_tty_hdr1);
1403 1417                  if (do_cpu)
1404 1418                          setup(print_cpu_hdr1);
1405 1419                  if (do_tty || do_cpu)
1406 1420                          setup(do_newline);
1407 1421                  if (do_tty)
1408 1422                          setup(print_tty_hdr2);
1409 1423                  if (do_cpu)
1410 1424                          setup(print_cpu_hdr2);
1411 1425                  if (do_tty || do_cpu)
1412 1426                          setup(do_newline);
1413 1427                  if (do_tty)
1414 1428                          setup(print_tty_data);
1415 1429                  if (do_cpu)
1416 1430                          setup(print_cpu_data);
1417 1431                  if (do_tty || do_cpu)
1418 1432                          setup(do_newline);
1419 1433                  printxhdr();
1420 1434  
1421 1435                  setup(show_all_disks);
1422 1436          } else {
1423 1437                  /*
1424 1438                   * These unholy gymnastics are necessary to place CPU/tty
1425 1439                   * data to the right of the disks/errors for the first
1426 1440                   * line in vertical mode.
1427 1441                   */
1428 1442                  if (do_disk & PRINT_VERTICAL) {
1429 1443                          printxhdr();
1430 1444  
1431 1445                          setup(show_first_disk);
1432 1446                          if (do_tty)
1433 1447                                  setup(print_tty_data);
1434 1448                          if (do_cpu)
1435 1449                                  setup(print_cpu_data);
1436 1450                          setup(do_newline);
1437 1451  
1438 1452                          setup(show_other_disks);
1439 1453                  } else {
1440 1454                          setup(hdrout);
1441 1455                          if (do_tty)
1442 1456                                  setup(print_tty_data);
1443 1457                          setup(show_all_disks);
1444 1458                          if (do_cpu)
1445 1459                                  setup(print_cpu_data);
1446 1460                  }
1447 1461  
1448 1462                  setup(do_newline);
1449 1463          }
1450 1464          if (do_disk & DISK_EXTENDED_ERRORS)
1451 1465                  setup(disk_errors);
1452 1466  }
1453 1467  
1454 1468  /*
1455 1469   * Add a new function to the list of functions
1456 1470   * for this invocation. Once on the stack the
1457 1471   * function is never removed nor does its place
1458 1472   * change.
1459 1473   */
1460 1474  void
1461 1475  setup(void (*nfunc)(void))
1462 1476  {
1463 1477          format_t *tmp;
1464 1478  
1465 1479          tmp = safe_alloc(sizeof (format_t));
1466 1480          tmp->nfunc = nfunc;
1467 1481          tmp->next = 0;
1468 1482          if (formatter_end)
1469 1483                  formatter_end->next = tmp;
1470 1484          else
1471 1485                  formatter_list = tmp;
1472 1486          formatter_end = tmp;
1473 1487  
1474 1488  }
1475 1489  
1476 1490  /*
1477 1491   * The functions after this comment are devoted to printing
1478 1492   * various parts of the header. They are selected based on the
1479 1493   * options provided when the program was invoked. The functions
1480 1494   * are either directly invoked in printhdr() or are indirectly
1481 1495   * invoked by being placed on the list of functions used when
1482 1496   * extended headers are used.
1483 1497   */
1484 1498  void
1485 1499  print_tty_hdr1(void)
1486 1500  {
1487 1501          char *fstr;
1488 1502          char *dstr;
1489 1503  
1490 1504          if (do_raw == 0) {
1491 1505                  fstr = "%10.10s";
1492 1506                  dstr = "tty    ";
1493 1507          } else {
1494 1508                  fstr = "%s";
1495 1509                  dstr = "tty";
1496 1510          }
1497 1511          push_out(fstr, dstr);
1498 1512  }
1499 1513  
1500 1514  void
1501 1515  print_tty_hdr2(void)
1502 1516  {
1503 1517          if (do_raw == 0)
1504 1518                  push_out("%-10.10s", " tin tout");
1505 1519          else
1506 1520                  push_out("tin,tout");
1507 1521  }
1508 1522  
1509 1523  void
1510 1524  print_cpu_hdr1(void)
1511 1525  {
1512 1526          char *dstr;
1513 1527  
1514 1528          if (do_raw == 0)
1515 1529                  dstr = "     cpu";
1516 1530          else
1517 1531                  dstr = "cpu";
1518 1532          push_out(dstr);
1519 1533  }
1520 1534  
1521 1535  void
1522 1536  print_cpu_hdr2(void)
1523 1537  {
1524 1538          char *dstr;
1525 1539  
1526 1540          if (do_raw == 0)
1527 1541                  dstr = " us sy dt id";
1528 1542          else
1529 1543                  dstr = "us,sy,dt,id";
1530 1544          push_out(dstr);
1531 1545  }
1532 1546  
1533 1547  /*
1534 1548   * Assumption is that tty data is always first - no need for raw mode leading
1535 1549   * comma.
1536 1550   */
1537 1551  void
1538 1552  print_tty_data(void)
1539 1553  {
1540 1554          char *fstr;
1541 1555          uint64_t deltas;
1542 1556          double raw;
1543 1557          double outch;
1544 1558          kstat_t *oldks = NULL;
1545 1559  
1546 1560          if (oldss)
1547 1561                  oldks = &oldss->s_sys.ss_agg_sys;
1548 1562  
1549 1563          if (do_raw == 0)
1550 1564                  fstr = " %3.0f %4.0f ";
1551 1565          else
1552 1566                  fstr = "%.0f,%.0f";
1553 1567          deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "rawch");
1554 1568          raw = deltas;
1555 1569          raw /= getime;
1556 1570          deltas = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "outch");
1557 1571          outch = deltas;
1558 1572          outch /= getime;
1559 1573          push_out(fstr, raw, outch);
1560 1574  }
1561 1575  
1562 1576  /*
1563 1577   * Write out CPU data
1564 1578   */
1565 1579  void
1566 1580  print_cpu_data(void)
1567 1581  {
1568 1582          char *fstr;
1569 1583          uint64_t idle;
1570 1584          uint64_t user;
1571 1585          uint64_t kern;
1572 1586          uint64_t dtrace;
1573 1587          uint64_t nsec_elapsed;
1574 1588          kstat_t *oldks = NULL;
1575 1589  
1576 1590          if (oldss)
1577 1591                  oldks = &oldss->s_sys.ss_agg_sys;
1578 1592  
1579 1593          if (do_raw == 0)
1580 1594                  fstr = " %2.0f %2.0f %2.0f %2.0f";
1581 1595          else
1582 1596                  fstr = "%.0f,%.0f,%.0f,%.0f";
1583 1597  
1584 1598          idle = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_idle");
1585 1599          user = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_user");
1586 1600          kern = kstat_delta(oldks, &newss->s_sys.ss_agg_sys, "cpu_ticks_kernel");
1587 1601          dtrace = kstat_delta(oldks, &newss->s_sys.ss_agg_sys,
1588 1602              "cpu_nsec_dtrace");
1589 1603          nsec_elapsed = newss->s_sys.ss_agg_sys.ks_snaptime -
1590 1604              (oldks == NULL ? 0 : oldks->ks_snaptime);
1591 1605          push_out(fstr, user * percent, kern * percent,
1592 1606              dtrace * 100.0 / nsec_elapsed / newss->s_nr_active_cpus,
1593 1607              idle * percent);
1594 1608  }
1595 1609  
1596 1610  /*
1597 1611   * Emit the appropriate header.
1598 1612   */
1599 1613  void
1600 1614  hdrout(void)
1601 1615  {
1602 1616          if (do_raw == 0) {
1603 1617                  if (--tohdr == 0)
1604 1618                          printhdr(0);
1605 1619          } else if (hdr_out == 0) {
1606 1620                  printhdr(0);
1607 1621                  hdr_out = 1;
1608 1622          }
1609 1623  }
1610 1624  
1611 1625  /*
1612 1626   * Write out disk errors when -E is specified.
1613 1627   */
1614 1628  void
1615 1629  disk_errors(void)
1616 1630  {
1617 1631          (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk_errors, NULL);
1618 1632  }
1619 1633  
1620 1634  void
1621 1635  show_first_disk(void)
1622 1636  {
1623 1637          int count = 0;
1624 1638  
1625 1639          show_disk_mode = SHOW_FIRST_ONLY;
1626 1640  
1627 1641          (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1628 1642  }
1629 1643  
1630 1644  void
1631 1645  show_other_disks(void)
1632 1646  {
1633 1647          int count = 0;
1634 1648  
1635 1649          show_disk_mode = SHOW_SECOND_ONWARDS;
1636 1650  
1637 1651          (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1638 1652  }
1639 1653  
1640 1654  void
1641 1655  show_all_disks(void)
1642 1656  {
1643 1657          int count = 0;
1644 1658  
1645 1659          show_disk_mode = SHOW_ALL;
1646 1660  
1647 1661          (void) snapshot_walk(SNAP_IODEVS, oldss, newss, show_disk, &count);
1648 1662  }
1649 1663  
1650 1664  /*
1651 1665   * Write a newline out and clear the lineout flag.
1652 1666   */
1653 1667  static void
1654 1668  do_newline(void)
1655 1669  {
1656 1670          if (lineout) {
1657 1671                  (void) putchar('\n');
1658 1672                  lineout = 0;
1659 1673          }
1660 1674  }
1661 1675  
1662 1676  /*
1663 1677   * Generalized printf function that determines what extra
1664 1678   * to print out if we're in raw mode. At this time we
1665 1679   * don't care about errors.
1666 1680   */
1667 1681  static void
1668 1682  push_out(const char *message, ...)
1669 1683  {
1670 1684          va_list args;
1671 1685  
1672 1686          va_start(args, message);
1673 1687          if (do_raw && lineout == 1)
1674 1688                  (void) putchar(',');
1675 1689          (void) vprintf(message, args);
1676 1690          va_end(args);
1677 1691          lineout = 1;
1678 1692  }
1679 1693  
1680 1694  /*
1681 1695   * Emit the header string when -e is specified.
1682 1696   */
1683 1697  static void
1684 1698  print_err_hdr(void)
1685 1699  {
1686 1700          char obuf[SMALL_SCRATCH_BUFLEN];
1687 1701  
1688 1702          if (do_raw) {
1689 1703                  push_out("errors");
1690 1704                  return;
1691 1705          }
1692 1706  
1693 1707          if (do_conversions == 0) {
1694 1708                  if (!(do_disk & DISK_EXTENDED)) {
1695 1709                          (void) snprintf(obuf, sizeof (obuf),
1696 1710                              "%11s", one_blank);
1697 1711                          push_out(obuf);
1698 1712                  }
1699 1713          } else if (do_disk == DISK_ERRORS)
1700 1714                  push_out(two_blanks);
1701 1715          else
1702 1716                  push_out(one_blank);
1703 1717          push_out("---- errors --- ");
1704 1718  }
1705 1719  
1706 1720  /*
1707 1721   * Emit the header string when -e is specified.
1708 1722   */
1709 1723  static void
1710 1724  print_disk_header(void)
1711 1725  {
1712 1726          push_out(disk_header);
1713 1727  }
1714 1728  
1715 1729  /*
1716 1730   * No, UINTMAX_MAX isn't the right thing here since
1717 1731   * it is #defined to be either INT32_MAX or INT64_MAX
1718 1732   * depending on the whether _LP64 is defined.
1719 1733   *
1720 1734   * We want to handle the odd future case of having
1721 1735   * ulonglong_t be more than 64 bits but we have
1722 1736   * no nice #define MAX value we can drop in place
1723 1737   * without having to change this code in the future.
1724 1738   */
1725 1739  
1726 1740  u_longlong_t
1727 1741  ull_delta(u_longlong_t old, u_longlong_t new)
1728 1742  {
1729 1743          if (new >= old)
1730 1744                  return (new - old);
1731 1745          else
1732 1746                  return ((UINT64_MAX - old) + new + 1);
1733 1747  }
1734 1748  
1735 1749  /*
1736 1750   * Take the difference of an unsigned 32
1737 1751   * bit int attempting to cater for
1738 1752   * overflow.
1739 1753   */
1740 1754  uint_t
1741 1755  u32_delta(uint_t old, uint_t new)
1742 1756  {
1743 1757          if (new >= old)
1744 1758                  return (new - old);
1745 1759          else
1746 1760                  return ((UINT32_MAX - old) + new + 1);
1747 1761  }
1748 1762  
1749 1763  /*
1750 1764   * This is exactly what is needed for standard iostat output,
1751 1765   * but make sure to use it only for that
1752 1766   */
1753 1767  #define EPSILON (0.1)
1754 1768  static int
1755 1769  fzero(double value)
1756 1770  {
1757 1771          return (value >= 0.0 && value < EPSILON);
1758 1772  }
1759 1773  
1760 1774  static int
1761 1775  safe_strtoi(char const *val, char *errmsg)
1762 1776  {
1763 1777          char *end;
1764 1778          long tmp;
1765 1779  
1766 1780          errno = 0;
1767 1781          tmp = strtol(val, &end, 10);
1768 1782          if (*end != '\0' || errno)
1769 1783                  fail(0, "%s %s", errmsg, val);
1770 1784          return ((int)tmp);
1771 1785  }
  
    | 
      ↓ open down ↓ | 
    636 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX