aboutsummaryrefslogtreecommitdiff
path: root/cmd/arcstat.in
blob: 220f343b5b62ac80c81ad565fd19161b7e668703 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
#!/usr/bin/env @PYTHON_SHEBANG@
#
# Print out ZFS ARC Statistics exported via kstat(1)
# For a definition of fields, or usage, use arcstat -v
#
# This script was originally a fork of the original arcstat.pl (0.1)
# by Neelakanth Nadgir, originally published on his Sun blog on
# 09/18/2007
#     http://blogs.sun.com/realneel/entry/zfs_arc_statistics
#
# A new version aimed to improve upon the original by adding features
# and fixing bugs as needed.  This version was maintained by Mike
# Harsch and was hosted in a public open source repository:
#    http://github.com/mharsch/arcstat
#
# but has since moved to the illumos-gate repository.
#
# This Python port was written by John Hixson for FreeNAS, introduced
# in commit e2c29f:
#    https://github.com/freenas/freenas
#
# and has been improved by many people since.
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License").  You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Fields have a fixed width. Every interval, we fill the "v"
# hash with its corresponding value (v[field]=value) using calculate().
# @hdr is the array of fields that needs to be printed, so we
# just iterate over this array and print the values using our pretty printer.
#
# This script must remain compatible with Python 3.6+.
#

import sys
import time
import getopt
import re
import copy

from signal import signal, SIGINT, SIGWINCH, SIG_DFL


cols = {
    # HDR:        [Size, Scale, Description]
    "time":       [8, -1, "Time"],
    "hits":       [4, 1000, "ARC hits per second"],
    "iohs":       [4, 1000, "ARC I/O hits per second"],
    "miss":       [4, 1000, "ARC misses per second"],
    "read":       [4, 1000, "Total ARC accesses per second"],
    "hit%":       [4, 100, "ARC hit percentage"],
    "ioh%":       [4, 100, "ARC I/O hit percentage"],
    "miss%":      [5, 100, "ARC miss percentage"],
    "dhit":       [4, 1000, "Demand hits per second"],
    "dioh":       [4, 1000, "Demand I/O hits per second"],
    "dmis":       [4, 1000, "Demand misses per second"],
    "dh%":        [3, 100, "Demand hit percentage"],
    "di%":        [3, 100, "Demand I/O hit percentage"],
    "dm%":        [3, 100, "Demand miss percentage"],
    "ddhit":      [5, 1000, "Demand data hits per second"],
    "ddioh":      [5, 1000, "Demand data I/O hits per second"],
    "ddmis":      [5, 1000, "Demand data misses per second"],
    "ddh%":       [4, 100, "Demand data hit percentage"],
    "ddi%":       [4, 100, "Demand data I/O hit percentage"],
    "ddm%":       [4, 100, "Demand data miss percentage"],
    "dmhit":      [5, 1000, "Demand metadata hits per second"],
    "dmioh":      [5, 1000, "Demand metadata I/O hits per second"],
    "dmmis":      [5, 1000, "Demand metadata misses per second"],
    "dmh%":       [4, 100, "Demand metadata hit percentage"],
    "dmi%":       [4, 100, "Demand metadata I/O hit percentage"],
    "dmm%":       [4, 100, "Demand metadata miss percentage"],
    "phit":       [4, 1000, "Prefetch hits per second"],
    "pioh":       [4, 1000, "Prefetch I/O hits per second"],
    "pmis":       [4, 1000, "Prefetch misses per second"],
    "ph%":        [3, 100, "Prefetch hits percentage"],
    "pi%":        [3, 100, "Prefetch I/O hits percentage"],
    "pm%":        [3, 100, "Prefetch miss percentage"],
    "pdhit":      [5, 1000, "Prefetch data hits per second"],
    "pdioh":      [5, 1000, "Prefetch data I/O hits per second"],
    "pdmis":      [5, 1000, "Prefetch data misses per second"],
    "pdh%":       [4, 100, "Prefetch data hits percentage"],
    "pdi%":       [4, 100, "Prefetch data I/O hits percentage"],
    "pdm%":       [4, 100, "Prefetch data miss percentage"],
    "pmhit":      [5, 1000, "Prefetch metadata hits per second"],
    "pmioh":      [5, 1000, "Prefetch metadata I/O hits per second"],
    "pmmis":      [5, 1000, "Prefetch metadata misses per second"],
    "pmh%":       [4, 100, "Prefetch metadata hits percentage"],
    "pmi%":       [4, 100, "Prefetch metadata I/O hits percentage"],
    "pmm%":       [4, 100, "Prefetch metadata miss percentage"],
    "mhit":       [4, 1000, "Metadata hits per second"],
    "mioh":       [4, 1000, "Metadata I/O hits per second"],
    "mmis":       [4, 1000, "Metadata misses per second"],
    "mread":      [5, 1000, "Metadata accesses per second"],
    "mh%":        [3, 100, "Metadata hit percentage"],
    "mi%":        [3, 100, "Metadata I/O hit percentage"],
    "mm%":        [3, 100, "Metadata miss percentage"],
    "arcsz":      [5, 1024, "ARC size"],
    "size":       [5, 1024, "ARC size"],
    "c":          [5, 1024, "ARC target size"],
    "mfu":        [4, 1000, "MFU list hits per second"],
    "mru":        [4, 1000, "MRU list hits per second"],
    "mfug":       [4, 1000, "MFU ghost list hits per second"],
    "mrug":       [4, 1000, "MRU ghost list hits per second"],
    "unc":        [4, 1000, "Uncached list hits per second"],
    "eskip":      [5, 1000, "evict_skip per second"],
    "el2skip":    [7, 1000, "evict skip, due to l2 writes, per second"],
    "el2cach":    [7, 1024, "Size of L2 cached evictions per second"],
    "el2el":      [5, 1024, "Size of L2 eligible evictions per second"],
    "el2mfu":     [6, 1024, "Size of L2 eligible MFU evictions per second"],
    "el2mru":     [6, 1024, "Size of L2 eligible MRU evictions per second"],
    "el2inel":    [7, 1024, "Size of L2 ineligible evictions per second"],
    "mtxmis":     [6, 1000, "mutex_miss per second"],
    "dread":      [5, 1000, "Demand accesses per second"],
    "ddread":     [6, 1000, "Demand data accesses per second"],
    "dmread":     [6, 1000, "Demand metadata accesses per second"],
    "pread":      [5, 1000, "Prefetch accesses per second"],
    "pdread":     [6, 1000, "Prefetch data accesses per second"],
    "pmread":     [6, 1000, "Prefetch metadata accesses per second"],
    "l2hits":     [6, 1000, "L2ARC hits per second"],
    "l2miss":     [6, 1000, "L2ARC misses per second"],
    "l2read":     [6, 1000, "Total L2ARC accesses per second"],
    "l2hit%":     [6, 100, "L2ARC access hit percentage"],
    "l2miss%":    [7, 100, "L2ARC access miss percentage"],
    "l2pref":     [6, 1024, "L2ARC prefetch allocated size"],
    "l2mfu":      [5, 1024, "L2ARC MFU allocated size"],
    "l2mru":      [5, 1024, "L2ARC MRU allocated size"],
    "l2data":     [6, 1024, "L2ARC data allocated size"],
    "l2meta":     [6, 1024, "L2ARC metadata allocated size"],
    "l2pref%":    [7, 100, "L2ARC prefetch percentage"],
    "l2mfu%":     [6, 100, "L2ARC MFU percentage"],
    "l2mru%":     [6, 100, "L2ARC MRU percentage"],
    "l2data%":    [7, 100, "L2ARC data percentage"],
    "l2meta%":    [7, 100, "L2ARC metadata percentage"],
    "l2asize":    [7, 1024, "Actual (compressed) size of the L2ARC"],
    "l2size":     [6, 1024, "Size of the L2ARC"],
    "l2bytes":    [7, 1024, "Bytes read per second from the L2ARC"],
    "grow":       [4, 1000, "ARC grow disabled"],
    "need":       [5, 1024, "ARC reclaim need"],
    "free":       [5, 1024, "ARC free memory"],
    "avail":      [5, 1024, "ARC available memory"],
    "waste":      [5, 1024, "Wasted memory due to round up to pagesize"],
    "ztotal":     [6, 1000, "zfetch total prefetcher calls per second"],
    "zhits":      [5, 1000, "zfetch stream hits per second"],
    "zahead":     [6, 1000, "zfetch hits ahead of streams per second"],
    "zpast":      [5, 1000, "zfetch hits behind streams per second"],
    "zmisses":    [7, 1000, "zfetch stream misses per second"],
    "zmax":       [4, 1000, "zfetch limit reached per second"],
    "zfuture":    [7, 1000, "zfetch stream future per second"],
    "zstride":    [7, 1000, "zfetch stream strides per second"],
    "zissued":    [7, 1000, "zfetch prefetches issued per second"],
    "zactive":    [7, 1000, "zfetch prefetches active per second"],
}

v = {}
hdr = ["time", "read", "ddread", "ddh%", "dmread", "dmh%", "pread", "ph%",
       "size", "c", "avail"]
xhdr = ["time", "mfu", "mru", "mfug", "mrug", "unc", "eskip", "mtxmis",
        "dread", "pread", "read"]
zhdr = ["time", "ztotal", "zhits", "zahead", "zpast", "zmisses", "zmax",
        "zfuture", "zstride", "zissued", "zactive"]
sint = 1               # Default interval is 1 second
count = 1              # Default count is 1
hdr_intr = 20          # Print header every 20 lines of output
opfile = None
sep = "  "              # Default separator is 2 spaces
l2exist = False
cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
       "[count]]\n")
cur = {}
d = {}
out = None
kstat = None
pretty_print = True


if sys.platform.startswith('freebsd'):
    # Requires py-sysctl on FreeBSD
    import sysctl

    def kstat_update():
        global kstat

        k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
             if ctl.type != sysctl.CTLTYPE_NODE]

        if not k:
            sys.exit(1)

        kstat = {}

        for s in k:
            if not s:
                continue

            name, value = s.name, s.value
            # Trims 'kstat.zfs.misc.arcstats' from the name
            kstat[name[24:]] = int(value)

elif sys.platform.startswith('linux'):
    def kstat_update():
        global kstat

        k1 = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]

        k2 = ["zfetch_" + line.strip() for line in
             open('/proc/spl/kstat/zfs/zfetchstats')]

        if k1 is None or k2 is None:
            sys.exit(1)

        del k1[0:2]
        del k2[0:2]
        k = k1 + k2
        kstat = {}

        for s in k:
            if not s:
                continue

            name, unused, value = s.split()
            kstat[name] = int(value)


def detailed_usage():
    sys.stderr.write("%s\n" % cmd)
    sys.stderr.write("Field definitions are as follows:\n")
    for key in cols:
        sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
    sys.stderr.write("\n")

    sys.exit(0)


def usage():
    sys.stderr.write("%s\n" % cmd)
    sys.stderr.write("\t -h : Print this help message\n")
    sys.stderr.write("\t -a : Print all possible stats\n")
    sys.stderr.write("\t -v : List all possible field headers and definitions"
                     "\n")
    sys.stderr.write("\t -x : Print extended stats\n")
    sys.stderr.write("\t -z : Print zfetch stats\n")
    sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
    sys.stderr.write("\t -o : Redirect output to the specified file\n")
    sys.stderr.write("\t -s : Override default field separator with custom "
                     "character or string\n")
    sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
    sys.stderr.write("\nExamples:\n")
    sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
    sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
    sys.stderr.write("\tarcstat -v\n")
    sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
    sys.stderr.write("\n")

    sys.exit(1)


def snap_stats():
    global cur
    global kstat

    prev = copy.deepcopy(cur)
    kstat_update()

    cur = kstat
    for key in cur:
        if re.match(key, "class"):
            continue
        if key in prev:
            d[key] = cur[key] - prev[key]
        else:
            d[key] = cur[key]


def prettynum(sz, scale, num=0):
    suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
    index = 0
    save = 0

    # Special case for date field
    if scale == -1:
        return "%s" % num

    # Rounding error, return 0
    elif 0 < num < 1:
        num = 0

    while abs(num) > scale and index < 5:
        save = num
        num = num / scale
        index += 1

    if index == 0:
        return "%*d" % (sz, num)

    if abs(save / scale) < 10:
        return "%*.1f%s" % (sz - 1, num, suffix[index])
    else:
        return "%*d%s" % (sz - 1, num, suffix[index])


def print_values():
    global hdr
    global sep
    global v
    global pretty_print

    if pretty_print:
        fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
    else:
        fmt = lambda col: str(v[col])

    sys.stdout.write(sep.join(fmt(col) for col in hdr))
    sys.stdout.write("\n")
    sys.stdout.flush()


def print_header():
    global hdr
    global sep
    global pretty_print

    if pretty_print:
        fmt = lambda col: "%*s" % (cols[col][0], col)
    else:
        fmt = lambda col: col

    sys.stdout.write(sep.join(fmt(col) for col in hdr))
    sys.stdout.write("\n")


def get_terminal_lines():
    try:
        import fcntl
        import termios
        import struct
        data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
        sz = struct.unpack('hh', data)
        return sz[0]
    except Exception:
        pass


def update_hdr_intr():
    global hdr_intr

    lines = get_terminal_lines()
    if lines and lines > 3:
        hdr_intr = lines - 3


def resize_handler(signum, frame):
    update_hdr_intr()


def init():
    global sint
    global count
    global hdr
    global xhdr
    global zhdr
    global opfile
    global sep
    global out
    global l2exist
    global pretty_print

    desired_cols = None
    aflag = False
    xflag = False
    hflag = False
    vflag = False
    zflag = False
    i = 1

    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            "axzo:hvs:f:p",
            [
                "all",
                "extended",
                "zfetch",
                "outfile",
                "help",
                "verbose",
                "separator",
                "columns",
                "parsable"
            ]
        )
    except getopt.error as msg:
        sys.stderr.write("Error: %s\n" % str(msg))
        usage()
        opts = None

    for opt, arg in opts:
        if opt in ('-a', '--all'):
            aflag = True
        if opt in ('-x', '--extended'):
            xflag = True
        if opt in ('-o', '--outfile'):
            opfile = arg
            i += 1
        if opt in ('-h', '--help'):
            hflag = True
        if opt in ('-v', '--verbose'):
            vflag = True
        if opt in ('-s', '--separator'):
            sep = arg
            i += 1
        if opt in ('-f', '--columns'):
            desired_cols = arg
            i += 1
        if opt in ('-p', '--parsable'):
            pretty_print = False
        if opt in ('-z', '--zfetch'):
            zflag = True
        i += 1

    argv = sys.argv[i:]
    sint = int(argv[0]) if argv else sint
    count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)

    if hflag or (xflag and zflag) or ((zflag or xflag) and desired_cols):
        usage()

    if vflag:
        detailed_usage()

    if xflag:
        hdr = xhdr

    if zflag:
        hdr = zhdr

    update_hdr_intr()

    # check if L2ARC exists
    snap_stats()
    l2_size = cur.get("l2_size")
    if l2_size:
        l2exist = True

    if desired_cols:
        hdr = desired_cols.split(",")

        invalid = []
        incompat = []
        for ele in hdr:
            if ele not in cols:
                invalid.append(ele)
            elif not l2exist and ele.startswith("l2"):
                sys.stdout.write("No L2ARC Here\n%s\n" % ele)
                incompat.append(ele)

        if len(invalid) > 0:
            sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
            usage()

        if len(incompat) > 0:
            sys.stderr.write("Incompatible field specified! -- %s\n" %
                             incompat)
            usage()

    if aflag:
        if l2exist:
            hdr = cols.keys()
        else:
            hdr = [col for col in cols.keys() if not col.startswith("l2")]

    if opfile:
        try:
            out = open(opfile, "w")
            sys.stdout = out

        except IOError:
            sys.stderr.write("Cannot open %s for writing\n" % opfile)
            sys.exit(1)


def calculate():
    global d
    global v
    global l2exist

    v = dict()
    v["time"] = time.strftime("%H:%M:%S", time.localtime())
    v["hits"] = d["hits"] // sint
    v["iohs"] = d["iohits"] // sint
    v["miss"] = d["misses"] // sint
    v["read"] = v["hits"] + v["iohs"] + v["miss"]
    v["hit%"] = 100 * v["hits"] // v["read"] if v["read"] > 0 else 0
    v["ioh%"] = 100 * v["iohs"] // v["read"] if v["read"] > 0 else 0
    v["miss%"] = 100 - v["hit%"] - v["ioh%"] if v["read"] > 0 else 0

    v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) // sint
    v["dioh"] = (d["demand_data_iohits"] + d["demand_metadata_iohits"]) // sint
    v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) // sint

    v["dread"] = v["dhit"] + v["dioh"] + v["dmis"]
    v["dh%"] = 100 * v["dhit"] // v["dread"] if v["dread"] > 0 else 0
    v["di%"] = 100 * v["dioh"] // v["dread"] if v["dread"] > 0 else 0
    v["dm%"] = 100 - v["dh%"] - v["di%"] if v["dread"] > 0 else 0

    v["ddhit"] = d["demand_data_hits"] // sint
    v["ddioh"] = d["demand_data_iohits"] // sint
    v["ddmis"] = d["demand_data_misses"] // sint

    v["ddread"] = v["ddhit"] + v["ddioh"] + v["ddmis"]
    v["ddh%"] = 100 * v["ddhit"] // v["ddread"] if v["ddread"] > 0 else 0
    v["ddi%"] = 100 * v["ddioh"] // v["ddread"] if v["ddread"] > 0 else 0
    v["ddm%"] = 100 - v["ddh%"] - v["ddi%"] if v["ddread"] > 0 else 0

    v["dmhit"] = d["demand_metadata_hits"] // sint
    v["dmioh"] = d["demand_metadata_iohits"] // sint
    v["dmmis"] = d["demand_metadata_misses"] // sint

    v["dmread"] = v["dmhit"] + v["dmioh"] + v["dmmis"]
    v["dmh%"] = 100 * v["dmhit"] // v["dmread"] if v["dmread"] > 0 else 0
    v["dmi%"] = 100 * v["dmioh"] // v["dmread"] if v["dmread"] > 0 else 0
    v["dmm%"] = 100 - v["dmh%"] - v["dmi%"] if v["dmread"] > 0 else 0

    v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) // sint
    v["pioh"] = (d["prefetch_data_iohits"] +
                 d["prefetch_metadata_iohits"]) // sint
    v["pmis"] = (d["prefetch_data_misses"] +
                 d["prefetch_metadata_misses"]) // sint

    v["pread"] = v["phit"] + v["pioh"] + v["pmis"]
    v["ph%"] = 100 * v["phit"] // v["pread"] if v["pread"] > 0 else 0
    v["pi%"] = 100 * v["pioh"] // v["pread"] if v["pread"] > 0 else 0
    v["pm%"] = 100 - v["ph%"] - v["pi%"] if v["pread"] > 0 else 0

    v["pdhit"] = d["prefetch_data_hits"] // sint
    v["pdioh"] = d["prefetch_data_iohits"] // sint
    v["pdmis"] = d["prefetch_data_misses"] // sint

    v["pdread"] = v["pdhit"] + v["pdioh"] + v["pdmis"]
    v["pdh%"] = 100 * v["pdhit"] // v["pdread"] if v["pdread"] > 0 else 0
    v["pdi%"] = 100 * v["pdioh"] // v["pdread"] if v["pdread"] > 0 else 0
    v["pdm%"] = 100 - v["pdh%"] - v["pdi%"] if v["pdread"] > 0 else 0

    v["pmhit"] = d["prefetch_metadata_hits"] // sint
    v["pmioh"] = d["prefetch_metadata_iohits"] // sint
    v["pmmis"] = d["prefetch_metadata_misses"] // sint

    v["pmread"] = v["pmhit"] + v["pmioh"] + v["pmmis"]
    v["pmh%"] = 100 * v["pmhit"] // v["pmread"] if v["pmread"] > 0 else 0
    v["pmi%"] = 100 * v["pmioh"] // v["pmread"] if v["pmread"] > 0 else 0
    v["pmm%"] = 100 - v["pmh%"] - v["pmi%"] if v["pmread"] > 0 else 0

    v["mhit"] = (d["prefetch_metadata_hits"] +
                 d["demand_metadata_hits"]) // sint
    v["mioh"] = (d["prefetch_metadata_iohits"] +
                 d["demand_metadata_iohits"]) // sint
    v["mmis"] = (d["prefetch_metadata_misses"] +
                 d["demand_metadata_misses"]) // sint

    v["mread"] = v["mhit"] + v["mioh"] + v["mmis"]
    v["mh%"] = 100 * v["mhit"] // v["mread"] if v["mread"] > 0 else 0
    v["mi%"] = 100 * v["mioh"] // v["mread"] if v["mread"] > 0 else 0
    v["mm%"] = 100 - v["mh%"] - v["mi%"] if v["mread"] > 0 else 0

    v["arcsz"] = cur["size"]
    v["size"] = cur["size"]
    v["c"] = cur["c"]
    v["mfu"] = d["mfu_hits"] // sint
    v["mru"] = d["mru_hits"] // sint
    v["mrug"] = d["mru_ghost_hits"] // sint
    v["mfug"] = d["mfu_ghost_hits"] // sint
    v["unc"] = d["uncached_hits"] // sint
    v["eskip"] = d["evict_skip"] // sint
    v["el2skip"] = d["evict_l2_skip"] // sint
    v["el2cach"] = d["evict_l2_cached"] // sint
    v["el2el"] = d["evict_l2_eligible"] // sint
    v["el2mfu"] = d["evict_l2_eligible_mfu"] // sint
    v["el2mru"] = d["evict_l2_eligible_mru"] // sint
    v["el2inel"] = d["evict_l2_ineligible"] // sint
    v["mtxmis"] = d["mutex_miss"] // sint
    v["ztotal"] = (d["zfetch_hits"] + d["zfetch_future"] + d["zfetch_stride"] +
                   d["zfetch_past"] + d["zfetch_misses"]) // sint
    v["zhits"] = d["zfetch_hits"] // sint
    v["zahead"] = (d["zfetch_future"] + d["zfetch_stride"]) // sint
    v["zpast"] = d["zfetch_past"] // sint
    v["zmisses"] = d["zfetch_misses"] // sint
    v["zmax"] = d["zfetch_max_streams"] // sint
    v["zfuture"] = d["zfetch_future"] // sint
    v["zstride"] = d["zfetch_stride"] // sint
    v["zissued"] = d["zfetch_io_issued"] // sint
    v["zactive"] = d["zfetch_io_active"] // sint

    if l2exist:
        v["l2hits"] = d["l2_hits"] // sint
        v["l2miss"] = d["l2_misses"] // sint
        v["l2read"] = v["l2hits"] + v["l2miss"]
        v["l2hit%"] = 100 * v["l2hits"] // v["l2read"] if v["l2read"] > 0 else 0

        v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
        v["l2asize"] = cur["l2_asize"]
        v["l2size"] = cur["l2_size"]
        v["l2bytes"] = d["l2_read_bytes"] // sint

        v["l2pref"] = cur["l2_prefetch_asize"]
        v["l2mfu"] = cur["l2_mfu_asize"]
        v["l2mru"] = cur["l2_mru_asize"]
        v["l2data"] = cur["l2_bufc_data_asize"]
        v["l2meta"] = cur["l2_bufc_metadata_asize"]
        v["l2pref%"] = 100 * v["l2pref"] // v["l2asize"]
        v["l2mfu%"] = 100 * v["l2mfu"] // v["l2asize"]
        v["l2mru%"] = 100 * v["l2mru"] // v["l2asize"]
        v["l2data%"] = 100 * v["l2data"] // v["l2asize"]
        v["l2meta%"] = 100 * v["l2meta"] // v["l2asize"]

    v["grow"] = 0 if cur["arc_no_grow"] else 1
    v["need"] = cur["arc_need_free"]
    v["free"] = cur["memory_free_bytes"]
    v["avail"] = cur["memory_available_bytes"]
    v["waste"] = cur["abd_chunk_waste_size"]


def main():
    global sint
    global count
    global hdr_intr

    i = 0
    count_flag = 0

    init()
    if count > 0:
        count_flag = 1

    signal(SIGINT, SIG_DFL)
    signal(SIGWINCH, resize_handler)
    while True:
        if i == 0:
            print_header()

        snap_stats()
        calculate()
        print_values()

        if count_flag == 1:
            if count <= 1:
                break
            count -= 1

        i = 0 if i >= hdr_intr else i + 1
        time.sleep(sint)

    if out:
        out.close()


if __name__ == '__main__':
    main()