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