xref: /freebsd/sys/contrib/openzfs/cmd/zarcsummary (revision dd32d6b29d49838c99d38ba30846ade210b2e6f7)
1*dd32d6b2SMartin Matuska#!/usr/bin/env python3
2*dd32d6b2SMartin Matuska# SPDX-License-Identifier: BSD-2-Clause
3*dd32d6b2SMartin Matuska#
4*dd32d6b2SMartin Matuska# Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>,
5*dd32d6b2SMartin Matuska# Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>,
6*dd32d6b2SMartin Matuska# Copyright (c) 2010-2011 Jason J. Hellenthal <jhell@DataIX.net>,
7*dd32d6b2SMartin Matuska# Copyright (c) 2017 Scot W. Stevenson <scot.stevenson@gmail.com>
8*dd32d6b2SMartin Matuska# All rights reserved.
9*dd32d6b2SMartin Matuska#
10*dd32d6b2SMartin Matuska# Redistribution and use in source and binary forms, with or without
11*dd32d6b2SMartin Matuska# modification, are permitted provided that the following conditions
12*dd32d6b2SMartin Matuska# are met:
13*dd32d6b2SMartin Matuska#
14*dd32d6b2SMartin Matuska# 1. Redistributions of source code must retain the above copyright
15*dd32d6b2SMartin Matuska#    notice, this list of conditions and the following disclaimer.
16*dd32d6b2SMartin Matuska# 2. Redistributions in binary form must reproduce the above copyright
17*dd32d6b2SMartin Matuska#    notice, this list of conditions and the following disclaimer in the
18*dd32d6b2SMartin Matuska#    documentation and/or other materials provided with the distribution.
19*dd32d6b2SMartin Matuska#
20*dd32d6b2SMartin Matuska# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21*dd32d6b2SMartin Matuska# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*dd32d6b2SMartin Matuska# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*dd32d6b2SMartin Matuska# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24*dd32d6b2SMartin Matuska# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25*dd32d6b2SMartin Matuska# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26*dd32d6b2SMartin Matuska# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*dd32d6b2SMartin Matuska# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28*dd32d6b2SMartin Matuska# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29*dd32d6b2SMartin Matuska# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*dd32d6b2SMartin Matuska# SUCH DAMAGE.
31*dd32d6b2SMartin Matuska"""Print statistics on the ZFS ARC Cache and other information
32*dd32d6b2SMartin Matuska
33*dd32d6b2SMartin MatuskaProvides basic information on the ARC, its efficiency, the L2ARC (if present),
34*dd32d6b2SMartin Matuskathe Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See
35*dd32d6b2SMartin Matuskathe in-source documentation and code at
36*dd32d6b2SMartin Matuskahttps://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
37*dd32d6b2SMartin MatuskaThe original introduction to zarcsummary can be found at
38*dd32d6b2SMartin Matuskahttp://cuddletech.com/?p=454
39*dd32d6b2SMartin Matuska"""
40*dd32d6b2SMartin Matuska
41*dd32d6b2SMartin Matuskaimport argparse
42*dd32d6b2SMartin Matuskaimport os
43*dd32d6b2SMartin Matuskaimport subprocess
44*dd32d6b2SMartin Matuskaimport sys
45*dd32d6b2SMartin Matuskaimport time
46*dd32d6b2SMartin Matuskaimport errno
47*dd32d6b2SMartin Matuska
48*dd32d6b2SMartin Matuska# We can't use env -S portably, and we need python3 -u to handle pipes in
49*dd32d6b2SMartin Matuska# the shell abruptly closing the way we want to, so...
50*dd32d6b2SMartin Matuskaimport io
51*dd32d6b2SMartin Matuskaif isinstance(sys.__stderr__.buffer, io.BufferedWriter):
52*dd32d6b2SMartin Matuska    os.execv(sys.executable, [sys.executable, "-u"] + sys.argv)
53*dd32d6b2SMartin Matuska
54*dd32d6b2SMartin MatuskaDESCRIPTION = 'Print ARC and other statistics for OpenZFS'
55*dd32d6b2SMartin MatuskaINDENT = ' '*8
56*dd32d6b2SMartin MatuskaLINE_LENGTH = 72
57*dd32d6b2SMartin MatuskaDATE_FORMAT = '%a %b %d %H:%M:%S %Y'
58*dd32d6b2SMartin MatuskaTITLE = 'ZFS Subsystem Report'
59*dd32d6b2SMartin Matuska
60*dd32d6b2SMartin MatuskaSECTIONS = 'arc archits dmu l2arc spl tunables vdev zil'.split()
61*dd32d6b2SMartin MatuskaSECTION_HELP = 'print info from one section ('+' '.join(SECTIONS)+')'
62*dd32d6b2SMartin Matuska
63*dd32d6b2SMartin Matuska# Tunables and SPL are handled separately because they come from
64*dd32d6b2SMartin Matuska# different sources
65*dd32d6b2SMartin MatuskaSECTION_PATHS = {'arc': 'arcstats',
66*dd32d6b2SMartin Matuska                 'dmu': 'dmu_tx',
67*dd32d6b2SMartin Matuska                 'l2arc': 'arcstats',  # L2ARC stuff lives in arcstats
68*dd32d6b2SMartin Matuska                 'zfetch': 'zfetchstats',
69*dd32d6b2SMartin Matuska                 'zil': 'zil'}
70*dd32d6b2SMartin Matuska
71*dd32d6b2SMartin Matuskaparser = argparse.ArgumentParser(description=DESCRIPTION)
72*dd32d6b2SMartin Matuskaparser.add_argument('-a', '--alternate', action='store_true', default=False,
73*dd32d6b2SMartin Matuska                    help='use alternate formatting for tunables and SPL',
74*dd32d6b2SMartin Matuska                    dest='alt')
75*dd32d6b2SMartin Matuskaparser.add_argument('-d', '--description', action='store_true', default=False,
76*dd32d6b2SMartin Matuska                    help='print descriptions with tunables and SPL',
77*dd32d6b2SMartin Matuska                    dest='desc')
78*dd32d6b2SMartin Matuskaparser.add_argument('-g', '--graph', action='store_true', default=False,
79*dd32d6b2SMartin Matuska                    help='print graph on ARC use and exit', dest='graph')
80*dd32d6b2SMartin Matuskaparser.add_argument('-p', '--page', type=int, dest='page',
81*dd32d6b2SMartin Matuska                    help='print page by number (DEPRECATED, use "-s")')
82*dd32d6b2SMartin Matuskaparser.add_argument('-r', '--raw', action='store_true', default=False,
83*dd32d6b2SMartin Matuska                    help='dump all available data with minimal formatting',
84*dd32d6b2SMartin Matuska                    dest='raw')
85*dd32d6b2SMartin Matuskaparser.add_argument('-s', '--section', dest='section', help=SECTION_HELP)
86*dd32d6b2SMartin MatuskaARGS = parser.parse_args()
87*dd32d6b2SMartin Matuska
88*dd32d6b2SMartin Matuska
89*dd32d6b2SMartin Matuskaif sys.platform.startswith('freebsd'):
90*dd32d6b2SMartin Matuska    # Requires py36-sysctl on FreeBSD
91*dd32d6b2SMartin Matuska    import sysctl
92*dd32d6b2SMartin Matuska
93*dd32d6b2SMartin Matuska    def is_value(ctl):
94*dd32d6b2SMartin Matuska        return ctl.type != sysctl.CTLTYPE_NODE
95*dd32d6b2SMartin Matuska
96*dd32d6b2SMartin Matuska    def namefmt(ctl, base='vfs.zfs.'):
97*dd32d6b2SMartin Matuska        # base is removed from the name
98*dd32d6b2SMartin Matuska        cut = len(base)
99*dd32d6b2SMartin Matuska        return ctl.name[cut:]
100*dd32d6b2SMartin Matuska
101*dd32d6b2SMartin Matuska    def load_kstats(section):
102*dd32d6b2SMartin Matuska        base = 'kstat.zfs.misc.{section}.'.format(section=section)
103*dd32d6b2SMartin Matuska        fmt = lambda kstat: '{name} : {value}'.format(name=namefmt(kstat, base),
104*dd32d6b2SMartin Matuska                                                      value=kstat.value)
105*dd32d6b2SMartin Matuska        kstats = sysctl.filter(base)
106*dd32d6b2SMartin Matuska        return [fmt(kstat) for kstat in kstats if is_value(kstat)]
107*dd32d6b2SMartin Matuska
108*dd32d6b2SMartin Matuska    def get_params(base):
109*dd32d6b2SMartin Matuska        ctls = sysctl.filter(base)
110*dd32d6b2SMartin Matuska        return {namefmt(ctl): str(ctl.value) for ctl in ctls if is_value(ctl)}
111*dd32d6b2SMartin Matuska
112*dd32d6b2SMartin Matuska    def get_tunable_params():
113*dd32d6b2SMartin Matuska        return get_params('vfs.zfs')
114*dd32d6b2SMartin Matuska
115*dd32d6b2SMartin Matuska    def get_vdev_params():
116*dd32d6b2SMartin Matuska        return get_params('vfs.zfs.vdev')
117*dd32d6b2SMartin Matuska
118*dd32d6b2SMartin Matuska    def get_version_impl(request):
119*dd32d6b2SMartin Matuska        # FreeBSD reports versions for zpl and spa instead of zfs and spl.
120*dd32d6b2SMartin Matuska        name = {'zfs': 'zpl',
121*dd32d6b2SMartin Matuska                'spl': 'spa'}[request]
122*dd32d6b2SMartin Matuska        mib = 'vfs.zfs.version.{}'.format(name)
123*dd32d6b2SMartin Matuska        version = sysctl.filter(mib)[0].value
124*dd32d6b2SMartin Matuska        return '{} version {}'.format(name, version)
125*dd32d6b2SMartin Matuska
126*dd32d6b2SMartin Matuska    def get_descriptions(_request):
127*dd32d6b2SMartin Matuska        ctls = sysctl.filter('vfs.zfs')
128*dd32d6b2SMartin Matuska        return {namefmt(ctl): ctl.description for ctl in ctls if is_value(ctl)}
129*dd32d6b2SMartin Matuska
130*dd32d6b2SMartin Matuska
131*dd32d6b2SMartin Matuskaelif sys.platform.startswith('linux'):
132*dd32d6b2SMartin Matuska    KSTAT_PATH = '/proc/spl/kstat/zfs'
133*dd32d6b2SMartin Matuska    SPL_PATH = '/sys/module/spl/parameters'
134*dd32d6b2SMartin Matuska    TUNABLES_PATH = '/sys/module/zfs/parameters'
135*dd32d6b2SMartin Matuska
136*dd32d6b2SMartin Matuska    def load_kstats(section):
137*dd32d6b2SMartin Matuska        path = os.path.join(KSTAT_PATH, section)
138*dd32d6b2SMartin Matuska        with open(path) as f:
139*dd32d6b2SMartin Matuska            return list(f)[2:] # Get rid of header
140*dd32d6b2SMartin Matuska
141*dd32d6b2SMartin Matuska    def get_params(basepath):
142*dd32d6b2SMartin Matuska        """Collect information on the Solaris Porting Layer (SPL) or the
143*dd32d6b2SMartin Matuska        tunables, depending on the PATH given. Does not check if PATH is
144*dd32d6b2SMartin Matuska        legal.
145*dd32d6b2SMartin Matuska        """
146*dd32d6b2SMartin Matuska        result = {}
147*dd32d6b2SMartin Matuska        for name in os.listdir(basepath):
148*dd32d6b2SMartin Matuska            path = os.path.join(basepath, name)
149*dd32d6b2SMartin Matuska            with open(path) as f:
150*dd32d6b2SMartin Matuska                value = f.read()
151*dd32d6b2SMartin Matuska                result[name] = value.strip()
152*dd32d6b2SMartin Matuska        return result
153*dd32d6b2SMartin Matuska
154*dd32d6b2SMartin Matuska    def get_spl_params():
155*dd32d6b2SMartin Matuska        return get_params(SPL_PATH)
156*dd32d6b2SMartin Matuska
157*dd32d6b2SMartin Matuska    def get_tunable_params():
158*dd32d6b2SMartin Matuska        return get_params(TUNABLES_PATH)
159*dd32d6b2SMartin Matuska
160*dd32d6b2SMartin Matuska    def get_vdev_params():
161*dd32d6b2SMartin Matuska        return get_params(TUNABLES_PATH)
162*dd32d6b2SMartin Matuska
163*dd32d6b2SMartin Matuska    def get_version_impl(request):
164*dd32d6b2SMartin Matuska        # The original zarcsummary called /sbin/modinfo/{spl,zfs} to get
165*dd32d6b2SMartin Matuska        # the version information. We switch to /sys/module/{spl,zfs}/version
166*dd32d6b2SMartin Matuska        # to make sure we get what is really loaded in the kernel
167*dd32d6b2SMartin Matuska        try:
168*dd32d6b2SMartin Matuska            with open("/sys/module/{}/version".format(request)) as f:
169*dd32d6b2SMartin Matuska                return f.read().strip()
170*dd32d6b2SMartin Matuska        except:
171*dd32d6b2SMartin Matuska            return "(unknown)"
172*dd32d6b2SMartin Matuska
173*dd32d6b2SMartin Matuska    def get_descriptions(request):
174*dd32d6b2SMartin Matuska        """Get the descriptions of the Solaris Porting Layer (SPL) or the
175*dd32d6b2SMartin Matuska        tunables, return with minimal formatting.
176*dd32d6b2SMartin Matuska        """
177*dd32d6b2SMartin Matuska
178*dd32d6b2SMartin Matuska        if request not in ('spl', 'zfs'):
179*dd32d6b2SMartin Matuska            print('ERROR: description of "{0}" requested)'.format(request))
180*dd32d6b2SMartin Matuska            sys.exit(1)
181*dd32d6b2SMartin Matuska
182*dd32d6b2SMartin Matuska        descs = {}
183*dd32d6b2SMartin Matuska        target_prefix = 'parm:'
184*dd32d6b2SMartin Matuska
185*dd32d6b2SMartin Matuska        # We would prefer to do this with /sys/modules -- see the discussion at
186*dd32d6b2SMartin Matuska        # get_version() -- but there isn't a way to get the descriptions from
187*dd32d6b2SMartin Matuska        # there, so we fall back on modinfo
188*dd32d6b2SMartin Matuska        command = ["/sbin/modinfo", request, "-0"]
189*dd32d6b2SMartin Matuska
190*dd32d6b2SMartin Matuska        info = ''
191*dd32d6b2SMartin Matuska
192*dd32d6b2SMartin Matuska        try:
193*dd32d6b2SMartin Matuska
194*dd32d6b2SMartin Matuska            info = subprocess.run(command, stdout=subprocess.PIPE,
195*dd32d6b2SMartin Matuska                                  check=True, universal_newlines=True)
196*dd32d6b2SMartin Matuska            raw_output = info.stdout.split('\0')
197*dd32d6b2SMartin Matuska
198*dd32d6b2SMartin Matuska        except subprocess.CalledProcessError:
199*dd32d6b2SMartin Matuska            print("Error: Descriptions not available",
200*dd32d6b2SMartin Matuska                  "(can't access kernel module)")
201*dd32d6b2SMartin Matuska            sys.exit(1)
202*dd32d6b2SMartin Matuska
203*dd32d6b2SMartin Matuska        for line in raw_output:
204*dd32d6b2SMartin Matuska
205*dd32d6b2SMartin Matuska            if not line.startswith(target_prefix):
206*dd32d6b2SMartin Matuska                continue
207*dd32d6b2SMartin Matuska
208*dd32d6b2SMartin Matuska            line = line[len(target_prefix):].strip()
209*dd32d6b2SMartin Matuska            name, raw_desc = line.split(':', 1)
210*dd32d6b2SMartin Matuska            desc = raw_desc.rsplit('(', 1)[0]
211*dd32d6b2SMartin Matuska
212*dd32d6b2SMartin Matuska            if desc == '':
213*dd32d6b2SMartin Matuska                desc = '(No description found)'
214*dd32d6b2SMartin Matuska
215*dd32d6b2SMartin Matuska            descs[name.strip()] = desc.strip()
216*dd32d6b2SMartin Matuska
217*dd32d6b2SMartin Matuska        return descs
218*dd32d6b2SMartin Matuska
219*dd32d6b2SMartin Matuskadef handle_unraisableException(exc_type, exc_value=None, exc_traceback=None,
220*dd32d6b2SMartin Matuska                               err_msg=None, object=None):
221*dd32d6b2SMartin Matuska   handle_Exception(exc_type, object, exc_traceback)
222*dd32d6b2SMartin Matuska
223*dd32d6b2SMartin Matuskadef handle_Exception(ex_cls, ex, tb):
224*dd32d6b2SMartin Matuska    if ex_cls is KeyboardInterrupt:
225*dd32d6b2SMartin Matuska        sys.exit()
226*dd32d6b2SMartin Matuska
227*dd32d6b2SMartin Matuska    if ex_cls is BrokenPipeError:
228*dd32d6b2SMartin Matuska        # It turns out that while sys.exit() triggers an exception
229*dd32d6b2SMartin Matuska        # not handled message on Python 3.8+, os._exit() does not.
230*dd32d6b2SMartin Matuska        os._exit(0)
231*dd32d6b2SMartin Matuska
232*dd32d6b2SMartin Matuska    if ex_cls is OSError:
233*dd32d6b2SMartin Matuska      if ex.errno == errno.ENOTCONN:
234*dd32d6b2SMartin Matuska        sys.exit()
235*dd32d6b2SMartin Matuska
236*dd32d6b2SMartin Matuska    raise ex
237*dd32d6b2SMartin Matuska
238*dd32d6b2SMartin Matuskaif hasattr(sys,'unraisablehook'): # Python 3.8+
239*dd32d6b2SMartin Matuska    sys.unraisablehook = handle_unraisableException
240*dd32d6b2SMartin Matuskasys.excepthook = handle_Exception
241*dd32d6b2SMartin Matuska
242*dd32d6b2SMartin Matuska
243*dd32d6b2SMartin Matuskadef cleanup_line(single_line):
244*dd32d6b2SMartin Matuska    """Format a raw line of data from /proc and isolate the name value
245*dd32d6b2SMartin Matuska    part, returning a tuple with each. Currently, this gets rid of the
246*dd32d6b2SMartin Matuska    middle '4'. For example "arc_no_grow    4    0" returns the tuple
247*dd32d6b2SMartin Matuska    ("arc_no_grow", "0").
248*dd32d6b2SMartin Matuska    """
249*dd32d6b2SMartin Matuska    name, _, value = single_line.split()
250*dd32d6b2SMartin Matuska
251*dd32d6b2SMartin Matuska    return name, value
252*dd32d6b2SMartin Matuska
253*dd32d6b2SMartin Matuska
254*dd32d6b2SMartin Matuskadef draw_graph(kstats_dict):
255*dd32d6b2SMartin Matuska    """Draw a primitive graph representing the basic information on the
256*dd32d6b2SMartin Matuska    ARC -- its size and the proportion used by MFU and MRU -- and quit.
257*dd32d6b2SMartin Matuska    We use max size of the ARC to calculate how full it is. This is a
258*dd32d6b2SMartin Matuska    very rough representation.
259*dd32d6b2SMartin Matuska    """
260*dd32d6b2SMartin Matuska
261*dd32d6b2SMartin Matuska    arc_stats = isolate_section('arcstats', kstats_dict)
262*dd32d6b2SMartin Matuska
263*dd32d6b2SMartin Matuska    GRAPH_INDENT = ' '*4
264*dd32d6b2SMartin Matuska    GRAPH_WIDTH = 70
265*dd32d6b2SMartin Matuska    arc_max = int(arc_stats['c_max'])
266*dd32d6b2SMartin Matuska    arc_size = f_bytes(arc_stats['size'])
267*dd32d6b2SMartin Matuska    arc_perc = f_perc(arc_stats['size'], arc_max)
268*dd32d6b2SMartin Matuska    data_size = f_bytes(arc_stats['data_size'])
269*dd32d6b2SMartin Matuska    meta_size = f_bytes(arc_stats['metadata_size'])
270*dd32d6b2SMartin Matuska    dnode_size = f_bytes(arc_stats['dnode_size'])
271*dd32d6b2SMartin Matuska
272*dd32d6b2SMartin Matuska    info_form = ('ARC: {0} ({1}) Data: {2} Meta: {3} Dnode: {4}')
273*dd32d6b2SMartin Matuska    info_line = info_form.format(arc_size, arc_perc, data_size, meta_size,
274*dd32d6b2SMartin Matuska                                 dnode_size)
275*dd32d6b2SMartin Matuska    info_spc = ' '*int((GRAPH_WIDTH-len(info_line))/2)
276*dd32d6b2SMartin Matuska    info_line = GRAPH_INDENT+info_spc+info_line
277*dd32d6b2SMartin Matuska
278*dd32d6b2SMartin Matuska    graph_line = GRAPH_INDENT+'+'+('-'*(GRAPH_WIDTH-2))+'+'
279*dd32d6b2SMartin Matuska
280*dd32d6b2SMartin Matuska    arc_perc = float(int(arc_stats['size'])/arc_max)
281*dd32d6b2SMartin Matuska    data_perc = float(int(arc_stats['data_size'])/arc_max)
282*dd32d6b2SMartin Matuska    meta_perc = float(int(arc_stats['metadata_size'])/arc_max)
283*dd32d6b2SMartin Matuska    dnode_perc = float(int(arc_stats['dnode_size'])/arc_max)
284*dd32d6b2SMartin Matuska    total_ticks = float(arc_perc)*GRAPH_WIDTH
285*dd32d6b2SMartin Matuska    data_ticks = data_perc*GRAPH_WIDTH
286*dd32d6b2SMartin Matuska    meta_ticks = meta_perc*GRAPH_WIDTH
287*dd32d6b2SMartin Matuska    dnode_ticks = dnode_perc*GRAPH_WIDTH
288*dd32d6b2SMartin Matuska    other_ticks = total_ticks-(data_ticks+meta_ticks+dnode_ticks)
289*dd32d6b2SMartin Matuska
290*dd32d6b2SMartin Matuska    core_form = 'D'*int(data_ticks)+'M'*int(meta_ticks)+'N'*int(dnode_ticks)+\
291*dd32d6b2SMartin Matuska        'O'*int(other_ticks)
292*dd32d6b2SMartin Matuska    core_spc = ' '*(GRAPH_WIDTH-(2+len(core_form)))
293*dd32d6b2SMartin Matuska    core_line = GRAPH_INDENT+'|'+core_form+core_spc+'|'
294*dd32d6b2SMartin Matuska
295*dd32d6b2SMartin Matuska    for line in ('', info_line, graph_line, core_line, graph_line, ''):
296*dd32d6b2SMartin Matuska        print(line)
297*dd32d6b2SMartin Matuska
298*dd32d6b2SMartin Matuska
299*dd32d6b2SMartin Matuskadef f_bytes(byte_string):
300*dd32d6b2SMartin Matuska    """Return human-readable representation of a byte value in
301*dd32d6b2SMartin Matuska    powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
302*dd32d6b2SMartin Matuska    points. Values smaller than one KiB are returned without
303*dd32d6b2SMartin Matuska    decimal points. Note "bytes" is a reserved keyword.
304*dd32d6b2SMartin Matuska    """
305*dd32d6b2SMartin Matuska
306*dd32d6b2SMartin Matuska    prefixes = ([2**80, "YiB"],   # yobibytes (yotta)
307*dd32d6b2SMartin Matuska                [2**70, "ZiB"],   # zebibytes (zetta)
308*dd32d6b2SMartin Matuska                [2**60, "EiB"],   # exbibytes (exa)
309*dd32d6b2SMartin Matuska                [2**50, "PiB"],   # pebibytes (peta)
310*dd32d6b2SMartin Matuska                [2**40, "TiB"],   # tebibytes (tera)
311*dd32d6b2SMartin Matuska                [2**30, "GiB"],   # gibibytes (giga)
312*dd32d6b2SMartin Matuska                [2**20, "MiB"],   # mebibytes (mega)
313*dd32d6b2SMartin Matuska                [2**10, "KiB"])   # kibibytes (kilo)
314*dd32d6b2SMartin Matuska
315*dd32d6b2SMartin Matuska    bites = int(byte_string)
316*dd32d6b2SMartin Matuska
317*dd32d6b2SMartin Matuska    if bites >= 2**10:
318*dd32d6b2SMartin Matuska        for limit, unit in prefixes:
319*dd32d6b2SMartin Matuska
320*dd32d6b2SMartin Matuska            if bites >= limit:
321*dd32d6b2SMartin Matuska                value = bites / limit
322*dd32d6b2SMartin Matuska                break
323*dd32d6b2SMartin Matuska
324*dd32d6b2SMartin Matuska        result = '{0:.1f} {1}'.format(value, unit)
325*dd32d6b2SMartin Matuska    else:
326*dd32d6b2SMartin Matuska        result = '{0} Bytes'.format(bites)
327*dd32d6b2SMartin Matuska
328*dd32d6b2SMartin Matuska    return result
329*dd32d6b2SMartin Matuska
330*dd32d6b2SMartin Matuska
331*dd32d6b2SMartin Matuskadef f_hits(hits_string):
332*dd32d6b2SMartin Matuska    """Create a human-readable representation of the number of hits.
333*dd32d6b2SMartin Matuska    The single-letter symbols used are SI to avoid the confusion caused
334*dd32d6b2SMartin Matuska    by the different "short scale" and "long scale" representations in
335*dd32d6b2SMartin Matuska    English, which use the same words for different values. See
336*dd32d6b2SMartin Matuska    https://en.wikipedia.org/wiki/Names_of_large_numbers and:
337*dd32d6b2SMartin Matuska    https://physics.nist.gov/cuu/Units/prefixes.html
338*dd32d6b2SMartin Matuska    """
339*dd32d6b2SMartin Matuska
340*dd32d6b2SMartin Matuska    numbers = ([10**24, 'Y'],  # yotta (septillion)
341*dd32d6b2SMartin Matuska               [10**21, 'Z'],  # zetta (sextillion)
342*dd32d6b2SMartin Matuska               [10**18, 'E'],  # exa   (quintrillion)
343*dd32d6b2SMartin Matuska               [10**15, 'P'],  # peta  (quadrillion)
344*dd32d6b2SMartin Matuska               [10**12, 'T'],  # tera  (trillion)
345*dd32d6b2SMartin Matuska               [10**9, 'G'],   # giga  (billion)
346*dd32d6b2SMartin Matuska               [10**6, 'M'],   # mega  (million)
347*dd32d6b2SMartin Matuska               [10**3, 'k'])   # kilo  (thousand)
348*dd32d6b2SMartin Matuska
349*dd32d6b2SMartin Matuska    hits = int(hits_string)
350*dd32d6b2SMartin Matuska
351*dd32d6b2SMartin Matuska    if hits >= 1000:
352*dd32d6b2SMartin Matuska        for limit, symbol in numbers:
353*dd32d6b2SMartin Matuska
354*dd32d6b2SMartin Matuska            if hits >= limit:
355*dd32d6b2SMartin Matuska                value = hits/limit
356*dd32d6b2SMartin Matuska                break
357*dd32d6b2SMartin Matuska
358*dd32d6b2SMartin Matuska        result = "%0.1f%s" % (value, symbol)
359*dd32d6b2SMartin Matuska    else:
360*dd32d6b2SMartin Matuska        result = "%d" % hits
361*dd32d6b2SMartin Matuska
362*dd32d6b2SMartin Matuska    return result
363*dd32d6b2SMartin Matuska
364*dd32d6b2SMartin Matuska
365*dd32d6b2SMartin Matuskadef f_perc(value1, value2):
366*dd32d6b2SMartin Matuska    """Calculate percentage and return in human-readable form. If
367*dd32d6b2SMartin Matuska    rounding produces the result '0.0' though the first number is
368*dd32d6b2SMartin Matuska    not zero, include a 'less-than' symbol to avoid confusion.
369*dd32d6b2SMartin Matuska    Division by zero is handled by returning 'n/a'; no error
370*dd32d6b2SMartin Matuska    is called.
371*dd32d6b2SMartin Matuska    """
372*dd32d6b2SMartin Matuska
373*dd32d6b2SMartin Matuska    v1 = float(value1)
374*dd32d6b2SMartin Matuska    v2 = float(value2)
375*dd32d6b2SMartin Matuska
376*dd32d6b2SMartin Matuska    try:
377*dd32d6b2SMartin Matuska        perc = 100 * v1/v2
378*dd32d6b2SMartin Matuska    except ZeroDivisionError:
379*dd32d6b2SMartin Matuska        result = 'n/a'
380*dd32d6b2SMartin Matuska    else:
381*dd32d6b2SMartin Matuska        result = '{0:0.1f} %'.format(perc)
382*dd32d6b2SMartin Matuska
383*dd32d6b2SMartin Matuska    if result == '0.0 %' and v1 > 0:
384*dd32d6b2SMartin Matuska        result = '< 0.1 %'
385*dd32d6b2SMartin Matuska
386*dd32d6b2SMartin Matuska    return result
387*dd32d6b2SMartin Matuska
388*dd32d6b2SMartin Matuska
389*dd32d6b2SMartin Matuskadef format_raw_line(name, value):
390*dd32d6b2SMartin Matuska    """For the --raw option for the tunable and SPL outputs, decide on the
391*dd32d6b2SMartin Matuska    correct formatting based on the --alternate flag.
392*dd32d6b2SMartin Matuska    """
393*dd32d6b2SMartin Matuska
394*dd32d6b2SMartin Matuska    if ARGS.alt:
395*dd32d6b2SMartin Matuska        result = '{0}{1}={2}'.format(INDENT, name, value)
396*dd32d6b2SMartin Matuska    else:
397*dd32d6b2SMartin Matuska        # Right-align the value within the line length if it fits,
398*dd32d6b2SMartin Matuska        # otherwise just separate it from the name by a single space.
399*dd32d6b2SMartin Matuska        fit = LINE_LENGTH - len(INDENT) - len(name)
400*dd32d6b2SMartin Matuska        overflow = len(value) + 1
401*dd32d6b2SMartin Matuska        w = max(fit, overflow)
402*dd32d6b2SMartin Matuska        result = '{0}{1}{2:>{w}}'.format(INDENT, name, value, w=w)
403*dd32d6b2SMartin Matuska
404*dd32d6b2SMartin Matuska    return result
405*dd32d6b2SMartin Matuska
406*dd32d6b2SMartin Matuska
407*dd32d6b2SMartin Matuskadef get_kstats():
408*dd32d6b2SMartin Matuska    """Collect information on the ZFS subsystem. The step does not perform any
409*dd32d6b2SMartin Matuska    further processing, giving us the option to only work on what is actually
410*dd32d6b2SMartin Matuska    needed. The name "kstat" is a holdover from the Solaris utility of the same
411*dd32d6b2SMartin Matuska    name.
412*dd32d6b2SMartin Matuska    """
413*dd32d6b2SMartin Matuska
414*dd32d6b2SMartin Matuska    result = {}
415*dd32d6b2SMartin Matuska
416*dd32d6b2SMartin Matuska    for section in SECTION_PATHS.values():
417*dd32d6b2SMartin Matuska        if section not in result:
418*dd32d6b2SMartin Matuska            result[section] = load_kstats(section)
419*dd32d6b2SMartin Matuska
420*dd32d6b2SMartin Matuska    return result
421*dd32d6b2SMartin Matuska
422*dd32d6b2SMartin Matuska
423*dd32d6b2SMartin Matuskadef get_version(request):
424*dd32d6b2SMartin Matuska    """Get the version number of ZFS or SPL on this machine for header.
425*dd32d6b2SMartin Matuska    Returns an error string, but does not raise an error, if we can't
426*dd32d6b2SMartin Matuska    get the ZFS/SPL version.
427*dd32d6b2SMartin Matuska    """
428*dd32d6b2SMartin Matuska
429*dd32d6b2SMartin Matuska    if request not in ('spl', 'zfs'):
430*dd32d6b2SMartin Matuska        error_msg = '(ERROR: "{0}" requested)'.format(request)
431*dd32d6b2SMartin Matuska        return error_msg
432*dd32d6b2SMartin Matuska
433*dd32d6b2SMartin Matuska    return get_version_impl(request)
434*dd32d6b2SMartin Matuska
435*dd32d6b2SMartin Matuska
436*dd32d6b2SMartin Matuskadef print_header():
437*dd32d6b2SMartin Matuska    """Print the initial heading with date and time as well as info on the
438*dd32d6b2SMartin Matuska    kernel and ZFS versions. This is not called for the graph.
439*dd32d6b2SMartin Matuska    """
440*dd32d6b2SMartin Matuska
441*dd32d6b2SMartin Matuska    # datetime is now recommended over time but we keep the exact formatting
442*dd32d6b2SMartin Matuska    # from the older version of zarcsummary in case there are scripts
443*dd32d6b2SMartin Matuska    # that expect it in this way
444*dd32d6b2SMartin Matuska    daydate = time.strftime(DATE_FORMAT)
445*dd32d6b2SMartin Matuska    spc_date = LINE_LENGTH-len(daydate)
446*dd32d6b2SMartin Matuska    sys_version = os.uname()
447*dd32d6b2SMartin Matuska
448*dd32d6b2SMartin Matuska    sys_msg = sys_version.sysname+' '+sys_version.release
449*dd32d6b2SMartin Matuska    zfs = get_version('zfs')
450*dd32d6b2SMartin Matuska    spc_zfs = LINE_LENGTH-len(zfs)
451*dd32d6b2SMartin Matuska
452*dd32d6b2SMartin Matuska    machine_msg = 'Machine: '+sys_version.nodename+' ('+sys_version.machine+')'
453*dd32d6b2SMartin Matuska    spl = get_version('spl')
454*dd32d6b2SMartin Matuska    spc_spl = LINE_LENGTH-len(spl)
455*dd32d6b2SMartin Matuska
456*dd32d6b2SMartin Matuska    print('\n'+('-'*LINE_LENGTH))
457*dd32d6b2SMartin Matuska    print('{0:<{spc}}{1}'.format(TITLE, daydate, spc=spc_date))
458*dd32d6b2SMartin Matuska    print('{0:<{spc}}{1}'.format(sys_msg, zfs, spc=spc_zfs))
459*dd32d6b2SMartin Matuska    print('{0:<{spc}}{1}\n'.format(machine_msg, spl, spc=spc_spl))
460*dd32d6b2SMartin Matuska
461*dd32d6b2SMartin Matuska
462*dd32d6b2SMartin Matuskadef print_raw(kstats_dict):
463*dd32d6b2SMartin Matuska    """Print all available data from the system in a minimally sorted format.
464*dd32d6b2SMartin Matuska    This can be used as a source to be piped through 'grep'.
465*dd32d6b2SMartin Matuska    """
466*dd32d6b2SMartin Matuska
467*dd32d6b2SMartin Matuska    sections = sorted(kstats_dict.keys())
468*dd32d6b2SMartin Matuska
469*dd32d6b2SMartin Matuska    for section in sections:
470*dd32d6b2SMartin Matuska
471*dd32d6b2SMartin Matuska        print('\n{0}:'.format(section.upper()))
472*dd32d6b2SMartin Matuska        lines = sorted(kstats_dict[section])
473*dd32d6b2SMartin Matuska
474*dd32d6b2SMartin Matuska        for line in lines:
475*dd32d6b2SMartin Matuska            name, value = cleanup_line(line)
476*dd32d6b2SMartin Matuska            print(format_raw_line(name, value))
477*dd32d6b2SMartin Matuska
478*dd32d6b2SMartin Matuska    # Tunables and SPL must be handled separately because they come from a
479*dd32d6b2SMartin Matuska    # different source and have descriptions the user might request
480*dd32d6b2SMartin Matuska    print()
481*dd32d6b2SMartin Matuska    section_spl()
482*dd32d6b2SMartin Matuska    section_tunables()
483*dd32d6b2SMartin Matuska
484*dd32d6b2SMartin Matuska
485*dd32d6b2SMartin Matuskadef isolate_section(section_name, kstats_dict):
486*dd32d6b2SMartin Matuska    """From the complete information on all sections, retrieve only those
487*dd32d6b2SMartin Matuska    for one section.
488*dd32d6b2SMartin Matuska    """
489*dd32d6b2SMartin Matuska
490*dd32d6b2SMartin Matuska    try:
491*dd32d6b2SMartin Matuska        section_data = kstats_dict[section_name]
492*dd32d6b2SMartin Matuska    except KeyError:
493*dd32d6b2SMartin Matuska        print('ERROR: Data on {0} not available'.format(section_data))
494*dd32d6b2SMartin Matuska        sys.exit(1)
495*dd32d6b2SMartin Matuska
496*dd32d6b2SMartin Matuska    section_dict = dict(cleanup_line(l) for l in section_data)
497*dd32d6b2SMartin Matuska
498*dd32d6b2SMartin Matuska    return section_dict
499*dd32d6b2SMartin Matuska
500*dd32d6b2SMartin Matuska
501*dd32d6b2SMartin Matuska# Formatted output helper functions
502*dd32d6b2SMartin Matuska
503*dd32d6b2SMartin Matuska
504*dd32d6b2SMartin Matuskadef prt_1(text, value):
505*dd32d6b2SMartin Matuska    """Print text and one value, no indent"""
506*dd32d6b2SMartin Matuska    spc = ' '*(LINE_LENGTH-(len(text)+len(value)))
507*dd32d6b2SMartin Matuska    print('{0}{spc}{1}'.format(text, value, spc=spc))
508*dd32d6b2SMartin Matuska
509*dd32d6b2SMartin Matuska
510*dd32d6b2SMartin Matuskadef prt_i1(text, value):
511*dd32d6b2SMartin Matuska    """Print text and one value, with indent"""
512*dd32d6b2SMartin Matuska    spc = ' '*(LINE_LENGTH-(len(INDENT)+len(text)+len(value)))
513*dd32d6b2SMartin Matuska    print(INDENT+'{0}{spc}{1}'.format(text, value, spc=spc))
514*dd32d6b2SMartin Matuska
515*dd32d6b2SMartin Matuska
516*dd32d6b2SMartin Matuskadef prt_2(text, value1, value2):
517*dd32d6b2SMartin Matuska    """Print text and two values, no indent"""
518*dd32d6b2SMartin Matuska    values = '{0:>9}  {1:>9}'.format(value1, value2)
519*dd32d6b2SMartin Matuska    spc = ' '*(LINE_LENGTH-(len(text)+len(values)+2))
520*dd32d6b2SMartin Matuska    print('{0}{spc}  {1}'.format(text, values, spc=spc))
521*dd32d6b2SMartin Matuska
522*dd32d6b2SMartin Matuska
523*dd32d6b2SMartin Matuskadef prt_i2(text, value1, value2):
524*dd32d6b2SMartin Matuska    """Print text and two values, with indent"""
525*dd32d6b2SMartin Matuska    values = '{0:>9}  {1:>9}'.format(value1, value2)
526*dd32d6b2SMartin Matuska    spc = ' '*(LINE_LENGTH-(len(INDENT)+len(text)+len(values)+2))
527*dd32d6b2SMartin Matuska    print(INDENT+'{0}{spc}  {1}'.format(text, values, spc=spc))
528*dd32d6b2SMartin Matuska
529*dd32d6b2SMartin Matuska
530*dd32d6b2SMartin Matuska# The section output concentrates on important parameters instead of
531*dd32d6b2SMartin Matuska# being exhaustive (that is what the --raw parameter is for)
532*dd32d6b2SMartin Matuska
533*dd32d6b2SMartin Matuska
534*dd32d6b2SMartin Matuskadef section_arc(kstats_dict):
535*dd32d6b2SMartin Matuska    """Give basic information on the ARC, MRU and MFU. This is the first
536*dd32d6b2SMartin Matuska    and most used section.
537*dd32d6b2SMartin Matuska    """
538*dd32d6b2SMartin Matuska
539*dd32d6b2SMartin Matuska    arc_stats = isolate_section('arcstats', kstats_dict)
540*dd32d6b2SMartin Matuska
541*dd32d6b2SMartin Matuska    memory_all = arc_stats['memory_all_bytes']
542*dd32d6b2SMartin Matuska    memory_free = arc_stats['memory_free_bytes']
543*dd32d6b2SMartin Matuska    memory_avail = arc_stats['memory_available_bytes']
544*dd32d6b2SMartin Matuska    arc_size = arc_stats['size']
545*dd32d6b2SMartin Matuska    arc_target_size = arc_stats['c']
546*dd32d6b2SMartin Matuska    arc_max = arc_stats['c_max']
547*dd32d6b2SMartin Matuska    arc_min = arc_stats['c_min']
548*dd32d6b2SMartin Matuska    dnode_limit = arc_stats['arc_dnode_limit']
549*dd32d6b2SMartin Matuska
550*dd32d6b2SMartin Matuska    print('ARC status:')
551*dd32d6b2SMartin Matuska    prt_i1('Total memory size:', f_bytes(memory_all))
552*dd32d6b2SMartin Matuska    prt_i2('Min target size:', f_perc(arc_min, memory_all), f_bytes(arc_min))
553*dd32d6b2SMartin Matuska    prt_i2('Max target size:', f_perc(arc_max, memory_all), f_bytes(arc_max))
554*dd32d6b2SMartin Matuska    prt_i2('Target size (adaptive):',
555*dd32d6b2SMartin Matuska           f_perc(arc_size, arc_max), f_bytes(arc_target_size))
556*dd32d6b2SMartin Matuska    prt_i2('Current size:', f_perc(arc_size, arc_max), f_bytes(arc_size))
557*dd32d6b2SMartin Matuska    prt_i1('Free memory size:', f_bytes(memory_free))
558*dd32d6b2SMartin Matuska    prt_i1('Available memory size:', f_bytes(memory_avail))
559*dd32d6b2SMartin Matuska    print()
560*dd32d6b2SMartin Matuska
561*dd32d6b2SMartin Matuska    compressed_size = arc_stats['compressed_size']
562*dd32d6b2SMartin Matuska    uncompressed_size = arc_stats['uncompressed_size']
563*dd32d6b2SMartin Matuska    overhead_size = arc_stats['overhead_size']
564*dd32d6b2SMartin Matuska    bonus_size = arc_stats['bonus_size']
565*dd32d6b2SMartin Matuska    dnode_size = arc_stats['dnode_size']
566*dd32d6b2SMartin Matuska    dbuf_size = arc_stats['dbuf_size']
567*dd32d6b2SMartin Matuska    hdr_size = arc_stats['hdr_size']
568*dd32d6b2SMartin Matuska    l2_hdr_size = arc_stats['l2_hdr_size']
569*dd32d6b2SMartin Matuska    abd_chunk_waste_size = arc_stats['abd_chunk_waste_size']
570*dd32d6b2SMartin Matuska
571*dd32d6b2SMartin Matuska    prt_1('ARC structural breakdown (current size):', f_bytes(arc_size))
572*dd32d6b2SMartin Matuska    prt_i2('Compressed size:',
573*dd32d6b2SMartin Matuska           f_perc(compressed_size, arc_size), f_bytes(compressed_size))
574*dd32d6b2SMartin Matuska    prt_i2('Overhead size:',
575*dd32d6b2SMartin Matuska           f_perc(overhead_size, arc_size), f_bytes(overhead_size))
576*dd32d6b2SMartin Matuska    prt_i2('Bonus size:',
577*dd32d6b2SMartin Matuska           f_perc(bonus_size, arc_size), f_bytes(bonus_size))
578*dd32d6b2SMartin Matuska    prt_i2('Dnode size:',
579*dd32d6b2SMartin Matuska           f_perc(dnode_size, arc_size), f_bytes(dnode_size))
580*dd32d6b2SMartin Matuska    prt_i2('Dbuf size:',
581*dd32d6b2SMartin Matuska           f_perc(dbuf_size, arc_size), f_bytes(dbuf_size))
582*dd32d6b2SMartin Matuska    prt_i2('Header size:',
583*dd32d6b2SMartin Matuska           f_perc(hdr_size, arc_size), f_bytes(hdr_size))
584*dd32d6b2SMartin Matuska    prt_i2('L2 header size:',
585*dd32d6b2SMartin Matuska           f_perc(l2_hdr_size, arc_size), f_bytes(l2_hdr_size))
586*dd32d6b2SMartin Matuska    prt_i2('ABD chunk waste size:',
587*dd32d6b2SMartin Matuska           f_perc(abd_chunk_waste_size, arc_size), f_bytes(abd_chunk_waste_size))
588*dd32d6b2SMartin Matuska    print()
589*dd32d6b2SMartin Matuska
590*dd32d6b2SMartin Matuska    meta = arc_stats['meta']
591*dd32d6b2SMartin Matuska    pd = arc_stats['pd']
592*dd32d6b2SMartin Matuska    pm = arc_stats['pm']
593*dd32d6b2SMartin Matuska    data_size = arc_stats['data_size']
594*dd32d6b2SMartin Matuska    metadata_size = arc_stats['metadata_size']
595*dd32d6b2SMartin Matuska    anon_data = arc_stats['anon_data']
596*dd32d6b2SMartin Matuska    anon_metadata = arc_stats['anon_metadata']
597*dd32d6b2SMartin Matuska    mfu_data = arc_stats['mfu_data']
598*dd32d6b2SMartin Matuska    mfu_metadata = arc_stats['mfu_metadata']
599*dd32d6b2SMartin Matuska    mfu_edata = arc_stats['mfu_evictable_data']
600*dd32d6b2SMartin Matuska    mfu_emetadata = arc_stats['mfu_evictable_metadata']
601*dd32d6b2SMartin Matuska    mru_data = arc_stats['mru_data']
602*dd32d6b2SMartin Matuska    mru_metadata = arc_stats['mru_metadata']
603*dd32d6b2SMartin Matuska    mru_edata = arc_stats['mru_evictable_data']
604*dd32d6b2SMartin Matuska    mru_emetadata = arc_stats['mru_evictable_metadata']
605*dd32d6b2SMartin Matuska    mfug_data = arc_stats['mfu_ghost_data']
606*dd32d6b2SMartin Matuska    mfug_metadata = arc_stats['mfu_ghost_metadata']
607*dd32d6b2SMartin Matuska    mrug_data = arc_stats['mru_ghost_data']
608*dd32d6b2SMartin Matuska    mrug_metadata = arc_stats['mru_ghost_metadata']
609*dd32d6b2SMartin Matuska    unc_data = arc_stats['uncached_data']
610*dd32d6b2SMartin Matuska    unc_metadata = arc_stats['uncached_metadata']
611*dd32d6b2SMartin Matuska    caches_size = int(anon_data)+int(anon_metadata)+\
612*dd32d6b2SMartin Matuska        int(mfu_data)+int(mfu_metadata)+int(mru_data)+int(mru_metadata)+\
613*dd32d6b2SMartin Matuska        int(unc_data)+int(unc_metadata)
614*dd32d6b2SMartin Matuska
615*dd32d6b2SMartin Matuska    prt_1('ARC types breakdown (compressed + overhead):', f_bytes(caches_size))
616*dd32d6b2SMartin Matuska    prt_i2('Data size:',
617*dd32d6b2SMartin Matuska           f_perc(data_size, caches_size), f_bytes(data_size))
618*dd32d6b2SMartin Matuska    prt_i2('Metadata size:',
619*dd32d6b2SMartin Matuska           f_perc(metadata_size, caches_size), f_bytes(metadata_size))
620*dd32d6b2SMartin Matuska    print()
621*dd32d6b2SMartin Matuska
622*dd32d6b2SMartin Matuska    prt_1('ARC states breakdown (compressed + overhead):', f_bytes(caches_size))
623*dd32d6b2SMartin Matuska    prt_i2('Anonymous data size:',
624*dd32d6b2SMartin Matuska           f_perc(anon_data, caches_size), f_bytes(anon_data))
625*dd32d6b2SMartin Matuska    prt_i2('Anonymous metadata size:',
626*dd32d6b2SMartin Matuska           f_perc(anon_metadata, caches_size), f_bytes(anon_metadata))
627*dd32d6b2SMartin Matuska    s = 4294967296
628*dd32d6b2SMartin Matuska    v = (s-int(pd))*(s-int(meta))/s
629*dd32d6b2SMartin Matuska    prt_i2('MFU data target:', f_perc(v, s),
630*dd32d6b2SMartin Matuska        f_bytes(v / 65536 * caches_size / 65536))
631*dd32d6b2SMartin Matuska    prt_i2('MFU data size:',
632*dd32d6b2SMartin Matuska           f_perc(mfu_data, caches_size), f_bytes(mfu_data))
633*dd32d6b2SMartin Matuska    prt_i2('MFU evictable data size:',
634*dd32d6b2SMartin Matuska           f_perc(mfu_edata, caches_size), f_bytes(mfu_edata))
635*dd32d6b2SMartin Matuska    prt_i1('MFU ghost data size:', f_bytes(mfug_data))
636*dd32d6b2SMartin Matuska    v = (s-int(pm))*int(meta)/s
637*dd32d6b2SMartin Matuska    prt_i2('MFU metadata target:', f_perc(v, s),
638*dd32d6b2SMartin Matuska        f_bytes(v / 65536 * caches_size / 65536))
639*dd32d6b2SMartin Matuska    prt_i2('MFU metadata size:',
640*dd32d6b2SMartin Matuska           f_perc(mfu_metadata, caches_size), f_bytes(mfu_metadata))
641*dd32d6b2SMartin Matuska    prt_i2('MFU evictable metadata size:',
642*dd32d6b2SMartin Matuska           f_perc(mfu_emetadata, caches_size), f_bytes(mfu_emetadata))
643*dd32d6b2SMartin Matuska    prt_i1('MFU ghost metadata size:', f_bytes(mfug_metadata))
644*dd32d6b2SMartin Matuska    v = int(pd)*(s-int(meta))/s
645*dd32d6b2SMartin Matuska    prt_i2('MRU data target:', f_perc(v, s),
646*dd32d6b2SMartin Matuska        f_bytes(v / 65536 * caches_size / 65536))
647*dd32d6b2SMartin Matuska    prt_i2('MRU data size:',
648*dd32d6b2SMartin Matuska           f_perc(mru_data, caches_size), f_bytes(mru_data))
649*dd32d6b2SMartin Matuska    prt_i2('MRU evictable data size:',
650*dd32d6b2SMartin Matuska           f_perc(mru_edata, caches_size), f_bytes(mru_edata))
651*dd32d6b2SMartin Matuska    prt_i1('MRU ghost data size:', f_bytes(mrug_data))
652*dd32d6b2SMartin Matuska    v = int(pm)*int(meta)/s
653*dd32d6b2SMartin Matuska    prt_i2('MRU metadata target:', f_perc(v, s),
654*dd32d6b2SMartin Matuska        f_bytes(v / 65536 * caches_size / 65536))
655*dd32d6b2SMartin Matuska    prt_i2('MRU metadata size:',
656*dd32d6b2SMartin Matuska           f_perc(mru_metadata, caches_size), f_bytes(mru_metadata))
657*dd32d6b2SMartin Matuska    prt_i2('MRU evictable metadata size:',
658*dd32d6b2SMartin Matuska           f_perc(mru_emetadata, caches_size), f_bytes(mru_emetadata))
659*dd32d6b2SMartin Matuska    prt_i1('MRU ghost metadata size:', f_bytes(mrug_metadata))
660*dd32d6b2SMartin Matuska    prt_i2('Uncached data size:',
661*dd32d6b2SMartin Matuska           f_perc(unc_data, caches_size), f_bytes(unc_data))
662*dd32d6b2SMartin Matuska    prt_i2('Uncached metadata size:',
663*dd32d6b2SMartin Matuska           f_perc(unc_metadata, caches_size), f_bytes(unc_metadata))
664*dd32d6b2SMartin Matuska    print()
665*dd32d6b2SMartin Matuska
666*dd32d6b2SMartin Matuska    print('ARC hash breakdown:')
667*dd32d6b2SMartin Matuska    prt_i1('Elements:', f_hits(arc_stats['hash_elements']))
668*dd32d6b2SMartin Matuska    prt_i1('Collisions:', f_hits(arc_stats['hash_collisions']))
669*dd32d6b2SMartin Matuska
670*dd32d6b2SMartin Matuska    prt_i1('Chain max:', f_hits(arc_stats['hash_chain_max']))
671*dd32d6b2SMartin Matuska    prt_i1('Chains:', f_hits(arc_stats['hash_chains']))
672*dd32d6b2SMartin Matuska    print()
673*dd32d6b2SMartin Matuska
674*dd32d6b2SMartin Matuska    print('ARC misc:')
675*dd32d6b2SMartin Matuska    prt_i2('Uncompressed size:', f_perc(uncompressed_size, compressed_size),
676*dd32d6b2SMartin Matuska           f_bytes(uncompressed_size))
677*dd32d6b2SMartin Matuska    prt_i1('Memory throttles:', arc_stats['memory_throttle_count'])
678*dd32d6b2SMartin Matuska    prt_i1('Memory direct reclaims:', arc_stats['memory_direct_count'])
679*dd32d6b2SMartin Matuska    prt_i1('Memory indirect reclaims:', arc_stats['memory_indirect_count'])
680*dd32d6b2SMartin Matuska    prt_i1('Deleted:', f_hits(arc_stats['deleted']))
681*dd32d6b2SMartin Matuska    prt_i1('Mutex misses:', f_hits(arc_stats['mutex_miss']))
682*dd32d6b2SMartin Matuska    prt_i1('Eviction skips:', f_hits(arc_stats['evict_skip']))
683*dd32d6b2SMartin Matuska    prt_i1('Eviction skips due to L2 writes:',
684*dd32d6b2SMartin Matuska           f_hits(arc_stats['evict_l2_skip']))
685*dd32d6b2SMartin Matuska    prt_i1('L2 cached evictions:', f_bytes(arc_stats['evict_l2_cached']))
686*dd32d6b2SMartin Matuska    prt_i1('L2 eligible evictions:', f_bytes(arc_stats['evict_l2_eligible']))
687*dd32d6b2SMartin Matuska    prt_i2('L2 eligible MFU evictions:',
688*dd32d6b2SMartin Matuska           f_perc(arc_stats['evict_l2_eligible_mfu'],
689*dd32d6b2SMartin Matuska           arc_stats['evict_l2_eligible']),
690*dd32d6b2SMartin Matuska           f_bytes(arc_stats['evict_l2_eligible_mfu']))
691*dd32d6b2SMartin Matuska    prt_i2('L2 eligible MRU evictions:',
692*dd32d6b2SMartin Matuska           f_perc(arc_stats['evict_l2_eligible_mru'],
693*dd32d6b2SMartin Matuska           arc_stats['evict_l2_eligible']),
694*dd32d6b2SMartin Matuska           f_bytes(arc_stats['evict_l2_eligible_mru']))
695*dd32d6b2SMartin Matuska    prt_i1('L2 ineligible evictions:',
696*dd32d6b2SMartin Matuska           f_bytes(arc_stats['evict_l2_ineligible']))
697*dd32d6b2SMartin Matuska    print()
698*dd32d6b2SMartin Matuska
699*dd32d6b2SMartin Matuska
700*dd32d6b2SMartin Matuskadef section_archits(kstats_dict):
701*dd32d6b2SMartin Matuska    """Print information on how the caches are accessed ("arc hits").
702*dd32d6b2SMartin Matuska    """
703*dd32d6b2SMartin Matuska
704*dd32d6b2SMartin Matuska    arc_stats = isolate_section('arcstats', kstats_dict)
705*dd32d6b2SMartin Matuska    all_accesses = int(arc_stats['hits'])+int(arc_stats['iohits'])+\
706*dd32d6b2SMartin Matuska        int(arc_stats['misses'])
707*dd32d6b2SMartin Matuska
708*dd32d6b2SMartin Matuska    prt_1('ARC total accesses:', f_hits(all_accesses))
709*dd32d6b2SMartin Matuska    ta_todo = (('Total hits:', arc_stats['hits']),
710*dd32d6b2SMartin Matuska               ('Total I/O hits:', arc_stats['iohits']),
711*dd32d6b2SMartin Matuska               ('Total misses:', arc_stats['misses']))
712*dd32d6b2SMartin Matuska    for title, value in ta_todo:
713*dd32d6b2SMartin Matuska        prt_i2(title, f_perc(value, all_accesses), f_hits(value))
714*dd32d6b2SMartin Matuska    print()
715*dd32d6b2SMartin Matuska
716*dd32d6b2SMartin Matuska    dd_total = int(arc_stats['demand_data_hits']) +\
717*dd32d6b2SMartin Matuska        int(arc_stats['demand_data_iohits']) +\
718*dd32d6b2SMartin Matuska        int(arc_stats['demand_data_misses'])
719*dd32d6b2SMartin Matuska    prt_2('ARC demand data accesses:', f_perc(dd_total, all_accesses),
720*dd32d6b2SMartin Matuska         f_hits(dd_total))
721*dd32d6b2SMartin Matuska    dd_todo = (('Demand data hits:', arc_stats['demand_data_hits']),
722*dd32d6b2SMartin Matuska               ('Demand data I/O hits:', arc_stats['demand_data_iohits']),
723*dd32d6b2SMartin Matuska               ('Demand data misses:', arc_stats['demand_data_misses']))
724*dd32d6b2SMartin Matuska    for title, value in dd_todo:
725*dd32d6b2SMartin Matuska        prt_i2(title, f_perc(value, dd_total), f_hits(value))
726*dd32d6b2SMartin Matuska    print()
727*dd32d6b2SMartin Matuska
728*dd32d6b2SMartin Matuska    dm_total = int(arc_stats['demand_metadata_hits']) +\
729*dd32d6b2SMartin Matuska        int(arc_stats['demand_metadata_iohits']) +\
730*dd32d6b2SMartin Matuska        int(arc_stats['demand_metadata_misses'])
731*dd32d6b2SMartin Matuska    prt_2('ARC demand metadata accesses:', f_perc(dm_total, all_accesses),
732*dd32d6b2SMartin Matuska          f_hits(dm_total))
733*dd32d6b2SMartin Matuska    dm_todo = (('Demand metadata hits:', arc_stats['demand_metadata_hits']),
734*dd32d6b2SMartin Matuska               ('Demand metadata I/O hits:',
735*dd32d6b2SMartin Matuska                arc_stats['demand_metadata_iohits']),
736*dd32d6b2SMartin Matuska               ('Demand metadata misses:', arc_stats['demand_metadata_misses']))
737*dd32d6b2SMartin Matuska    for title, value in dm_todo:
738*dd32d6b2SMartin Matuska        prt_i2(title, f_perc(value, dm_total), f_hits(value))
739*dd32d6b2SMartin Matuska    print()
740*dd32d6b2SMartin Matuska
741*dd32d6b2SMartin Matuska    pd_total = int(arc_stats['prefetch_data_hits']) +\
742*dd32d6b2SMartin Matuska        int(arc_stats['prefetch_data_iohits']) +\
743*dd32d6b2SMartin Matuska        int(arc_stats['prefetch_data_misses'])
744*dd32d6b2SMartin Matuska    prt_2('ARC prefetch data accesses:', f_perc(pd_total, all_accesses),
745*dd32d6b2SMartin Matuska          f_hits(pd_total))
746*dd32d6b2SMartin Matuska    pd_todo = (('Prefetch data hits:', arc_stats['prefetch_data_hits']),
747*dd32d6b2SMartin Matuska               ('Prefetch data I/O hits:', arc_stats['prefetch_data_iohits']),
748*dd32d6b2SMartin Matuska               ('Prefetch data misses:', arc_stats['prefetch_data_misses']))
749*dd32d6b2SMartin Matuska    for title, value in pd_todo:
750*dd32d6b2SMartin Matuska        prt_i2(title, f_perc(value, pd_total), f_hits(value))
751*dd32d6b2SMartin Matuska    print()
752*dd32d6b2SMartin Matuska
753*dd32d6b2SMartin Matuska    pm_total = int(arc_stats['prefetch_metadata_hits']) +\
754*dd32d6b2SMartin Matuska        int(arc_stats['prefetch_metadata_iohits']) +\
755*dd32d6b2SMartin Matuska        int(arc_stats['prefetch_metadata_misses'])
756*dd32d6b2SMartin Matuska    prt_2('ARC prefetch metadata accesses:', f_perc(pm_total, all_accesses),
757*dd32d6b2SMartin Matuska          f_hits(pm_total))
758*dd32d6b2SMartin Matuska    pm_todo = (('Prefetch metadata hits:',
759*dd32d6b2SMartin Matuska                arc_stats['prefetch_metadata_hits']),
760*dd32d6b2SMartin Matuska               ('Prefetch metadata I/O hits:',
761*dd32d6b2SMartin Matuska                arc_stats['prefetch_metadata_iohits']),
762*dd32d6b2SMartin Matuska               ('Prefetch metadata misses:',
763*dd32d6b2SMartin Matuska                arc_stats['prefetch_metadata_misses']))
764*dd32d6b2SMartin Matuska    for title, value in pm_todo:
765*dd32d6b2SMartin Matuska        prt_i2(title, f_perc(value, pm_total), f_hits(value))
766*dd32d6b2SMartin Matuska    print()
767*dd32d6b2SMartin Matuska
768*dd32d6b2SMartin Matuska    all_prefetches = int(arc_stats['predictive_prefetch'])+\
769*dd32d6b2SMartin Matuska        int(arc_stats['prescient_prefetch'])
770*dd32d6b2SMartin Matuska    prt_2('ARC predictive prefetches:',
771*dd32d6b2SMartin Matuska           f_perc(arc_stats['predictive_prefetch'], all_prefetches),
772*dd32d6b2SMartin Matuska           f_hits(arc_stats['predictive_prefetch']))
773*dd32d6b2SMartin Matuska    prt_i2('Demand hits after predictive:',
774*dd32d6b2SMartin Matuska           f_perc(arc_stats['demand_hit_predictive_prefetch'],
775*dd32d6b2SMartin Matuska                  arc_stats['predictive_prefetch']),
776*dd32d6b2SMartin Matuska           f_hits(arc_stats['demand_hit_predictive_prefetch']))
777*dd32d6b2SMartin Matuska    prt_i2('Demand I/O hits after predictive:',
778*dd32d6b2SMartin Matuska           f_perc(arc_stats['demand_iohit_predictive_prefetch'],
779*dd32d6b2SMartin Matuska                  arc_stats['predictive_prefetch']),
780*dd32d6b2SMartin Matuska           f_hits(arc_stats['demand_iohit_predictive_prefetch']))
781*dd32d6b2SMartin Matuska    never = int(arc_stats['predictive_prefetch']) -\
782*dd32d6b2SMartin Matuska        int(arc_stats['demand_hit_predictive_prefetch']) -\
783*dd32d6b2SMartin Matuska        int(arc_stats['demand_iohit_predictive_prefetch'])
784*dd32d6b2SMartin Matuska    prt_i2('Never demanded after predictive:',
785*dd32d6b2SMartin Matuska           f_perc(never, arc_stats['predictive_prefetch']),
786*dd32d6b2SMartin Matuska           f_hits(never))
787*dd32d6b2SMartin Matuska    print()
788*dd32d6b2SMartin Matuska
789*dd32d6b2SMartin Matuska    prt_2('ARC prescient prefetches:',
790*dd32d6b2SMartin Matuska           f_perc(arc_stats['prescient_prefetch'], all_prefetches),
791*dd32d6b2SMartin Matuska           f_hits(arc_stats['prescient_prefetch']))
792*dd32d6b2SMartin Matuska    prt_i2('Demand hits after prescient:',
793*dd32d6b2SMartin Matuska           f_perc(arc_stats['demand_hit_prescient_prefetch'],
794*dd32d6b2SMartin Matuska                  arc_stats['prescient_prefetch']),
795*dd32d6b2SMartin Matuska           f_hits(arc_stats['demand_hit_prescient_prefetch']))
796*dd32d6b2SMartin Matuska    prt_i2('Demand I/O hits after prescient:',
797*dd32d6b2SMartin Matuska           f_perc(arc_stats['demand_iohit_prescient_prefetch'],
798*dd32d6b2SMartin Matuska                  arc_stats['prescient_prefetch']),
799*dd32d6b2SMartin Matuska           f_hits(arc_stats['demand_iohit_prescient_prefetch']))
800*dd32d6b2SMartin Matuska    never = int(arc_stats['prescient_prefetch'])-\
801*dd32d6b2SMartin Matuska        int(arc_stats['demand_hit_prescient_prefetch'])-\
802*dd32d6b2SMartin Matuska        int(arc_stats['demand_iohit_prescient_prefetch'])
803*dd32d6b2SMartin Matuska    prt_i2('Never demanded after prescient:',
804*dd32d6b2SMartin Matuska           f_perc(never, arc_stats['prescient_prefetch']),
805*dd32d6b2SMartin Matuska           f_hits(never))
806*dd32d6b2SMartin Matuska    print()
807*dd32d6b2SMartin Matuska
808*dd32d6b2SMartin Matuska    print('ARC states hits of all accesses:')
809*dd32d6b2SMartin Matuska    cl_todo = (('Most frequently used (MFU):', arc_stats['mfu_hits']),
810*dd32d6b2SMartin Matuska               ('Most recently used (MRU):', arc_stats['mru_hits']),
811*dd32d6b2SMartin Matuska               ('Most frequently used (MFU) ghost:',
812*dd32d6b2SMartin Matuska                arc_stats['mfu_ghost_hits']),
813*dd32d6b2SMartin Matuska               ('Most recently used (MRU) ghost:',
814*dd32d6b2SMartin Matuska                arc_stats['mru_ghost_hits']),
815*dd32d6b2SMartin Matuska               ('Uncached:', arc_stats['uncached_hits']))
816*dd32d6b2SMartin Matuska    for title, value in cl_todo:
817*dd32d6b2SMartin Matuska        prt_i2(title, f_perc(value, all_accesses), f_hits(value))
818*dd32d6b2SMartin Matuska    print()
819*dd32d6b2SMartin Matuska
820*dd32d6b2SMartin Matuska
821*dd32d6b2SMartin Matuskadef section_dmu(kstats_dict):
822*dd32d6b2SMartin Matuska    """Collect information on the DMU"""
823*dd32d6b2SMartin Matuska
824*dd32d6b2SMartin Matuska    zfetch_stats = isolate_section('zfetchstats', kstats_dict)
825*dd32d6b2SMartin Matuska
826*dd32d6b2SMartin Matuska    zfetch_access_total = int(zfetch_stats['hits']) +\
827*dd32d6b2SMartin Matuska        int(zfetch_stats['future']) + int(zfetch_stats['stride']) +\
828*dd32d6b2SMartin Matuska        int(zfetch_stats['past']) + int(zfetch_stats['misses'])
829*dd32d6b2SMartin Matuska
830*dd32d6b2SMartin Matuska    prt_1('DMU predictive prefetcher calls:', f_hits(zfetch_access_total))
831*dd32d6b2SMartin Matuska    prt_i2('Stream hits:',
832*dd32d6b2SMartin Matuska           f_perc(zfetch_stats['hits'], zfetch_access_total),
833*dd32d6b2SMartin Matuska           f_hits(zfetch_stats['hits']))
834*dd32d6b2SMartin Matuska    future = int(zfetch_stats['future']) + int(zfetch_stats['stride'])
835*dd32d6b2SMartin Matuska    prt_i2('Hits ahead of stream:', f_perc(future, zfetch_access_total),
836*dd32d6b2SMartin Matuska           f_hits(future))
837*dd32d6b2SMartin Matuska    prt_i2('Hits behind stream:',
838*dd32d6b2SMartin Matuska           f_perc(zfetch_stats['past'], zfetch_access_total),
839*dd32d6b2SMartin Matuska           f_hits(zfetch_stats['past']))
840*dd32d6b2SMartin Matuska    prt_i2('Stream misses:',
841*dd32d6b2SMartin Matuska           f_perc(zfetch_stats['misses'], zfetch_access_total),
842*dd32d6b2SMartin Matuska           f_hits(zfetch_stats['misses']))
843*dd32d6b2SMartin Matuska    prt_i2('Streams limit reached:',
844*dd32d6b2SMartin Matuska           f_perc(zfetch_stats['max_streams'], zfetch_stats['misses']),
845*dd32d6b2SMartin Matuska           f_hits(zfetch_stats['max_streams']))
846*dd32d6b2SMartin Matuska    prt_i1('Stream strides:', f_hits(zfetch_stats['stride']))
847*dd32d6b2SMartin Matuska    prt_i1('Prefetches issued', f_hits(zfetch_stats['io_issued']))
848*dd32d6b2SMartin Matuska    print()
849*dd32d6b2SMartin Matuska
850*dd32d6b2SMartin Matuska
851*dd32d6b2SMartin Matuskadef section_l2arc(kstats_dict):
852*dd32d6b2SMartin Matuska    """Collect information on L2ARC device if present. If not, tell user
853*dd32d6b2SMartin Matuska    that we're skipping the section.
854*dd32d6b2SMartin Matuska    """
855*dd32d6b2SMartin Matuska
856*dd32d6b2SMartin Matuska    # The L2ARC statistics live in the same section as the normal ARC stuff
857*dd32d6b2SMartin Matuska    arc_stats = isolate_section('arcstats', kstats_dict)
858*dd32d6b2SMartin Matuska
859*dd32d6b2SMartin Matuska    if arc_stats['l2_size'] == '0':
860*dd32d6b2SMartin Matuska        print('L2ARC not detected, skipping section\n')
861*dd32d6b2SMartin Matuska        return
862*dd32d6b2SMartin Matuska
863*dd32d6b2SMartin Matuska    l2_errors = int(arc_stats['l2_writes_error']) +\
864*dd32d6b2SMartin Matuska        int(arc_stats['l2_cksum_bad']) +\
865*dd32d6b2SMartin Matuska        int(arc_stats['l2_io_error'])
866*dd32d6b2SMartin Matuska
867*dd32d6b2SMartin Matuska    l2_access_total = int(arc_stats['l2_hits'])+int(arc_stats['l2_misses'])
868*dd32d6b2SMartin Matuska    health = 'HEALTHY'
869*dd32d6b2SMartin Matuska
870*dd32d6b2SMartin Matuska    if l2_errors > 0:
871*dd32d6b2SMartin Matuska        health = 'DEGRADED'
872*dd32d6b2SMartin Matuska
873*dd32d6b2SMartin Matuska    prt_1('L2ARC status:', health)
874*dd32d6b2SMartin Matuska
875*dd32d6b2SMartin Matuska    l2_todo = (('Low memory aborts:', 'l2_abort_lowmem'),
876*dd32d6b2SMartin Matuska               ('Free on write:', 'l2_free_on_write'),
877*dd32d6b2SMartin Matuska               ('R/W clashes:', 'l2_rw_clash'),
878*dd32d6b2SMartin Matuska               ('Bad checksums:', 'l2_cksum_bad'),
879*dd32d6b2SMartin Matuska               ('Read errors:', 'l2_io_error'),
880*dd32d6b2SMartin Matuska               ('Write errors:', 'l2_writes_error'))
881*dd32d6b2SMartin Matuska
882*dd32d6b2SMartin Matuska    for title, value in l2_todo:
883*dd32d6b2SMartin Matuska        prt_i1(title, f_hits(arc_stats[value]))
884*dd32d6b2SMartin Matuska
885*dd32d6b2SMartin Matuska    print()
886*dd32d6b2SMartin Matuska    prt_1('L2ARC size (adaptive):', f_bytes(arc_stats['l2_size']))
887*dd32d6b2SMartin Matuska    prt_i2('Compressed:', f_perc(arc_stats['l2_asize'], arc_stats['l2_size']),
888*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_asize']))
889*dd32d6b2SMartin Matuska    prt_i2('Header size:',
890*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_hdr_size'], arc_stats['l2_size']),
891*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_hdr_size']))
892*dd32d6b2SMartin Matuska    prt_i2('MFU allocated size:',
893*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_mfu_asize'], arc_stats['l2_asize']),
894*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_mfu_asize']))
895*dd32d6b2SMartin Matuska    prt_i2('MRU allocated size:',
896*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_mru_asize'], arc_stats['l2_asize']),
897*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_mru_asize']))
898*dd32d6b2SMartin Matuska    prt_i2('Prefetch allocated size:',
899*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_prefetch_asize'], arc_stats['l2_asize']),
900*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_prefetch_asize']))
901*dd32d6b2SMartin Matuska    prt_i2('Data (buffer content) allocated size:',
902*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_bufc_data_asize'], arc_stats['l2_asize']),
903*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_bufc_data_asize']))
904*dd32d6b2SMartin Matuska    prt_i2('Metadata (buffer content) allocated size:',
905*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_bufc_metadata_asize'], arc_stats['l2_asize']),
906*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_bufc_metadata_asize']))
907*dd32d6b2SMartin Matuska
908*dd32d6b2SMartin Matuska    print()
909*dd32d6b2SMartin Matuska    prt_1('L2ARC breakdown:', f_hits(l2_access_total))
910*dd32d6b2SMartin Matuska    prt_i2('Hit ratio:',
911*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_hits'], l2_access_total),
912*dd32d6b2SMartin Matuska           f_hits(arc_stats['l2_hits']))
913*dd32d6b2SMartin Matuska    prt_i2('Miss ratio:',
914*dd32d6b2SMartin Matuska           f_perc(arc_stats['l2_misses'], l2_access_total),
915*dd32d6b2SMartin Matuska           f_hits(arc_stats['l2_misses']))
916*dd32d6b2SMartin Matuska
917*dd32d6b2SMartin Matuska    print()
918*dd32d6b2SMartin Matuska    print('L2ARC I/O:')
919*dd32d6b2SMartin Matuska    prt_i2('Reads:',
920*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_read_bytes']),
921*dd32d6b2SMartin Matuska           f_hits(arc_stats['l2_hits']))
922*dd32d6b2SMartin Matuska    prt_i2('Writes:',
923*dd32d6b2SMartin Matuska           f_bytes(arc_stats['l2_write_bytes']),
924*dd32d6b2SMartin Matuska           f_hits(arc_stats['l2_writes_sent']))
925*dd32d6b2SMartin Matuska
926*dd32d6b2SMartin Matuska    print()
927*dd32d6b2SMartin Matuska    print('L2ARC evicts:')
928*dd32d6b2SMartin Matuska    prt_i1('L1 cached:', f_hits(arc_stats['l2_evict_l1cached']))
929*dd32d6b2SMartin Matuska    prt_i1('While reading:', f_hits(arc_stats['l2_evict_reading']))
930*dd32d6b2SMartin Matuska    print()
931*dd32d6b2SMartin Matuska
932*dd32d6b2SMartin Matuska
933*dd32d6b2SMartin Matuskadef section_spl(*_):
934*dd32d6b2SMartin Matuska    """Print the SPL parameters, if requested with alternative format
935*dd32d6b2SMartin Matuska    and/or descriptions. This does not use kstats.
936*dd32d6b2SMartin Matuska    """
937*dd32d6b2SMartin Matuska
938*dd32d6b2SMartin Matuska    if sys.platform.startswith('freebsd'):
939*dd32d6b2SMartin Matuska        # No SPL support in FreeBSD
940*dd32d6b2SMartin Matuska        return
941*dd32d6b2SMartin Matuska
942*dd32d6b2SMartin Matuska    spls = get_spl_params()
943*dd32d6b2SMartin Matuska    keylist = sorted(spls.keys())
944*dd32d6b2SMartin Matuska    print('Solaris Porting Layer (SPL):')
945*dd32d6b2SMartin Matuska
946*dd32d6b2SMartin Matuska    if ARGS.desc:
947*dd32d6b2SMartin Matuska        descriptions = get_descriptions('spl')
948*dd32d6b2SMartin Matuska
949*dd32d6b2SMartin Matuska    for key in keylist:
950*dd32d6b2SMartin Matuska        value = spls[key]
951*dd32d6b2SMartin Matuska
952*dd32d6b2SMartin Matuska        if ARGS.desc:
953*dd32d6b2SMartin Matuska            try:
954*dd32d6b2SMartin Matuska                print(INDENT+'#', descriptions[key])
955*dd32d6b2SMartin Matuska            except KeyError:
956*dd32d6b2SMartin Matuska                print(INDENT+'# (No description found)')  # paranoid
957*dd32d6b2SMartin Matuska
958*dd32d6b2SMartin Matuska        print(format_raw_line(key, value))
959*dd32d6b2SMartin Matuska
960*dd32d6b2SMartin Matuska    print()
961*dd32d6b2SMartin Matuska
962*dd32d6b2SMartin Matuska
963*dd32d6b2SMartin Matuskadef section_tunables(*_):
964*dd32d6b2SMartin Matuska    """Print the tunables, if requested with alternative format and/or
965*dd32d6b2SMartin Matuska    descriptions. This does not use kstasts.
966*dd32d6b2SMartin Matuska    """
967*dd32d6b2SMartin Matuska
968*dd32d6b2SMartin Matuska    tunables = get_tunable_params()
969*dd32d6b2SMartin Matuska    keylist = sorted(tunables.keys())
970*dd32d6b2SMartin Matuska    print('Tunables:')
971*dd32d6b2SMartin Matuska
972*dd32d6b2SMartin Matuska    if ARGS.desc:
973*dd32d6b2SMartin Matuska        descriptions = get_descriptions('zfs')
974*dd32d6b2SMartin Matuska
975*dd32d6b2SMartin Matuska    for key in keylist:
976*dd32d6b2SMartin Matuska        value = tunables[key]
977*dd32d6b2SMartin Matuska
978*dd32d6b2SMartin Matuska        if ARGS.desc:
979*dd32d6b2SMartin Matuska            try:
980*dd32d6b2SMartin Matuska                print(INDENT+'#', descriptions[key])
981*dd32d6b2SMartin Matuska            except KeyError:
982*dd32d6b2SMartin Matuska                print(INDENT+'# (No description found)')  # paranoid
983*dd32d6b2SMartin Matuska
984*dd32d6b2SMartin Matuska        print(format_raw_line(key, value))
985*dd32d6b2SMartin Matuska
986*dd32d6b2SMartin Matuska    print()
987*dd32d6b2SMartin Matuska
988*dd32d6b2SMartin Matuska
989*dd32d6b2SMartin Matuskadef section_zil(kstats_dict):
990*dd32d6b2SMartin Matuska    """Collect information on the ZFS Intent Log. Some of the information
991*dd32d6b2SMartin Matuska    taken from https://github.com/openzfs/zfs/blob/master/include/sys/zil.h
992*dd32d6b2SMartin Matuska    """
993*dd32d6b2SMartin Matuska
994*dd32d6b2SMartin Matuska    zil_stats = isolate_section('zil', kstats_dict)
995*dd32d6b2SMartin Matuska
996*dd32d6b2SMartin Matuska    prt_1('ZIL committed transactions:',
997*dd32d6b2SMartin Matuska          f_hits(zil_stats['zil_itx_count']))
998*dd32d6b2SMartin Matuska    prt_i1('Commit requests:', f_hits(zil_stats['zil_commit_count']))
999*dd32d6b2SMartin Matuska    prt_i1('Flushes to stable storage:',
1000*dd32d6b2SMartin Matuska           f_hits(zil_stats['zil_commit_writer_count']))
1001*dd32d6b2SMartin Matuska    prt_i2('Transactions to SLOG storage pool:',
1002*dd32d6b2SMartin Matuska           f_bytes(zil_stats['zil_itx_metaslab_slog_bytes']),
1003*dd32d6b2SMartin Matuska           f_hits(zil_stats['zil_itx_metaslab_slog_count']))
1004*dd32d6b2SMartin Matuska    prt_i2('Transactions to non-SLOG storage pool:',
1005*dd32d6b2SMartin Matuska           f_bytes(zil_stats['zil_itx_metaslab_normal_bytes']),
1006*dd32d6b2SMartin Matuska           f_hits(zil_stats['zil_itx_metaslab_normal_count']))
1007*dd32d6b2SMartin Matuska    print()
1008*dd32d6b2SMartin Matuska
1009*dd32d6b2SMartin Matuska
1010*dd32d6b2SMartin Matuskasection_calls = {'arc': section_arc,
1011*dd32d6b2SMartin Matuska                 'archits': section_archits,
1012*dd32d6b2SMartin Matuska                 'dmu': section_dmu,
1013*dd32d6b2SMartin Matuska                 'l2arc': section_l2arc,
1014*dd32d6b2SMartin Matuska                 'spl': section_spl,
1015*dd32d6b2SMartin Matuska                 'tunables': section_tunables,
1016*dd32d6b2SMartin Matuska                 'zil': section_zil}
1017*dd32d6b2SMartin Matuska
1018*dd32d6b2SMartin Matuska
1019*dd32d6b2SMartin Matuskadef main():
1020*dd32d6b2SMartin Matuska    """Run program. The options to draw a graph and to print all data raw are
1021*dd32d6b2SMartin Matuska    treated separately because they come with their own call.
1022*dd32d6b2SMartin Matuska    """
1023*dd32d6b2SMartin Matuska
1024*dd32d6b2SMartin Matuska    kstats = get_kstats()
1025*dd32d6b2SMartin Matuska
1026*dd32d6b2SMartin Matuska    if ARGS.graph:
1027*dd32d6b2SMartin Matuska        draw_graph(kstats)
1028*dd32d6b2SMartin Matuska        sys.exit(0)
1029*dd32d6b2SMartin Matuska
1030*dd32d6b2SMartin Matuska    print_header()
1031*dd32d6b2SMartin Matuska
1032*dd32d6b2SMartin Matuska    if ARGS.raw:
1033*dd32d6b2SMartin Matuska        print_raw(kstats)
1034*dd32d6b2SMartin Matuska
1035*dd32d6b2SMartin Matuska    elif ARGS.section:
1036*dd32d6b2SMartin Matuska
1037*dd32d6b2SMartin Matuska        try:
1038*dd32d6b2SMartin Matuska            section_calls[ARGS.section](kstats)
1039*dd32d6b2SMartin Matuska        except KeyError:
1040*dd32d6b2SMartin Matuska            print('Error: Section "{0}" unknown'.format(ARGS.section))
1041*dd32d6b2SMartin Matuska            sys.exit(1)
1042*dd32d6b2SMartin Matuska
1043*dd32d6b2SMartin Matuska    elif ARGS.page:
1044*dd32d6b2SMartin Matuska        print('WARNING: Pages are deprecated, please use "--section"\n')
1045*dd32d6b2SMartin Matuska
1046*dd32d6b2SMartin Matuska        pages_to_calls = {1: 'arc',
1047*dd32d6b2SMartin Matuska                          2: 'archits',
1048*dd32d6b2SMartin Matuska                          3: 'l2arc',
1049*dd32d6b2SMartin Matuska                          4: 'dmu',
1050*dd32d6b2SMartin Matuska                          5: 'vdev',
1051*dd32d6b2SMartin Matuska                          6: 'tunables'}
1052*dd32d6b2SMartin Matuska
1053*dd32d6b2SMartin Matuska        try:
1054*dd32d6b2SMartin Matuska            call = pages_to_calls[ARGS.page]
1055*dd32d6b2SMartin Matuska        except KeyError:
1056*dd32d6b2SMartin Matuska            print('Error: Page "{0}" not supported'.format(ARGS.page))
1057*dd32d6b2SMartin Matuska            sys.exit(1)
1058*dd32d6b2SMartin Matuska        else:
1059*dd32d6b2SMartin Matuska            section_calls[call](kstats)
1060*dd32d6b2SMartin Matuska
1061*dd32d6b2SMartin Matuska    else:
1062*dd32d6b2SMartin Matuska        # If no parameters were given, we print all sections. We might want to
1063*dd32d6b2SMartin Matuska        # change the sequence by hand
1064*dd32d6b2SMartin Matuska        calls = sorted(section_calls.keys())
1065*dd32d6b2SMartin Matuska
1066*dd32d6b2SMartin Matuska        for section in calls:
1067*dd32d6b2SMartin Matuska            section_calls[section](kstats)
1068*dd32d6b2SMartin Matuska
1069*dd32d6b2SMartin Matuska    sys.exit(0)
1070*dd32d6b2SMartin Matuska
1071*dd32d6b2SMartin Matuska
1072*dd32d6b2SMartin Matuskaif __name__ == '__main__':
1073*dd32d6b2SMartin Matuska    main()
1074