xref: /freebsd/sys/contrib/openzfs/cmd/arcstat.in (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1#!/usr/bin/env @PYTHON_SHEBANG@
2# SPDX-License-Identifier: CDDL-1.0
3#
4# Print out ZFS ARC Statistics exported via kstat(1)
5# For a definition of fields, or usage, use arcstat -v
6#
7# This script was originally a fork of the original arcstat.pl (0.1)
8# by Neelakanth Nadgir, originally published on his Sun blog on
9# 09/18/2007
10#     http://blogs.sun.com/realneel/entry/zfs_arc_statistics
11#
12# A new version aimed to improve upon the original by adding features
13# and fixing bugs as needed.  This version was maintained by Mike
14# Harsch and was hosted in a public open source repository:
15#    http://github.com/mharsch/arcstat
16#
17# but has since moved to the illumos-gate repository.
18#
19# This Python port was written by John Hixson for FreeNAS, introduced
20# in commit e2c29f:
21#    https://github.com/freenas/freenas
22#
23# and has been improved by many people since.
24#
25# CDDL HEADER START
26#
27# The contents of this file are subject to the terms of the
28# Common Development and Distribution License, Version 1.0 only
29# (the "License").  You may not use this file except in compliance
30# with the License.
31#
32# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
33# or https://opensource.org/licenses/CDDL-1.0.
34# See the License for the specific language governing permissions
35# and limitations under the License.
36#
37# When distributing Covered Code, include this CDDL HEADER in each
38# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
39# If applicable, add the following below this CDDL HEADER, with the
40# fields enclosed by brackets "[]" replaced with your own identifying
41# information: Portions Copyright [yyyy] [name of copyright owner]
42#
43# CDDL HEADER END
44#
45#
46# Fields have a fixed width. Every interval, we fill the "v"
47# hash with its corresponding value (v[field]=value) using calculate().
48# @hdr is the array of fields that needs to be printed, so we
49# just iterate over this array and print the values using our pretty printer.
50#
51# This script must remain compatible with Python 3.6+.
52#
53
54import sys
55import time
56import getopt
57import re
58import copy
59
60from signal import signal, SIGINT, SIGWINCH, SIG_DFL
61
62
63cols = {
64    # HDR:        [Size, Scale, Description]
65    "time":       [8, -1, "Time"],
66    "hits":       [4, 1000, "ARC hits per second"],
67    "iohs":       [4, 1000, "ARC I/O hits per second"],
68    "miss":       [4, 1000, "ARC misses per second"],
69    "read":       [4, 1000, "Total ARC accesses per second"],
70    "hit%":       [4, 100, "ARC hit percentage"],
71    "ioh%":       [4, 100, "ARC I/O hit percentage"],
72    "miss%":      [5, 100, "ARC miss percentage"],
73    "dhit":       [4, 1000, "Demand hits per second"],
74    "dioh":       [4, 1000, "Demand I/O hits per second"],
75    "dmis":       [4, 1000, "Demand misses per second"],
76    "dh%":        [3, 100, "Demand hit percentage"],
77    "di%":        [3, 100, "Demand I/O hit percentage"],
78    "dm%":        [3, 100, "Demand miss percentage"],
79    "ddhit":      [5, 1000, "Demand data hits per second"],
80    "ddioh":      [5, 1000, "Demand data I/O hits per second"],
81    "ddmis":      [5, 1000, "Demand data misses per second"],
82    "ddh%":       [4, 100, "Demand data hit percentage"],
83    "ddi%":       [4, 100, "Demand data I/O hit percentage"],
84    "ddm%":       [4, 100, "Demand data miss percentage"],
85    "dmhit":      [5, 1000, "Demand metadata hits per second"],
86    "dmioh":      [5, 1000, "Demand metadata I/O hits per second"],
87    "dmmis":      [5, 1000, "Demand metadata misses per second"],
88    "dmh%":       [4, 100, "Demand metadata hit percentage"],
89    "dmi%":       [4, 100, "Demand metadata I/O hit percentage"],
90    "dmm%":       [4, 100, "Demand metadata miss percentage"],
91    "phit":       [4, 1000, "Prefetch hits per second"],
92    "pioh":       [4, 1000, "Prefetch I/O hits per second"],
93    "pmis":       [4, 1000, "Prefetch misses per second"],
94    "ph%":        [3, 100, "Prefetch hits percentage"],
95    "pi%":        [3, 100, "Prefetch I/O hits percentage"],
96    "pm%":        [3, 100, "Prefetch miss percentage"],
97    "pdhit":      [5, 1000, "Prefetch data hits per second"],
98    "pdioh":      [5, 1000, "Prefetch data I/O hits per second"],
99    "pdmis":      [5, 1000, "Prefetch data misses per second"],
100    "pdh%":       [4, 100, "Prefetch data hits percentage"],
101    "pdi%":       [4, 100, "Prefetch data I/O hits percentage"],
102    "pdm%":       [4, 100, "Prefetch data miss percentage"],
103    "pmhit":      [5, 1000, "Prefetch metadata hits per second"],
104    "pmioh":      [5, 1000, "Prefetch metadata I/O hits per second"],
105    "pmmis":      [5, 1000, "Prefetch metadata misses per second"],
106    "pmh%":       [4, 100, "Prefetch metadata hits percentage"],
107    "pmi%":       [4, 100, "Prefetch metadata I/O hits percentage"],
108    "pmm%":       [4, 100, "Prefetch metadata miss percentage"],
109    "mhit":       [4, 1000, "Metadata hits per second"],
110    "mioh":       [4, 1000, "Metadata I/O hits per second"],
111    "mmis":       [4, 1000, "Metadata misses per second"],
112    "mread":      [5, 1000, "Metadata accesses per second"],
113    "mh%":        [3, 100, "Metadata hit percentage"],
114    "mi%":        [3, 100, "Metadata I/O hit percentage"],
115    "mm%":        [3, 100, "Metadata miss percentage"],
116    "arcsz":      [5, 1024, "ARC size"],
117    "size":       [5, 1024, "ARC size"],
118    "c":          [5, 1024, "ARC target size"],
119    "mfu":        [4, 1000, "MFU list hits per second"],
120    "mru":        [4, 1000, "MRU list hits per second"],
121    "mfug":       [4, 1000, "MFU ghost list hits per second"],
122    "mrug":       [4, 1000, "MRU ghost list hits per second"],
123    "unc":        [4, 1000, "Uncached list hits per second"],
124    "eskip":      [5, 1000, "evict_skip per second"],
125    "el2skip":    [7, 1000, "evict skip, due to l2 writes, per second"],
126    "el2cach":    [7, 1024, "Size of L2 cached evictions per second"],
127    "el2el":      [5, 1024, "Size of L2 eligible evictions per second"],
128    "el2mfu":     [6, 1024, "Size of L2 eligible MFU evictions per second"],
129    "el2mru":     [6, 1024, "Size of L2 eligible MRU evictions per second"],
130    "el2inel":    [7, 1024, "Size of L2 ineligible evictions per second"],
131    "mtxmis":     [6, 1000, "mutex_miss per second"],
132    "dread":      [5, 1000, "Demand accesses per second"],
133    "ddread":     [6, 1000, "Demand data accesses per second"],
134    "dmread":     [6, 1000, "Demand metadata accesses per second"],
135    "pread":      [5, 1000, "Prefetch accesses per second"],
136    "pdread":     [6, 1000, "Prefetch data accesses per second"],
137    "pmread":     [6, 1000, "Prefetch metadata accesses per second"],
138    "l2hits":     [6, 1000, "L2ARC hits per second"],
139    "l2miss":     [6, 1000, "L2ARC misses per second"],
140    "l2read":     [6, 1000, "Total L2ARC accesses per second"],
141    "l2hit%":     [6, 100, "L2ARC access hit percentage"],
142    "l2miss%":    [7, 100, "L2ARC access miss percentage"],
143    "l2pref":     [6, 1024, "L2ARC prefetch allocated size"],
144    "l2mfu":      [5, 1024, "L2ARC MFU allocated size"],
145    "l2mru":      [5, 1024, "L2ARC MRU allocated size"],
146    "l2data":     [6, 1024, "L2ARC data allocated size"],
147    "l2meta":     [6, 1024, "L2ARC metadata allocated size"],
148    "l2pref%":    [7, 100, "L2ARC prefetch percentage"],
149    "l2mfu%":     [6, 100, "L2ARC MFU percentage"],
150    "l2mru%":     [6, 100, "L2ARC MRU percentage"],
151    "l2data%":    [7, 100, "L2ARC data percentage"],
152    "l2meta%":    [7, 100, "L2ARC metadata percentage"],
153    "l2asize":    [7, 1024, "Actual (compressed) size of the L2ARC"],
154    "l2size":     [6, 1024, "Size of the L2ARC"],
155    "l2bytes":    [7, 1024, "Bytes read per second from the L2ARC"],
156    "l2wbytes":   [8, 1024, "Bytes written per second to the L2ARC"],
157    "grow":       [4, 1000, "ARC grow disabled"],
158    "need":       [5, 1024, "ARC reclaim need"],
159    "free":       [5, 1024, "ARC free memory"],
160    "avail":      [5, 1024, "ARC available memory"],
161    "waste":      [5, 1024, "Wasted memory due to round up to pagesize"],
162    "ztotal":     [6, 1000, "zfetch total prefetcher calls per second"],
163    "zhits":      [5, 1000, "zfetch stream hits per second"],
164    "zahead":     [6, 1000, "zfetch hits ahead of streams per second"],
165    "zpast":      [5, 1000, "zfetch hits behind streams per second"],
166    "zmisses":    [7, 1000, "zfetch stream misses per second"],
167    "zmax":       [4, 1000, "zfetch limit reached per second"],
168    "zfuture":    [7, 1000, "zfetch stream future per second"],
169    "zstride":    [7, 1000, "zfetch stream strides per second"],
170    "zissued":    [7, 1000, "zfetch prefetches issued per second"],
171    "zactive":    [7, 1000, "zfetch prefetches active per second"],
172}
173
174# ARC structural breakdown from arc_summary
175structfields = {
176    "cmp":      ["compressed", "Compressed"],
177    "ovh":      ["overhead", "Overhead"],
178    "bon":      ["bonus", "Bonus"],
179    "dno":      ["dnode", "Dnode"],
180    "dbu":      ["dbuf", "Dbuf"],
181    "hdr":      ["hdr", "Header"],
182    "l2h":      ["l2_hdr", "L2 header"],
183    "abd":      ["abd_chunk_waste", "ABD chunk waste"],
184}
185structstats = {                             # size stats
186    "percent":  "size",                     # percentage of this value
187    "sz":       ["_size", "size"],
188}
189
190# ARC types breakdown from arc_summary
191typefields = {
192    "data":     ["data", "ARC data"],
193    "meta":     ["metadata", "ARC metadata"],
194}
195typestats = {                               # size stats
196    "percent":  "cachessz",                 # percentage of this value
197    "tg":       ["_target", "target"],
198    "sz":       ["_size", "size"],
199}
200
201# ARC states breakdown from arc_summary
202statefields = {
203    "ano":      ["anon", "Anonymous"],
204    "mfu":      ["mfu", "MFU"],
205    "mru":      ["mru", "MRU"],
206    "unc":      ["uncached", "Uncached"],
207}
208targetstats = {
209    "percent":  "cachessz",                 # percentage of this value
210    "fields":   ["mfu", "mru"],             # only applicable to these fields
211    "tg":       ["_target", "target"],
212    "dt":       ["_data_target", "data target"],
213    "mt":       ["_metadata_target", "metadata target"],
214}
215statestats = {                              # size stats
216    "percent":  "cachessz",                 # percentage of this value
217    "sz":       ["_size", "size"],
218    "da":       ["_data", "data size"],
219    "me":       ["_metadata", "metadata size"],
220    "ed":       ["_evictable_data", "evictable data size"],
221    "em":       ["_evictable_metadata", "evictable metadata size"],
222}
223ghoststats = {
224    "fields":   ["mfu", "mru"],             # only applicable to these fields
225    "gsz":      ["_ghost_size", "ghost size"],
226    "gd":       ["_ghost_data", "ghost data size"],
227    "gm":       ["_ghost_metadata", "ghost metadata size"],
228}
229
230# fields and stats
231fieldstats = [
232    [structfields, structstats],
233    [typefields, typestats],
234    [statefields, targetstats, statestats, ghoststats],
235]
236for fs in fieldstats:
237    fields, stats = fs[0], fs[1:]
238    for field, fieldval in fields.items():
239        for group in stats:
240            for stat, statval in group.items():
241                if stat in ["fields", "percent"] or \
242                    ("fields" in group and field not in group["fields"]):
243                    continue
244                colname = field + stat
245                coldesc = fieldval[1] + " " + statval[1]
246                cols[colname] = [len(colname), 1024, coldesc]
247                if "percent" in group:
248                    cols[colname + "%"] = [len(colname) + 1, 100, \
249                        coldesc + " percentage"]
250
251v = {}
252hdr = ["time", "read", "ddread", "ddh%", "dmread", "dmh%", "pread", "ph%",
253       "size", "c", "avail"]
254xhdr = ["time", "mfu", "mru", "mfug", "mrug", "unc", "eskip", "mtxmis",
255        "dread", "pread", "read"]
256zhdr = ["time", "ztotal", "zhits", "zahead", "zpast", "zmisses", "zmax",
257        "zfuture", "zstride", "zissued", "zactive"]
258sint = 1               # Default interval is 1 second
259count = 1              # Default count is 1
260hdr_intr = 20          # Print header every 20 lines of output
261opfile = None
262sep = "  "              # Default separator is 2 spaces
263l2exist = False
264cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
265       "[count]]\n")
266cur = {}
267d = {}
268out = None
269kstat = None
270pretty_print = True
271
272
273if sys.platform.startswith('freebsd'):
274    # Requires py-sysctl on FreeBSD
275    import sysctl
276
277    def kstat_update():
278        global kstat
279
280        k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
281             if ctl.type != sysctl.CTLTYPE_NODE]
282        k += [ctl for ctl in sysctl.filter('kstat.zfs.misc.zfetchstats')
283             if ctl.type != sysctl.CTLTYPE_NODE]
284
285        if not k:
286            sys.exit(1)
287
288        kstat = {}
289
290        for s in k:
291            if not s:
292                continue
293
294            name, value = s.name, s.value
295
296            if "arcstats" in name:
297                # Trims 'kstat.zfs.misc.arcstats' from the name
298                kstat[name[24:]] = int(value)
299            else:
300                kstat["zfetch_" + name[27:]] = int(value)
301
302elif sys.platform.startswith('linux'):
303    def kstat_update():
304        global kstat
305
306        k1 = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
307
308        k2 = ["zfetch_" + line.strip() for line in
309             open('/proc/spl/kstat/zfs/zfetchstats')]
310
311        if k1 is None or k2 is None:
312            sys.exit(1)
313
314        del k1[0:2]
315        del k2[0:2]
316        k = k1 + k2
317        kstat = {}
318
319        for s in k:
320            if not s:
321                continue
322
323            name, unused, value = s.split()
324            kstat[name] = int(value)
325
326
327def detailed_usage():
328    sys.stderr.write("%s\n" % cmd)
329    sys.stderr.write("Field definitions are as follows:\n")
330    for key in cols:
331        sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
332    sys.stderr.write("\n")
333
334    sys.exit(0)
335
336
337def usage():
338    sys.stderr.write("%s\n" % cmd)
339    sys.stderr.write("\t -h : Print this help message\n")
340    sys.stderr.write("\t -a : Print all possible stats\n")
341    sys.stderr.write("\t -v : List all possible field headers and definitions"
342                     "\n")
343    sys.stderr.write("\t -x : Print extended stats\n")
344    sys.stderr.write("\t -z : Print zfetch stats\n")
345    sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
346    sys.stderr.write("\t -o : Redirect output to the specified file\n")
347    sys.stderr.write("\t -s : Override default field separator with custom "
348                     "character or string\n")
349    sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
350    sys.stderr.write("\nExamples:\n")
351    sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
352    sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
353    sys.stderr.write("\tarcstat -v\n")
354    sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
355    sys.stderr.write("\n")
356
357    sys.exit(1)
358
359
360def snap_stats():
361    global cur
362    global kstat
363
364    prev = copy.deepcopy(cur)
365    kstat_update()
366
367    cur = kstat
368
369    # fill in additional values from arc_summary
370    cur["caches_size"] = caches_size = cur["anon_data"]+cur["anon_metadata"]+\
371        cur["mfu_data"]+cur["mfu_metadata"]+cur["mru_data"]+cur["mru_metadata"]+\
372        cur["uncached_data"]+cur["uncached_metadata"]
373    s = 4294967296
374    pd = cur["pd"]
375    pm = cur["pm"]
376    meta = cur["meta"]
377    v = (s-int(pd))*(s-int(meta))/s
378    cur["mfu_data_target"] = v / 65536 * caches_size / 65536
379    v = (s-int(pm))*int(meta)/s
380    cur["mfu_metadata_target"] = v / 65536 * caches_size / 65536
381    v = int(pd)*(s-int(meta))/s
382    cur["mru_data_target"] = v / 65536 * caches_size / 65536
383    v = int(pm)*int(meta)/s
384    cur["mru_metadata_target"] = v / 65536 * caches_size / 65536
385
386    cur["data_target"] = cur["mfu_data_target"] + cur["mru_data_target"]
387    cur["metadata_target"] = cur["mfu_metadata_target"] + cur["mru_metadata_target"]
388    cur["mfu_target"] = cur["mfu_data_target"] + cur["mfu_metadata_target"]
389    cur["mru_target"] = cur["mru_data_target"] + cur["mru_metadata_target"]
390
391    for key in cur:
392        if re.match(key, "class"):
393            continue
394        if key in prev:
395            d[key] = cur[key] - prev[key]
396        else:
397            d[key] = cur[key]
398
399
400def isint(num):
401    if isinstance(num, float):
402        return num.is_integer()
403    if isinstance(num, int):
404        return True
405    return False
406
407
408def prettynum(sz, scale, num=0):
409    suffix = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
410    index = 0
411
412    # Special case for date field
413    if scale == -1:
414        return "%s" % num
415
416    if scale != 100:
417        while abs(num) > scale and index < 5:
418            num = num / scale
419            index += 1
420
421    width = sz - (0 if index == 0 else 1)
422    intlen = len("%.0f" % num)              # %.0f rounds to nearest int
423    if sint == 1 and isint(num) or width < intlen + 2:
424        decimal = 0
425    else:
426        decimal = 1
427    return "%*.*f%s" % (width, decimal, num, suffix[index])
428
429
430def print_values():
431    global hdr
432    global sep
433    global v
434    global pretty_print
435
436    if pretty_print:
437        fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
438    else:
439        fmt = lambda col: str(v[col])
440
441    sys.stdout.write(sep.join(fmt(col) for col in hdr))
442    sys.stdout.write("\n")
443    sys.stdout.flush()
444
445
446def print_header():
447    global hdr
448    global sep
449    global pretty_print
450
451    if pretty_print:
452        fmt = lambda col: "%*s" % (cols[col][0], col)
453    else:
454        fmt = lambda col: col
455
456    sys.stdout.write(sep.join(fmt(col) for col in hdr))
457    sys.stdout.write("\n")
458
459
460def get_terminal_lines():
461    try:
462        import fcntl
463        import termios
464        import struct
465        data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
466        sz = struct.unpack('hh', data)
467        return sz[0]
468    except Exception:
469        pass
470
471
472def update_hdr_intr():
473    global hdr_intr
474
475    lines = get_terminal_lines()
476    if lines and lines > 3:
477        hdr_intr = lines - 3
478
479
480def resize_handler(signum, frame):
481    update_hdr_intr()
482
483
484def init():
485    global sint
486    global count
487    global hdr
488    global xhdr
489    global zhdr
490    global opfile
491    global sep
492    global out
493    global l2exist
494    global pretty_print
495
496    desired_cols = None
497    aflag = False
498    xflag = False
499    hflag = False
500    vflag = False
501    zflag = False
502    i = 1
503
504    try:
505        opts, args = getopt.getopt(
506            sys.argv[1:],
507            "axzo:hvs:f:p",
508            [
509                "all",
510                "extended",
511                "zfetch",
512                "outfile",
513                "help",
514                "verbose",
515                "separator",
516                "columns",
517                "parsable"
518            ]
519        )
520    except getopt.error as msg:
521        sys.stderr.write("Error: %s\n" % str(msg))
522        usage()
523        opts = None
524
525    for opt, arg in opts:
526        if opt in ('-a', '--all'):
527            aflag = True
528        if opt in ('-x', '--extended'):
529            xflag = True
530        if opt in ('-o', '--outfile'):
531            opfile = arg
532            i += 1
533        if opt in ('-h', '--help'):
534            hflag = True
535        if opt in ('-v', '--verbose'):
536            vflag = True
537        if opt in ('-s', '--separator'):
538            sep = arg
539            i += 1
540        if opt in ('-f', '--columns'):
541            desired_cols = arg
542            i += 1
543        if opt in ('-p', '--parsable'):
544            pretty_print = False
545        if opt in ('-z', '--zfetch'):
546            zflag = True
547        i += 1
548
549    argv = sys.argv[i:]
550    sint = int(argv[0]) if argv else sint
551    count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)
552
553    if hflag or (xflag and zflag) or ((zflag or xflag) and desired_cols):
554        usage()
555
556    if vflag:
557        detailed_usage()
558
559    if xflag:
560        hdr = xhdr
561
562    if zflag:
563        hdr = zhdr
564
565    update_hdr_intr()
566
567    # check if L2ARC exists
568    snap_stats()
569    l2_size = cur.get("l2_size")
570    if l2_size:
571        l2exist = True
572
573    if desired_cols:
574        hdr = desired_cols.split(",")
575
576        invalid = []
577        incompat = []
578        for ele in hdr:
579            if ele not in cols:
580                invalid.append(ele)
581            elif not l2exist and ele.startswith("l2"):
582                sys.stdout.write("No L2ARC Here\n%s\n" % ele)
583                incompat.append(ele)
584
585        if len(invalid) > 0:
586            sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
587            usage()
588
589        if len(incompat) > 0:
590            sys.stderr.write("Incompatible field specified! -- %s\n" %
591                             incompat)
592            usage()
593
594    if aflag:
595        if l2exist:
596            hdr = cols.keys()
597        else:
598            hdr = [col for col in cols.keys() if not col.startswith("l2")]
599
600    if opfile:
601        try:
602            out = open(opfile, "w")
603            sys.stdout = out
604
605        except IOError:
606            sys.stderr.write("Cannot open %s for writing\n" % opfile)
607            sys.exit(1)
608
609
610def calculate():
611    global d
612    global v
613    global l2exist
614
615    v = dict()
616    v["time"] = time.strftime("%H:%M:%S", time.localtime())
617    v["hits"] = d["hits"] / sint
618    v["iohs"] = d["iohits"] / sint
619    v["miss"] = d["misses"] / sint
620    v["read"] = v["hits"] + v["iohs"] + v["miss"]
621    v["hit%"] = 100 * v["hits"] / v["read"] if v["read"] > 0 else 0
622    v["ioh%"] = 100 * v["iohs"] / v["read"] if v["read"] > 0 else 0
623    v["miss%"] = 100 - v["hit%"] - v["ioh%"] if v["read"] > 0 else 0
624
625    v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) / sint
626    v["dioh"] = (d["demand_data_iohits"] + d["demand_metadata_iohits"]) / sint
627    v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) / sint
628
629    v["dread"] = v["dhit"] + v["dioh"] + v["dmis"]
630    v["dh%"] = 100 * v["dhit"] / v["dread"] if v["dread"] > 0 else 0
631    v["di%"] = 100 * v["dioh"] / v["dread"] if v["dread"] > 0 else 0
632    v["dm%"] = 100 - v["dh%"] - v["di%"] if v["dread"] > 0 else 0
633
634    v["ddhit"] = d["demand_data_hits"] / sint
635    v["ddioh"] = d["demand_data_iohits"] / sint
636    v["ddmis"] = d["demand_data_misses"] / sint
637
638    v["ddread"] = v["ddhit"] + v["ddioh"] + v["ddmis"]
639    v["ddh%"] = 100 * v["ddhit"] / v["ddread"] if v["ddread"] > 0 else 0
640    v["ddi%"] = 100 * v["ddioh"] / v["ddread"] if v["ddread"] > 0 else 0
641    v["ddm%"] = 100 - v["ddh%"] - v["ddi%"] if v["ddread"] > 0 else 0
642
643    v["dmhit"] = d["demand_metadata_hits"] / sint
644    v["dmioh"] = d["demand_metadata_iohits"] / sint
645    v["dmmis"] = d["demand_metadata_misses"] / sint
646
647    v["dmread"] = v["dmhit"] + v["dmioh"] + v["dmmis"]
648    v["dmh%"] = 100 * v["dmhit"] / v["dmread"] if v["dmread"] > 0 else 0
649    v["dmi%"] = 100 * v["dmioh"] / v["dmread"] if v["dmread"] > 0 else 0
650    v["dmm%"] = 100 - v["dmh%"] - v["dmi%"] if v["dmread"] > 0 else 0
651
652    v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) / sint
653    v["pioh"] = (d["prefetch_data_iohits"] +
654                 d["prefetch_metadata_iohits"]) / sint
655    v["pmis"] = (d["prefetch_data_misses"] +
656                 d["prefetch_metadata_misses"]) / sint
657
658    v["pread"] = v["phit"] + v["pioh"] + v["pmis"]
659    v["ph%"] = 100 * v["phit"] / v["pread"] if v["pread"] > 0 else 0
660    v["pi%"] = 100 * v["pioh"] / v["pread"] if v["pread"] > 0 else 0
661    v["pm%"] = 100 - v["ph%"] - v["pi%"] if v["pread"] > 0 else 0
662
663    v["pdhit"] = d["prefetch_data_hits"] / sint
664    v["pdioh"] = d["prefetch_data_iohits"] / sint
665    v["pdmis"] = d["prefetch_data_misses"] / sint
666
667    v["pdread"] = v["pdhit"] + v["pdioh"] + v["pdmis"]
668    v["pdh%"] = 100 * v["pdhit"] / v["pdread"] if v["pdread"] > 0 else 0
669    v["pdi%"] = 100 * v["pdioh"] / v["pdread"] if v["pdread"] > 0 else 0
670    v["pdm%"] = 100 - v["pdh%"] - v["pdi%"] if v["pdread"] > 0 else 0
671
672    v["pmhit"] = d["prefetch_metadata_hits"] / sint
673    v["pmioh"] = d["prefetch_metadata_iohits"] / sint
674    v["pmmis"] = d["prefetch_metadata_misses"] / sint
675
676    v["pmread"] = v["pmhit"] + v["pmioh"] + v["pmmis"]
677    v["pmh%"] = 100 * v["pmhit"] / v["pmread"] if v["pmread"] > 0 else 0
678    v["pmi%"] = 100 * v["pmioh"] / v["pmread"] if v["pmread"] > 0 else 0
679    v["pmm%"] = 100 - v["pmh%"] - v["pmi%"] if v["pmread"] > 0 else 0
680
681    v["mhit"] = (d["prefetch_metadata_hits"] +
682                 d["demand_metadata_hits"]) / sint
683    v["mioh"] = (d["prefetch_metadata_iohits"] +
684                 d["demand_metadata_iohits"]) / sint
685    v["mmis"] = (d["prefetch_metadata_misses"] +
686                 d["demand_metadata_misses"]) / sint
687
688    v["mread"] = v["mhit"] + v["mioh"] + v["mmis"]
689    v["mh%"] = 100 * v["mhit"] / v["mread"] if v["mread"] > 0 else 0
690    v["mi%"] = 100 * v["mioh"] / v["mread"] if v["mread"] > 0 else 0
691    v["mm%"] = 100 - v["mh%"] - v["mi%"] if v["mread"] > 0 else 0
692
693    v["arcsz"] = cur["size"]
694    v["size"] = cur["size"]
695    v["c"] = cur["c"]
696    v["mfu"] = d["mfu_hits"] / sint
697    v["mru"] = d["mru_hits"] / sint
698    v["mrug"] = d["mru_ghost_hits"] / sint
699    v["mfug"] = d["mfu_ghost_hits"] / sint
700    v["unc"] = d["uncached_hits"] / sint
701    v["eskip"] = d["evict_skip"] / sint
702    v["el2skip"] = d["evict_l2_skip"] / sint
703    v["el2cach"] = d["evict_l2_cached"] / sint
704    v["el2el"] = d["evict_l2_eligible"] / sint
705    v["el2mfu"] = d["evict_l2_eligible_mfu"] / sint
706    v["el2mru"] = d["evict_l2_eligible_mru"] / sint
707    v["el2inel"] = d["evict_l2_ineligible"] / sint
708    v["mtxmis"] = d["mutex_miss"] / sint
709    v["ztotal"] = (d["zfetch_hits"] + d["zfetch_future"] + d["zfetch_stride"] +
710                   d["zfetch_past"] + d["zfetch_misses"]) / sint
711    v["zhits"] = d["zfetch_hits"] / sint
712    v["zahead"] = (d["zfetch_future"] + d["zfetch_stride"]) / sint
713    v["zpast"] = d["zfetch_past"] / sint
714    v["zmisses"] = d["zfetch_misses"] / sint
715    v["zmax"] = d["zfetch_max_streams"] / sint
716    v["zfuture"] = d["zfetch_future"] / sint
717    v["zstride"] = d["zfetch_stride"] / sint
718    v["zissued"] = d["zfetch_io_issued"] / sint
719    v["zactive"] = d["zfetch_io_active"] / sint
720
721    # ARC structural breakdown, ARC types breakdown, ARC states breakdown
722    v["cachessz"] = cur["caches_size"]
723    for fs in fieldstats:
724        fields, stats = fs[0], fs[1:]
725        for field, fieldval in fields.items():
726            for group in stats:
727                for stat, statval in group.items():
728                    if stat in ["fields", "percent"] or \
729                        ("fields" in group and field not in group["fields"]):
730                        continue
731                    colname = field + stat
732                    v[colname] = cur[fieldval[0] + statval[0]]
733                    if "percent" in group:
734                        v[colname + "%"] = 100 * v[colname] / \
735                            v[group["percent"]] if v[group["percent"]] > 0 else 0
736
737    if l2exist:
738        v["l2hits"] = d["l2_hits"] / sint
739        v["l2miss"] = d["l2_misses"] / sint
740        v["l2read"] = v["l2hits"] + v["l2miss"]
741        v["l2hit%"] = 100 * v["l2hits"] / v["l2read"] if v["l2read"] > 0 else 0
742
743        v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
744        v["l2asize"] = cur["l2_asize"]
745        v["l2size"] = cur["l2_size"]
746        v["l2bytes"] = d["l2_read_bytes"] / sint
747        v["l2wbytes"] = d["l2_write_bytes"] / sint
748
749        v["l2pref"] = cur["l2_prefetch_asize"]
750        v["l2mfu"] = cur["l2_mfu_asize"]
751        v["l2mru"] = cur["l2_mru_asize"]
752        v["l2data"] = cur["l2_bufc_data_asize"]
753        v["l2meta"] = cur["l2_bufc_metadata_asize"]
754        v["l2pref%"] = 100 * v["l2pref"] / v["l2asize"]
755        v["l2mfu%"] = 100 * v["l2mfu"] / v["l2asize"]
756        v["l2mru%"] = 100 * v["l2mru"] / v["l2asize"]
757        v["l2data%"] = 100 * v["l2data"] / v["l2asize"]
758        v["l2meta%"] = 100 * v["l2meta"] / v["l2asize"]
759
760    v["grow"] = 0 if cur["arc_no_grow"] else 1
761    v["need"] = cur["arc_need_free"]
762    v["free"] = cur["memory_free_bytes"]
763    v["avail"] = cur["memory_available_bytes"]
764    v["waste"] = cur["abd_chunk_waste_size"]
765
766
767def main():
768    global sint
769    global count
770    global hdr_intr
771
772    i = 0
773    count_flag = 0
774
775    init()
776    if count > 0:
777        count_flag = 1
778
779    signal(SIGINT, SIG_DFL)
780    signal(SIGWINCH, resize_handler)
781    while True:
782        if i == 0:
783            print_header()
784
785        snap_stats()
786        calculate()
787        print_values()
788
789        if count_flag == 1:
790            if count <= 1:
791                break
792            count -= 1
793
794        i = 0 if i >= hdr_intr else i + 1
795        time.sleep(sint)
796
797    if out:
798        out.close()
799
800
801if __name__ == '__main__':
802    main()
803