1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2014 David Hoeppner.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Display ZFS ARC statistics.
  18  *
  19  * Based on work by Neelakanth Nadgir and Mike Harsch.
  20  */
  21 #include <sys/list.h>
  22 #include <assert.h>
  23 #include <errno.h>
  24 #include <kstat.h>
  25 #include <libintl.h>
  26 #include <limits.h>
  27 #include <locale.h>
  28 #include <poll.h>
  29 #include <stddef.h>
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <strings.h>
  33 
  34 #include "statcommon.h"
  35 
  36 char *cmdname = "arcstat";      /* Name of this command */
  37 int caught_cont = 0;            /* Have caught a SIGCONT */
  38 
  39 /* Saved command line options */
  40 static boolean_t g_fflg = B_FALSE;      /* custom header fields */
  41 static boolean_t g_oflg = B_FALSE;      /* write output file */
  42 static boolean_t g_rflg = B_FALSE;      /* raw output */
  43 static boolean_t g_vflg = B_FALSE;      /* verbose help */
  44 static boolean_t g_xflg = B_FALSE;      /* extended header */
  45 
  46 /* Time in seconds between snapshots */
  47 int             interval = 1;
  48 
  49 static list_t   fields_list;
  50 static char     *separator = "  ";
  51 static char     *output_file = NULL;
  52 static char     *hdr;
  53 
  54 typedef struct _arcstat_delta {
  55         int64_t ad_hits;
  56         int64_t ad_miss;
  57         int64_t ad_read;
  58         int64_t ad_hit_percent;
  59         int64_t ad_miss_percent;
  60         int64_t ad_dhit;
  61         int64_t ad_dmis;
  62         int64_t ad_dread;
  63         int64_t ad_dh_percent;
  64         int64_t ad_dm_percent;
  65         int64_t ad_phit;
  66         int64_t ad_pmis;
  67         int64_t ad_pread;
  68         int64_t ad_ph_percent;
  69         int64_t ad_pm_percent;
  70         int64_t ad_mhit;
  71         int64_t ad_mmis;
  72         int64_t ad_mread;
  73         int64_t ad_mh_percent;
  74         int64_t ad_mm_percent;
  75         int64_t ad_arcsz;
  76         int64_t ad_c;
  77         int64_t ad_mfu;
  78         int64_t ad_mru;
  79         int64_t ad_mfug;
  80         int64_t ad_mrug;
  81         int64_t ad_eskip;
  82         int64_t ad_rmiss;
  83         int64_t ad_mtxmis;
  84 } arcstat_delta_t;
  85 
  86 typedef struct arcstat_snapshot {
  87         kstat_t as_arcstats;
  88         uint64_t as_hits;
  89         uint64_t as_misses;
  90         uint64_t as_demand_data_hits;
  91         uint64_t as_demand_data_misses;
  92         uint64_t as_demand_metadata_hits;
  93         uint64_t as_demand_metadata_misses;
  94         uint64_t as_prefetch_data_hits;
  95         uint64_t as_prefetch_data_misses;
  96         uint64_t as_prefetch_metadata_hits;
  97         uint64_t as_prefetch_metadata_misses;
  98         uint64_t as_size;
  99         uint64_t as_c;
 100         uint64_t as_mfu_hits;
 101         uint64_t as_mru_hits;
 102         uint64_t as_mru_ghost_hits;
 103         uint64_t as_mfu_ghost_hits;
 104         uint64_t as_evict_skip;
 105         uint64_t as_recycle_miss;
 106         uint64_t as_mutex_miss;
 107         uint64_t as_l2_hits;
 108         uint64_t as_l2_misses;
 109         uint64_t as_l2_size;
 110         uint64_t as_l2_read_bytes;
 111 } arcstat_snapshot_t;
 112 
 113 typedef struct arcstat_hdr_field {
 114         list_node_t     ahf_next;
 115         char            *ahf_name;
 116         struct arcstat_field *ahf_desc;
 117 } arcstat_hdr_field_t;
 118 
 119 typedef enum field_type {
 120         as_time,
 121         as_hits,
 122         as_miss,
 123         as_read,
 124         as_hit_percent,
 125         as_miss_percent,
 126         as_dhit,
 127         as_dmis,
 128         as_dread,
 129         as_dh_percent,
 130         as_dm_percent,
 131         as_phit,
 132         as_pmis,
 133         as_pread,
 134         as_ph_percent,
 135         as_pm_percent,
 136         as_mhit,
 137         as_mmis,
 138         as_mread,
 139         as_mh_percent,
 140         as_mm_percent,
 141         as_arcsz,
 142         as_c,
 143 } field_type_t;
 144 
 145 /* XXX */
 146 struct arcstat_field {
 147         char    *af_name;
 148         char    *af_description;
 149         int     af_length;
 150         int     af_interval;
 151         field_type_t    af_type;
 152 } arcstat_fields[] = {
 153         { "time", "Time", 8, -1, as_time },
 154         { "hits", "ARC reads per second", 4, 1000, as_hits },
 155         { "miss", "ARC misses per second", 4, 1000, as_miss },
 156         { "read", "Total ARC accesses per second", 4, 1000, as_read },
 157         { "hit%", "ARC Hit percentage", 4, 100, as_hit_percent },
 158         { "miss%", "ARC miss percentage", 5, 100, as_miss_percent },
 159         { "dhit", "Demand Data hits per second", 4, 1000, as_dhit },
 160         { "dmis", "Demand Data misses per second", 4, 1000, as_dmis },
 161         { "dread", "Demand data accesses per second", 5, 1000, as_dread },
 162         { "dh%", "Demand Data hit percentage", 3, 100, as_dh_percent  },
 163         { "dm%", "Demand Data miss percentage", 3, 100, as_dm_percent },
 164         { "phit", "Prefetch hits per second", 4, 1000, as_phit },
 165         { "pmis", "Prefetch misses per second", 4, 1000, as_pmis },
 166         { "ph%", "Prefetch hits percentage", 3, 100, as_ph_percent },
 167         { "pm%", "Prefetch miss percentage", 3, 100, as_pm_percent },
 168         { "mhit", "Metadata hits per second", 4, 1000, as_mhit },
 169         { "mmis", "Metadata misses per second", 4, 1000, as_mmis },
 170         { "mread", "Metadata accesses per second", 4, 1000, as_mread },
 171         { "mh%", "Metadata hit percentage", 3, 100, as_mh_percent },
 172         { "mm%", "Metadata miss percentage", 3, 100, as_mm_percent },
 173         { "arcsz", "ARC Size", 5, 1024, as_arcsz },
 174         { "c", "ARC Target Size", 4, 1024, as_c },
 175         { "mfu", "MFU List hits per second", 4, 1000 },
 176         { "mru", "MRU List hits per second", 4, 1000 },
 177         { "mfug", "MFU Ghost List hits per second", 4, 1000 },
 178         { "mrug", "MRU Ghost List hits per second", 4, 1000 },
 179         { "eskip", "evict_skip per second", 5, 1000 },
 180         { "mtxmis", "mutex_miss per second", 6, 1000 },
 181         { "rmis", "recycle_miss per second", 4, 1000 },
 182         { "pread", "Prefetch accesses per second", 5, 1000 },
 183         { "l2hits", "L2ARC hits per second", 6, 1000 },
 184         { "l2miss", "L2ARC misses per second", 6, 1000 },
 185         { "l2read", "Total L2ARC accesses per second", 6, 1000 },
 186         { "l2hit%", "L2ARC access hit percentage", 6, 100 },
 187         { "l2miss%", "L2ARC access miss percentage", 7, 100 },
 188         { "l2size", "Size of the L2ARC", 6, 1024 },
 189         { "l2bytes", "bytes read per second from the L2ARC", 7, 1024 },
 190         { NULL, NULL, -1, -1 },
 191 };
 192 
 193 /*
 194  * Print usage.
 195  */
 196 static void
 197 usage(void)
 198 {
 199         (void) fprintf(stderr, gettext(
 200             "Usage: arcstat [-hvxr] [-f fields] [-o file] [-s string] "
 201             "[interval [count]]\n\n"));
 202 
 203         if (g_vflg) {
 204                 int     i = 0;
 205 
 206                 (void) fprintf(stderr, gettext(
 207                     "Field definitions are as follows:\n"));
 208 
 209                 for (;arcstat_fields[i].af_name != NULL; i++) {
 210                         (void) fprintf(stderr, "%11s : ",
 211                             arcstat_fields[i].af_name);
 212                         (void) fprintf(stderr, "%s\n", gettext(
 213                             arcstat_fields[i].af_description));
 214                 }
 215         }
 216 }
 217 
 218 /*
 219  * Print header.
 220  */
 221 static void
 222 printhdr(int sig)
 223 {
 224         arcstat_hdr_field_t     *hdr_field;
 225 
 226         /*
 227          * Reenable the signal.
 228          */
 229         if (sig)
 230                 (void) signal(SIGCONT, printhdr);
 231         if (sig == SIGCONT)
 232                 caught_cont = 1;
 233 
 234         /*
 235          * Print fields in fields list for this header.
 236          */
 237         hdr_field = list_head(&fields_list);
 238         while (hdr_field != NULL) {
 239                 if (g_rflg) {
 240                         printf("%s%s", hdr_field->ahf_name, separator);
 241                 } else {
 242                         printf("%*s%s", hdr_field->ahf_desc->af_length,
 243                             hdr_field->ahf_name, separator);
 244                 }
 245 
 246                 hdr_field = list_next(&fields_list, hdr_field);
 247         }
 248 
 249         (void) putchar('\n');
 250 }
 251 
 252 /*
 253  * Pretty print values.
 254  */
 255 static void
 256 printvals(arcstat_delta_t *delta)
 257 {
 258         arcstat_hdr_field_t     *hdr_field;
 259 
 260 #define ARCSTAT_PRINT_VALUE(field, delta, value)        \
 261         (void) printf("%*llu%s", field->ahf_desc->af_length, delta->ad_##value, separator);
 262 
 263         /*
 264          * Print values for fields.
 265          */
 266         hdr_field = list_head(&fields_list);
 267         while (hdr_field != NULL) {
 268                 switch (hdr_field->ahf_desc->af_type) {
 269                 case as_time: {
 270                         time_t  t = time(NULL);
 271                         char    dstr[64];
 272                         char    *fmt = "%H:%M:%S";
 273                         int     len;
 274 
 275                         len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
 276                         if (len > 0)
 277                                 (void) printf("%s%s", dstr, separator);
 278                         break;
 279                 };
 280                 case as_miss:
 281                         ARCSTAT_PRINT_VALUE(hdr_field, delta, miss);
 282                         break;
 283                 case as_read:
 284                         (void) printf("%*llu%s", hdr_field->ahf_desc->af_length, delta->ad_miss, separator);
 285                         break;
 286                 case as_hit_percent:
 287                         ARCSTAT_PRINT_VALUE(hdr_field, delta, hit_percent);
 288                         break;
 289                 case as_miss_percent:
 290                         ARCSTAT_PRINT_VALUE(hdr_field, delta, miss_percent);
 291                         break;
 292                 case as_dhit:
 293                         ARCSTAT_PRINT_VALUE(hdr_field, delta, dhit);
 294                         break;
 295                 case as_dmis:
 296                         ARCSTAT_PRINT_VALUE(hdr_field, delta, dmis);
 297                         break;
 298                 case as_dread:
 299                         ARCSTAT_PRINT_VALUE(hdr_field, delta, dread);
 300                         break;
 301                 case as_dh_percent:
 302                         ARCSTAT_PRINT_VALUE(hdr_field, delta, dh_percent);
 303                         break;
 304                 case as_dm_percent:
 305                         ARCSTAT_PRINT_VALUE(hdr_field, delta, dm_percent);
 306                         break;
 307                 case as_phit:
 308                         ARCSTAT_PRINT_VALUE(hdr_field, delta, phit);
 309                         break;
 310                 case as_pmis:
 311                         ARCSTAT_PRINT_VALUE(hdr_field, delta, pmis);
 312                         break;
 313                 case as_pread:
 314                         ARCSTAT_PRINT_VALUE(hdr_field, delta, pread);
 315                         break;
 316                 case as_ph_percent:
 317                         ARCSTAT_PRINT_VALUE(hdr_field, delta, ph_percent);
 318                         break;
 319                 case as_pm_percent:
 320                         ARCSTAT_PRINT_VALUE(hdr_field, delta, pm_percent);
 321                         break;
 322                 case as_mhit:
 323                         ARCSTAT_PRINT_VALUE(hdr_field, delta, mhit);
 324                         break;
 325                 case as_mmis:
 326                         ARCSTAT_PRINT_VALUE(hdr_field, delta, mmis);
 327                         break;
 328                 case as_mread:
 329                         ARCSTAT_PRINT_VALUE(hdr_field, delta, mread);
 330                         break;
 331                 case as_mh_percent:
 332                         ARCSTAT_PRINT_VALUE(hdr_field, delta, mh_percent);
 333                         break;
 334                 case as_mm_percent:
 335                         ARCSTAT_PRINT_VALUE(hdr_field, delta, mm_percent);
 336                         break;
 337                 case as_arcsz:
 338                         ARCSTAT_PRINT_VALUE(hdr_field, delta, arcsz);
 339                         break;
 340                 case as_c:
 341                         ARCSTAT_PRINT_VALUE(hdr_field, delta, c);
 342                         break;
 343                 default:
 344                         break;
 345                 }
 346 
 347                 // number_to_scaled_string
 348 
 349                 hdr_field = list_next(&fields_list, hdr_field);
 350         }
 351 
 352         (void) putchar('\n');
 353 }
 354 
 355 arcstat_snapshot_t *
 356 arcstat_acquire_snapshot(kstat_ctl_t *kc)
 357 {
 358         arcstat_snapshot_t      *snapshot;
 359         kstat_named_t           *knp;
 360         kstat_t                 *ksp;
 361 
 362         if ((ksp = kstat_lookup(kc, "zfs", 0, "arcstats")) == NULL)
 363                 return (NULL);
 364 
 365         if (kstat_read(kc, ksp, NULL) == -1)
 366                 return (NULL);
 367 
 368         snapshot = safe_alloc(sizeof (arcstat_snapshot_t));
 369 
 370 /* XXX snapshot */
 371 #define ARC_KSTAT_DATA_LOOKUP(name)                                \
 372         knp = (kstat_named_t *)kstat_data_lookup(ksp, #name);      \
 373         if (knp == NULL)                                        \
 374                 return (NULL);                                 \
 375         snapshot->as_##name = knp->value.ui64;
 376 
 377         ARC_KSTAT_DATA_LOOKUP(hits);
 378         ARC_KSTAT_DATA_LOOKUP(misses);
 379         ARC_KSTAT_DATA_LOOKUP(demand_data_hits);
 380         ARC_KSTAT_DATA_LOOKUP(demand_data_misses);
 381         ARC_KSTAT_DATA_LOOKUP(demand_metadata_hits);
 382         ARC_KSTAT_DATA_LOOKUP(demand_metadata_misses);
 383         ARC_KSTAT_DATA_LOOKUP(prefetch_data_hits);
 384         ARC_KSTAT_DATA_LOOKUP(prefetch_data_misses);
 385         ARC_KSTAT_DATA_LOOKUP(prefetch_metadata_hits);
 386         ARC_KSTAT_DATA_LOOKUP(prefetch_metadata_misses);
 387         ARC_KSTAT_DATA_LOOKUP(size);
 388         ARC_KSTAT_DATA_LOOKUP(c);
 389         ARC_KSTAT_DATA_LOOKUP(mfu_hits);
 390         ARC_KSTAT_DATA_LOOKUP(mru_hits);
 391         ARC_KSTAT_DATA_LOOKUP(mru_ghost_hits);
 392         ARC_KSTAT_DATA_LOOKUP(mfu_ghost_hits);
 393         ARC_KSTAT_DATA_LOOKUP(evict_skip);
 394         ARC_KSTAT_DATA_LOOKUP(recycle_miss);
 395         ARC_KSTAT_DATA_LOOKUP(mutex_miss);
 396         ARC_KSTAT_DATA_LOOKUP(l2_hits);
 397         ARC_KSTAT_DATA_LOOKUP(l2_misses);
 398         ARC_KSTAT_DATA_LOOKUP(l2_size);
 399         ARC_KSTAT_DATA_LOOKUP(l2_read_bytes);
 400 
 401         return (snapshot);
 402 }
 403 
 404 static void
 405 arcstat_free_snapshot(arcstat_snapshot_t *old)
 406 {
 407 }
 408 
 409 static void
 410 arcstat_chain_update(kstat_ctl_t *kc)
 411 {
 412         int     ret;
 413 
 414         ret = kstat_chain_update(kc);
 415         if (ret != 0) {
 416                 (void) printf("<State Changed>\n");
 417         }
 418 }
 419 
 420 static void
 421 free_field_list(list_t list)
 422 {
 423         arcstat_hdr_field_t     *hdr_field;
 424 
 425         hdr_field = list_head(&list);
 426         while (hdr_field != NULL) {
 427 
 428                 hdr_field = list_next(&list, hdr_field);
 429         }
 430 }
 431 
 432 static arcstat_delta_t *
 433 arcstat_calculate_delta(arcstat_snapshot_t *old, arcstat_snapshot_t *new)
 434 {
 435         arcstat_delta_t *delta;
 436 
 437         assert(old != NULL && new != NULL);
 438 
 439         delta = safe_alloc(sizeof (arcstat_delta_t));
 440 
 441         (void) memset(delta, 0, sizeof (arcstat_delta_t));
 442 
 443 #define ARC_SNAPSHOT_DIFF(O, N, S)                      \
 444         (N->as_##S - O->as_##S)
 445 
 446         delta->ad_hits = ARC_SNAPSHOT_DIFF(old, new, hits) / interval;
 447         delta->ad_miss = ARC_SNAPSHOT_DIFF(old, new, misses) / interval;
 448 
 449         delta->ad_read = delta->ad_hits + delta->ad_miss;
 450         if (delta->ad_read > 0) {
 451                 delta->ad_hit_percent = 100 * (delta->ad_hits / delta->ad_read);
 452                 delta->ad_miss_percent = 100 - delta->ad_hit_percent;
 453         }
 454 
 455         delta->ad_dhit = (ARC_SNAPSHOT_DIFF(old, new, demand_data_hits) +
 456             ARC_SNAPSHOT_DIFF(old, new, demand_metadata_hits)) / interval;
 457         delta->ad_dmis = (ARC_SNAPSHOT_DIFF(old, new, demand_data_misses) +
 458             ARC_SNAPSHOT_DIFF(old, new, demand_metadata_misses)) / interval;
 459 
 460         delta->ad_dread = delta->ad_dhit + delta->ad_dmis;
 461         if (delta->ad_dread > 0) {
 462                 delta->ad_dh_percent = 100 * (delta->ad_dhit / delta->ad_dread);
 463                 delta->ad_dm_percent = 100 - delta->ad_dh_percent;
 464         }
 465 
 466         delta->ad_phit = (ARC_SNAPSHOT_DIFF(old, new, prefetch_data_hits) +
 467             ARC_SNAPSHOT_DIFF(old, new, prefetch_metadata_hits)) / interval;
 468         delta->ad_pmis = (ARC_SNAPSHOT_DIFF(old, new, prefetch_data_misses) +
 469             ARC_SNAPSHOT_DIFF(old, new, prefetch_metadata_misses)) / interval;
 470 
 471         delta->ad_pread = delta->ad_phit + delta->ad_pmis;
 472         if (delta->ad_pread > 0) {
 473                 delta->ad_ph_percent = 100 * (delta->ad_phit / delta->ad_pread);
 474                 delta->ad_pm_percent = 100 - delta->ad_ph_percent;
 475         }
 476 
 477         delta->ad_mhit = (ARC_SNAPSHOT_DIFF(old, new, prefetch_metadata_hits) +
 478             ARC_SNAPSHOT_DIFF(old, new, demand_metadata_hits)) / interval;
 479         delta->ad_mmis = (ARC_SNAPSHOT_DIFF(old, new,
 480             prefetch_metadata_misses) + ARC_SNAPSHOT_DIFF(old, new,
 481             demand_metadata_misses)) / interval;
 482 
 483         delta->ad_mread = delta->ad_mhit + delta->ad_mmis;
 484         if (delta->ad_mread > 0) {
 485                 delta->ad_mh_percent = 100 * (delta->ad_mhit / delta->ad_mread);
 486                 delta->ad_mm_percent = 100 - delta->ad_mh_percent;
 487         }
 488 
 489         delta->ad_arcsz = new->as_size;
 490         delta->ad_c = new->as_c;
 491         delta->ad_mfu = ARC_SNAPSHOT_DIFF(old, new, mfu_hits) / interval;
 492         delta->ad_mru = ARC_SNAPSHOT_DIFF(old, new, mru_hits) / interval;
 493         delta->ad_mfug = ARC_SNAPSHOT_DIFF(old, new, mfu_ghost_hits) /
 494             interval;
 495         delta->ad_mrug = ARC_SNAPSHOT_DIFF(old, new, mru_ghost_hits) /
 496             interval;
 497         delta->ad_eskip = ARC_SNAPSHOT_DIFF(old, new, evict_skip) / interval;
 498         delta->ad_rmiss = ARC_SNAPSHOT_DIFF(old, new, recycle_miss) / interval;
 499         delta->ad_mtxmis = ARC_SNAPSHOT_DIFF(old, new, mutex_miss) / interval;
 500 
 501         /* XXX L2 cache */
 502 
 503         return (delta);
 504 }
 505 
 506 int
 507 main(int argc, char **argv)
 508 {
 509         int             c;
 510         int             i = 0;
 511         int             iter = 1;
 512         char            *endptr;
 513         int             infinite_cycles = 0;
 514         kstat_ctl_t     *kc;
 515         hrtime_t        start_n;
 516         hrtime_t        period_n;
 517         list_t          invalid_list;
 518         list_t          incompt_list;
 519         boolean_t       l2exist = B_FALSE;
 520         arcstat_hdr_field_t     *hdr_field;
 521         arcstat_snapshot_t      *old = NULL;
 522         arcstat_snapshot_t      *new = NULL;
 523 
 524 
 525         (void) setlocale(LC_ALL, "");
 526 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 527 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it wasn't */
 528 #endif
 529         (void) textdomain(TEXT_DOMAIN);
 530 
 531         /*
 532          * Parse command line arguments.
 533          */
 534         while ((c = getopt(argc, argv, "h?f:o:rs:vx")) != EOF) {
 535                 switch (c) {
 536                 case 'h':
 537                 case '?':
 538                         usage();
 539                         exit(0);
 540                         break;
 541                 case 'f':
 542                         g_fflg = B_TRUE;
 543                         hdr = safe_strdup(optarg);
 544                         break;
 545                 case 'o':
 546                         g_oflg = B_TRUE;
 547                         output_file = (char *)optarg;
 548                 case 'r':
 549                         g_rflg = B_TRUE;
 550                         break;
 551                 case 's':
 552                         separator = (char *)optarg;
 553                         break;
 554                 case 'v':
 555                         g_vflg = B_TRUE;
 556                         usage();
 557                         exit(0);
 558                         break;
 559                 case 'x':
 560                         g_xflg = B_TRUE;
 561                         break;
 562                 default:
 563                         break;
 564                 }
 565         }
 566 
 567         /*
 568          * Select the standard, extended or user supplied header fields.
 569          */
 570         if (!g_fflg) {
 571                 if (g_xflg) {
 572                         hdr = safe_strdup("time,mfu,mru,mfug,mrug,eskip,mtxmis,rmis,dread,pread,read");
 573                 } else {
 574                         hdr = safe_strdup("time,read,miss,miss%,dmis,dm%,pmis,pm%,mmis,mm%,arcsz,c");
 575                 }
 576         }
 577 
 578         /*
 579          * Interval and count.
 580          */
 581         if (argc > optind) {
 582                 interval = (int)strtol(argv[optind], &endptr, 10);
 583                 if (*endptr != NULL)
 584                         usage();
 585                 period_n = (hrtime_t)interval * NANOSEC;
 586                 if (argc > optind + 1) {
 587                         iter = (unsigned int)strtoul
 588                             (argv[optind + 1], &endptr, 10);
 589                         if (*endptr != NULL || iter < 0)
 590                                 usage();
 591                         if (iter == 0)
 592                                 return (0);     /* XXX */
 593                 } else {
 594                         infinite_cycles = 1;
 595                 }
 596         }
 597 
 598         /*
 599          * Need to know if there is a L2 cache.
 600          */
 601         kc = open_kstat();
 602 
 603         /* XXX L2 cache */
 604 
 605         /* Valid field names */
 606         list_create(&fields_list, sizeof (arcstat_hdr_field_t),
 607             offsetof(arcstat_hdr_field_t, ahf_next));
 608 
 609         /* Invalid field names */
 610         list_create(&invalid_list, sizeof (arcstat_hdr_field_t),
 611             offsetof(arcstat_hdr_field_t, ahf_next));
 612 
 613         /* Invalid field names if no L2 ARC */
 614         list_create(&incompt_list, sizeof (arcstat_hdr_field_t),
 615             offsetof(arcstat_hdr_field_t, ahf_next));
 616 
 617         while ((endptr = (char *)strsep(&hdr, ",")) != NULL) {
 618                 boolean_t       found_field = B_FALSE;
 619                 int             i = 0;
 620 
 621                 for (; arcstat_fields[i].af_name != NULL; i++) {
 622                         if (strcmp(arcstat_fields[i].af_name, endptr) == 0) {
 623                                 found_field = B_TRUE;
 624                                 break;
 625                         }
 626                 }
 627 
 628                 /*
 629                  * Allocate a new header field and link it to the fields table.
 630                  */
 631                 hdr_field = safe_alloc(sizeof (arcstat_hdr_field_t));
 632                 hdr_field->ahf_name = safe_strdup(endptr);
 633                 hdr_field->ahf_desc = &arcstat_fields[i];
 634 
 635                 list_link_init(&hdr_field->ahf_next);
 636 
 637                 /*
 638                  * Add valid fields to fields_list or to the list of invalid fields.
 639                  */
 640                 if (found_field) {
 641                         list_insert_tail(&fields_list, hdr_field);
 642                 } else {
 643                         list_insert_tail(&invalid_list, hdr_field);
 644                 }
 645         }
 646 
 647         /*
 648          * User supplied an invalid field per cmdline.
 649          */
 650         if (!list_is_empty(&invalid_list)) {
 651                 (void) fprintf(stderr, "%s -- ", gettext(
 652                     "Invalid column definition!"));
 653 
 654                 hdr_field = list_head(&invalid_list);
 655                 while (hdr_field != NULL) {
 656                         (void) fprintf(stderr, "%s ", hdr_field->ahf_name);
 657 
 658                         hdr_field = list_next(&invalid_list, hdr_field);
 659                 }
 660 
 661                 (void) fprintf(stderr, "\n\n");
 662 
 663                 //free_field_list(invalid_list);
 664 
 665                 usage();
 666                 exit(2);
 667         }
 668 
 669         /*
 670          * User supplied an L2 cache field, but we have no L2 cache.
 671          */
 672         if (!list_is_empty(&incompt_list)) {
 673                 (void) fprintf(stderr, "%s -- ", gettext(
 674                     "Incompatible field specified!"));
 675 
 676                 hdr_field = list_head(&incompt_list);
 677                 while (hdr_field != NULL) {
 678                         (void) fprintf(stderr, "%s ", hdr_field->ahf_name);
 679 
 680                         hdr_field = list_next(&incompt_list, hdr_field);
 681                 }
 682 
 683                 (void) fprintf(stderr, "\n\n");
 684 
 685                 usage();
 686                 exit(2);
 687         }
 688 
 689         /* We should have at least one valid field */
 690         assert(!list_is_empty(&fields_list));
 691 
 692         /*
 693          * If we need write to a file, try to open it.
 694          */
 695         if (g_oflg) {
 696 
 697         }
 698 
 699         // (void) sigset(SIGCONT, printhdr);
 700         /* Set up handler for SIGCONT */
 701         if (signal(SIGCONT, cont_handler) == SIG_ERR)
 702                 fail(1, "signal failed");
 703 
 704         start_n = gethrtime();
 705 
 706         new = arcstat_acquire_snapshot(kc);
 707         while (infinite_cycles || iter > 0) {
 708                 arcstat_delta_t *delta;
 709 
 710                 arcstat_free_snapshot(old);
 711                 old = new;
 712                 new = arcstat_acquire_snapshot(kc);
 713 
 714                 arcstat_chain_update(kc);
 715 
 716                 /* XXX */
 717                 if (i % 20 == 0)
 718                         printhdr(0);
 719                 i++;
 720 
 721                 delta = arcstat_calculate_delta(old, new);
 722                 printvals(delta);
 723 
 724                 if (!infinite_cycles && --iter < 1)
 725                         break;
 726 
 727                 sleep_until(&start_n, period_n, infinite_cycles, &caught_cont);
 728         }
 729 
 730         (void) kstat_close(kc);
 731 
 732         arcstat_free_snapshot(old);
 733         arcstat_free_snapshot(new);
 734 
 735         free(hdr);
 736 
 737         return (0);
 738 }
 739 
 740 #define NUMBER_WIDTH    64
 741 typedef char numbuf_t[NUMBER_WIDTH];
 742 
 743 /* Copied from du.c */
 744 static char *
 745 number_to_scaled_string(
 746         numbuf_t buf,                   /* put the result here */
 747         unsigned long long number,      /* convert this number */
 748         unsigned long long unit_from,   /* number of byes per input unit */
 749         unsigned long long scale)       /* 1024 (-h) or 1000 (-H) */
 750 {
 751         unsigned long long save = 0;
 752         char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
 753         char *uom = M;  /* unit of measurement, initially 'K' (=M[0]) */
 754 
 755         if ((long long)number == (long long) -1) {
 756                 (void) strcpy(buf, "-1");
 757                 return (buf);
 758         }
 759 
 760         /*
 761          * Convert number from unit_from to given scale (1024 or 1000)
 762          * This means multiply number with unit_from and divide by scale.
 763          * if number is large enough, we first divide and then multiply
 764          * to avoid an overflow (large enough here means 100 (rather arbitrary
 765          * value) times scale in order to reduce rounding errors)
 766          * otherwise, we first multiply and then divide to avoid an underflow.
 767          */
 768         if (number >= 100L * scale) {
 769                 number = number / scale;
 770                 number = number * unit_from;
 771         } else {
 772                 number = number * unit_from;
 773                 number = number / scale;
 774         }
 775 
 776         /*
 777          * Now we have number as a count of scale units.
 778          * Stop scaling when we reached exa bytes, then something is
 779          * probably wrong with our number.
 780          */
 781         while ((number >= scale) && (*uom != 'E')) {
 782                 uom++;  /* Next unit of measurement */
 783                 save = number;
 784                 number = (number + (scale / 2)) / scale;
 785         }
 786 
 787         /* Check if we should output a decimal place after the point */
 788         if (save && ((save / scale) < 10)) {
 789                 /* sprintf() will round for us */
 790                 float fnum = (float)save / scale;
 791                 (void) sprintf(buf, "%.1f%c", fnum, *uom);
 792         } else {
 793                 (void) sprintf(buf, "%llu%c", number, *uom);
 794         }
 795         return (buf);
 796 }