aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/arc_summary/arc_summary3
diff options
context:
space:
mode:
authorнаб <[email protected]>2022-04-10 22:47:56 +0200
committerBrian Behlendorf <[email protected]>2022-05-10 10:20:34 -0700
commit0a9aaa7f0cc16f1771f492cce665bc5c5eb8e735 (patch)
tree685a29bda113f8264935eb2d1e2238ef2de31765 /cmd/arc_summary/arc_summary3
parenteaf94bda6cc31eb11822be5c782b8b43279212ee (diff)
cmd: move single-file binaries up, extract udev programs to udev/
Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Ahelenia Ziemiańska <[email protected]> Closes #13316
Diffstat (limited to 'cmd/arc_summary/arc_summary3')
-rwxr-xr-xcmd/arc_summary/arc_summary3978
1 files changed, 0 insertions, 978 deletions
diff --git a/cmd/arc_summary/arc_summary3 b/cmd/arc_summary/arc_summary3
deleted file mode 100755
index 4f275813d..000000000
--- a/cmd/arc_summary/arc_summary3
+++ /dev/null
@@ -1,978 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (c) 2008 Ben Rockwood <[email protected]>,
-# Copyright (c) 2010 Martin Matuska <[email protected]>,
-# Copyright (c) 2010-2011 Jason J. Hellenthal <[email protected]>,
-# Copyright (c) 2017 Scot W. Stevenson <[email protected]>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-"""Print statistics on the ZFS ARC Cache and other information
-
-Provides basic information on the ARC, its efficiency, the L2ARC (if present),
-the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See
-the in-source documentation and code at
-https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
-The original introduction to arc_summary can be found at
-http://cuddletech.com/?p=454
-"""
-
-import argparse
-import os
-import subprocess
-import sys
-import time
-import errno
-
-# We can't use env -S portably, and we need python3 -u to handle pipes in
-# the shell abruptly closing the way we want to, so...
-import io
-if isinstance(sys.__stderr__.buffer, io.BufferedWriter):
- os.execv(sys.executable, [sys.executable, "-u"] + sys.argv)
-
-DESCRIPTION = 'Print ARC and other statistics for OpenZFS'
-INDENT = ' '*8
-LINE_LENGTH = 72
-DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
-TITLE = 'ZFS Subsystem Report'
-
-SECTIONS = 'arc archits dmu l2arc spl tunables vdev zil'.split()
-SECTION_HELP = 'print info from one section ('+' '.join(SECTIONS)+')'
-
-# Tunables and SPL are handled separately because they come from
-# different sources
-SECTION_PATHS = {'arc': 'arcstats',
- 'dmu': 'dmu_tx',
- 'l2arc': 'arcstats', # L2ARC stuff lives in arcstats
- 'vdev': 'vdev_cache_stats',
- 'zfetch': 'zfetchstats',
- 'zil': 'zil'}
-
-parser = argparse.ArgumentParser(description=DESCRIPTION)
-parser.add_argument('-a', '--alternate', action='store_true', default=False,
- help='use alternate formatting for tunables and SPL',
- dest='alt')
-parser.add_argument('-d', '--description', action='store_true', default=False,
- help='print descriptions with tunables and SPL',
- dest='desc')
-parser.add_argument('-g', '--graph', action='store_true', default=False,
- help='print graph on ARC use and exit', dest='graph')
-parser.add_argument('-p', '--page', type=int, dest='page',
- help='print page by number (DEPRECATED, use "-s")')
-parser.add_argument('-r', '--raw', action='store_true', default=False,
- help='dump all available data with minimal formatting',
- dest='raw')
-parser.add_argument('-s', '--section', dest='section', help=SECTION_HELP)
-ARGS = parser.parse_args()
-
-
-if sys.platform.startswith('freebsd'):
- # Requires py36-sysctl on FreeBSD
- import sysctl
-
- VDEV_CACHE_SIZE = 'vdev.cache_size'
-
- def is_value(ctl):
- return ctl.type != sysctl.CTLTYPE_NODE
-
- def namefmt(ctl, base='vfs.zfs.'):
- # base is removed from the name
- cut = len(base)
- return ctl.name[cut:]
-
- def load_kstats(section):
- base = 'kstat.zfs.misc.{section}.'.format(section=section)
- fmt = lambda kstat: '{name} : {value}'.format(name=namefmt(kstat, base),
- value=kstat.value)
- kstats = sysctl.filter(base)
- return [fmt(kstat) for kstat in kstats if is_value(kstat)]
-
- def get_params(base):
- ctls = sysctl.filter(base)
- return {namefmt(ctl): str(ctl.value) for ctl in ctls if is_value(ctl)}
-
- def get_tunable_params():
- return get_params('vfs.zfs')
-
- def get_vdev_params():
- return get_params('vfs.zfs.vdev')
-
- def get_version_impl(request):
- # FreeBSD reports versions for zpl and spa instead of zfs and spl.
- name = {'zfs': 'zpl',
- 'spl': 'spa'}[request]
- mib = 'vfs.zfs.version.{}'.format(name)
- version = sysctl.filter(mib)[0].value
- return '{} version {}'.format(name, version)
-
- def get_descriptions(_request):
- ctls = sysctl.filter('vfs.zfs')
- return {namefmt(ctl): ctl.description for ctl in ctls if is_value(ctl)}
-
-
-elif sys.platform.startswith('linux'):
- KSTAT_PATH = '/proc/spl/kstat/zfs'
- SPL_PATH = '/sys/module/spl/parameters'
- TUNABLES_PATH = '/sys/module/zfs/parameters'
-
- VDEV_CACHE_SIZE = 'zfs_vdev_cache_size'
-
- def load_kstats(section):
- path = os.path.join(KSTAT_PATH, section)
- with open(path) as f:
- return list(f)[2:] # Get rid of header
-
- def get_params(basepath):
- """Collect information on the Solaris Porting Layer (SPL) or the
- tunables, depending on the PATH given. Does not check if PATH is
- legal.
- """
- result = {}
- for name in os.listdir(basepath):
- path = os.path.join(basepath, name)
- with open(path) as f:
- value = f.read()
- result[name] = value.strip()
- return result
-
- def get_spl_params():
- return get_params(SPL_PATH)
-
- def get_tunable_params():
- return get_params(TUNABLES_PATH)
-
- def get_vdev_params():
- return get_params(TUNABLES_PATH)
-
- def get_version_impl(request):
- # The original arc_summary called /sbin/modinfo/{spl,zfs} to get
- # the version information. We switch to /sys/module/{spl,zfs}/version
- # to make sure we get what is really loaded in the kernel
- try:
- with open("/sys/module/{}/version".format(request)) as f:
- return f.read().strip()
- except:
- return "(unknown)"
-
- def get_descriptions(request):
- """Get the descriptions of the Solaris Porting Layer (SPL) or the
- tunables, return with minimal formatting.
- """
-
- if request not in ('spl', 'zfs'):
- print('ERROR: description of "{0}" requested)'.format(request))
- sys.exit(1)
-
- descs = {}
- target_prefix = 'parm:'
-
- # We would prefer to do this with /sys/modules -- see the discussion at
- # get_version() -- but there isn't a way to get the descriptions from
- # there, so we fall back on modinfo
- command = ["/sbin/modinfo", request, "-0"]
-
- info = ''
-
- try:
-
- info = subprocess.run(command, stdout=subprocess.PIPE,
- check=True, universal_newlines=True)
- raw_output = info.stdout.split('\0')
-
- except subprocess.CalledProcessError:
- print("Error: Descriptions not available",
- "(can't access kernel module)")
- sys.exit(1)
-
- for line in raw_output:
-
- if not line.startswith(target_prefix):
- continue
-
- line = line[len(target_prefix):].strip()
- name, raw_desc = line.split(':', 1)
- desc = raw_desc.rsplit('(', 1)[0]
-
- if desc == '':
- desc = '(No description found)'
-
- descs[name.strip()] = desc.strip()
-
- return descs
-
-def handle_unraisableException(exc_type, exc_value=None, exc_traceback=None,
- err_msg=None, object=None):
- handle_Exception(exc_type, object, exc_traceback)
-
-def handle_Exception(ex_cls, ex, tb):
- if ex_cls is KeyboardInterrupt:
- sys.exit()
-
- if ex_cls is BrokenPipeError:
- # It turns out that while sys.exit() triggers an exception
- # not handled message on Python 3.8+, os._exit() does not.
- os._exit(0)
-
- if ex_cls is OSError:
- if ex.errno == errno.ENOTCONN:
- sys.exit()
-
- raise ex
-
-if hasattr(sys,'unraisablehook'): # Python 3.8+
- sys.unraisablehook = handle_unraisableException
-sys.excepthook = handle_Exception
-
-
-def cleanup_line(single_line):
- """Format a raw line of data from /proc and isolate the name value
- part, returning a tuple with each. Currently, this gets rid of the
- middle '4'. For example "arc_no_grow 4 0" returns the tuple
- ("arc_no_grow", "0").
- """
- name, _, value = single_line.split()
-
- return name, value
-
-
-def draw_graph(kstats_dict):
- """Draw a primitive graph representing the basic information on the
- ARC -- its size and the proportion used by MFU and MRU -- and quit.
- We use max size of the ARC to calculate how full it is. This is a
- very rough representation.
- """
-
- arc_stats = isolate_section('arcstats', kstats_dict)
-
- GRAPH_INDENT = ' '*4
- GRAPH_WIDTH = 60
- arc_size = f_bytes(arc_stats['size'])
- arc_perc = f_perc(arc_stats['size'], arc_stats['c_max'])
- mfu_size = f_bytes(arc_stats['mfu_size'])
- mru_size = f_bytes(arc_stats['mru_size'])
- meta_limit = f_bytes(arc_stats['arc_meta_limit'])
- meta_size = f_bytes(arc_stats['arc_meta_used'])
- dnode_limit = f_bytes(arc_stats['arc_dnode_limit'])
- dnode_size = f_bytes(arc_stats['dnode_size'])
-
- info_form = ('ARC: {0} ({1}) MFU: {2} MRU: {3} META: {4} ({5}) '
- 'DNODE {6} ({7})')
- info_line = info_form.format(arc_size, arc_perc, mfu_size, mru_size,
- meta_size, meta_limit, dnode_size,
- dnode_limit)
- info_spc = ' '*int((GRAPH_WIDTH-len(info_line))/2)
- info_line = GRAPH_INDENT+info_spc+info_line
-
- graph_line = GRAPH_INDENT+'+'+('-'*(GRAPH_WIDTH-2))+'+'
-
- mfu_perc = float(int(arc_stats['mfu_size'])/int(arc_stats['c_max']))
- mru_perc = float(int(arc_stats['mru_size'])/int(arc_stats['c_max']))
- arc_perc = float(int(arc_stats['size'])/int(arc_stats['c_max']))
- total_ticks = float(arc_perc)*GRAPH_WIDTH
- mfu_ticks = mfu_perc*GRAPH_WIDTH
- mru_ticks = mru_perc*GRAPH_WIDTH
- other_ticks = total_ticks-(mfu_ticks+mru_ticks)
-
- core_form = 'F'*int(mfu_ticks)+'R'*int(mru_ticks)+'O'*int(other_ticks)
- core_spc = ' '*(GRAPH_WIDTH-(2+len(core_form)))
- core_line = GRAPH_INDENT+'|'+core_form+core_spc+'|'
-
- for line in ('', info_line, graph_line, core_line, graph_line, ''):
- print(line)
-
-
-def f_bytes(byte_string):
- """Return human-readable representation of a byte value in
- powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
- points. Values smaller than one KiB are returned without
- decimal points. Note "bytes" is a reserved keyword.
- """
-
- prefixes = ([2**80, "YiB"], # yobibytes (yotta)
- [2**70, "ZiB"], # zebibytes (zetta)
- [2**60, "EiB"], # exbibytes (exa)
- [2**50, "PiB"], # pebibytes (peta)
- [2**40, "TiB"], # tebibytes (tera)
- [2**30, "GiB"], # gibibytes (giga)
- [2**20, "MiB"], # mebibytes (mega)
- [2**10, "KiB"]) # kibibytes (kilo)
-
- bites = int(byte_string)
-
- if bites >= 2**10:
- for limit, unit in prefixes:
-
- if bites >= limit:
- value = bites / limit
- break
-
- result = '{0:.1f} {1}'.format(value, unit)
- else:
- result = '{0} Bytes'.format(bites)
-
- return result
-
-
-def f_hits(hits_string):
- """Create a human-readable representation of the number of hits.
- The single-letter symbols used are SI to avoid the confusion caused
- by the different "short scale" and "long scale" representations in
- English, which use the same words for different values. See
- https://en.wikipedia.org/wiki/Names_of_large_numbers and:
- https://physics.nist.gov/cuu/Units/prefixes.html
- """
-
- numbers = ([10**24, 'Y'], # yotta (septillion)
- [10**21, 'Z'], # zetta (sextillion)
- [10**18, 'E'], # exa (quintrillion)
- [10**15, 'P'], # peta (quadrillion)
- [10**12, 'T'], # tera (trillion)
- [10**9, 'G'], # giga (billion)
- [10**6, 'M'], # mega (million)
- [10**3, 'k']) # kilo (thousand)
-
- hits = int(hits_string)
-
- if hits >= 1000:
- for limit, symbol in numbers:
-
- if hits >= limit:
- value = hits/limit
- break
-
- result = "%0.1f%s" % (value, symbol)
- else:
- result = "%d" % hits
-
- return result
-
-
-def f_perc(value1, value2):
- """Calculate percentage and return in human-readable form. If
- rounding produces the result '0.0' though the first number is
- not zero, include a 'less-than' symbol to avoid confusion.
- Division by zero is handled by returning 'n/a'; no error
- is called.
- """
-
- v1 = float(value1)
- v2 = float(value2)
-
- try:
- perc = 100 * v1/v2
- except ZeroDivisionError:
- result = 'n/a'
- else:
- result = '{0:0.1f} %'.format(perc)
-
- if result == '0.0 %' and v1 > 0:
- result = '< 0.1 %'
-
- return result
-
-
-def format_raw_line(name, value):
- """For the --raw option for the tunable and SPL outputs, decide on the
- correct formatting based on the --alternate flag.
- """
-
- if ARGS.alt:
- result = '{0}{1}={2}'.format(INDENT, name, value)
- else:
- # Right-align the value within the line length if it fits,
- # otherwise just separate it from the name by a single space.
- fit = LINE_LENGTH - len(INDENT) - len(name)
- overflow = len(value) + 1
- w = max(fit, overflow)
- result = '{0}{1}{2:>{w}}'.format(INDENT, name, value, w=w)
-
- return result
-
-
-def get_kstats():
- """Collect information on the ZFS subsystem. The step does not perform any
- further processing, giving us the option to only work on what is actually
- needed. The name "kstat" is a holdover from the Solaris utility of the same
- name.
- """
-
- result = {}
-
- for section in SECTION_PATHS.values():
- if section not in result:
- result[section] = load_kstats(section)
-
- return result
-
-
-def get_version(request):
- """Get the version number of ZFS or SPL on this machine for header.
- Returns an error string, but does not raise an error, if we can't
- get the ZFS/SPL version.
- """
-
- if request not in ('spl', 'zfs'):
- error_msg = '(ERROR: "{0}" requested)'.format(request)
- return error_msg
-
- return get_version_impl(request)
-
-
-def print_header():
- """Print the initial heading with date and time as well as info on the
- kernel and ZFS versions. This is not called for the graph.
- """
-
- # datetime is now recommended over time but we keep the exact formatting
- # from the older version of arc_summary in case there are scripts
- # that expect it in this way
- daydate = time.strftime(DATE_FORMAT)
- spc_date = LINE_LENGTH-len(daydate)
- sys_version = os.uname()
-
- sys_msg = sys_version.sysname+' '+sys_version.release
- zfs = get_version('zfs')
- spc_zfs = LINE_LENGTH-len(zfs)
-
- machine_msg = 'Machine: '+sys_version.nodename+' ('+sys_version.machine+')'
- spl = get_version('spl')
- spc_spl = LINE_LENGTH-len(spl)
-
- print('\n'+('-'*LINE_LENGTH))
- print('{0:<{spc}}{1}'.format(TITLE, daydate, spc=spc_date))
- print('{0:<{spc}}{1}'.format(sys_msg, zfs, spc=spc_zfs))
- print('{0:<{spc}}{1}\n'.format(machine_msg, spl, spc=spc_spl))
-
-
-def print_raw(kstats_dict):
- """Print all available data from the system in a minimally sorted format.
- This can be used as a source to be piped through 'grep'.
- """
-
- sections = sorted(kstats_dict.keys())
-
- for section in sections:
-
- print('\n{0}:'.format(section.upper()))
- lines = sorted(kstats_dict[section])
-
- for line in lines:
- name, value = cleanup_line(line)
- print(format_raw_line(name, value))
-
- # Tunables and SPL must be handled separately because they come from a
- # different source and have descriptions the user might request
- print()
- section_spl()
- section_tunables()
-
-
-def isolate_section(section_name, kstats_dict):
- """From the complete information on all sections, retrieve only those
- for one section.
- """
-
- try:
- section_data = kstats_dict[section_name]
- except KeyError:
- print('ERROR: Data on {0} not available'.format(section_data))
- sys.exit(1)
-
- section_dict = dict(cleanup_line(l) for l in section_data)
-
- return section_dict
-
-
-# Formatted output helper functions
-
-
-def prt_1(text, value):
- """Print text and one value, no indent"""
- spc = ' '*(LINE_LENGTH-(len(text)+len(value)))
- print('{0}{spc}{1}'.format(text, value, spc=spc))
-
-
-def prt_i1(text, value):
- """Print text and one value, with indent"""
- spc = ' '*(LINE_LENGTH-(len(INDENT)+len(text)+len(value)))
- print(INDENT+'{0}{spc}{1}'.format(text, value, spc=spc))
-
-
-def prt_2(text, value1, value2):
- """Print text and two values, no indent"""
- values = '{0:>9} {1:>9}'.format(value1, value2)
- spc = ' '*(LINE_LENGTH-(len(text)+len(values)+2))
- print('{0}{spc} {1}'.format(text, values, spc=spc))
-
-
-def prt_i2(text, value1, value2):
- """Print text and two values, with indent"""
- values = '{0:>9} {1:>9}'.format(value1, value2)
- spc = ' '*(LINE_LENGTH-(len(INDENT)+len(text)+len(values)+2))
- print(INDENT+'{0}{spc} {1}'.format(text, values, spc=spc))
-
-
-# The section output concentrates on important parameters instead of
-# being exhaustive (that is what the --raw parameter is for)
-
-
-def section_arc(kstats_dict):
- """Give basic information on the ARC, MRU and MFU. This is the first
- and most used section.
- """
-
- arc_stats = isolate_section('arcstats', kstats_dict)
-
- throttle = arc_stats['memory_throttle_count']
-
- if throttle == '0':
- health = 'HEALTHY'
- else:
- health = 'THROTTLED'
-
- prt_1('ARC status:', health)
- prt_i1('Memory throttle count:', throttle)
- print()
-
- arc_size = arc_stats['size']
- arc_target_size = arc_stats['c']
- arc_max = arc_stats['c_max']
- arc_min = arc_stats['c_min']
- mfu_size = arc_stats['mfu_size']
- mru_size = arc_stats['mru_size']
- meta_limit = arc_stats['arc_meta_limit']
- meta_size = arc_stats['arc_meta_used']
- dnode_limit = arc_stats['arc_dnode_limit']
- dnode_size = arc_stats['dnode_size']
- target_size_ratio = '{0}:1'.format(int(arc_max) // int(arc_min))
-
- prt_2('ARC size (current):',
- f_perc(arc_size, arc_max), f_bytes(arc_size))
- prt_i2('Target size (adaptive):',
- f_perc(arc_target_size, arc_max), f_bytes(arc_target_size))
- prt_i2('Min size (hard limit):',
- f_perc(arc_min, arc_max), f_bytes(arc_min))
- prt_i2('Max size (high water):',
- target_size_ratio, f_bytes(arc_max))
- caches_size = int(mfu_size)+int(mru_size)
- prt_i2('Most Frequently Used (MFU) cache size:',
- f_perc(mfu_size, caches_size), f_bytes(mfu_size))
- prt_i2('Most Recently Used (MRU) cache size:',
- f_perc(mru_size, caches_size), f_bytes(mru_size))
- prt_i2('Metadata cache size (hard limit):',
- f_perc(meta_limit, arc_max), f_bytes(meta_limit))
- prt_i2('Metadata cache size (current):',
- f_perc(meta_size, meta_limit), f_bytes(meta_size))
- prt_i2('Dnode cache size (hard limit):',
- f_perc(dnode_limit, meta_limit), f_bytes(dnode_limit))
- prt_i2('Dnode cache size (current):',
- f_perc(dnode_size, dnode_limit), f_bytes(dnode_size))
- print()
-
- print('ARC hash breakdown:')
- prt_i1('Elements max:', f_hits(arc_stats['hash_elements_max']))
- prt_i2('Elements current:',
- f_perc(arc_stats['hash_elements'], arc_stats['hash_elements_max']),
- f_hits(arc_stats['hash_elements']))
- prt_i1('Collisions:', f_hits(arc_stats['hash_collisions']))
-
- prt_i1('Chain max:', f_hits(arc_stats['hash_chain_max']))
- prt_i1('Chains:', f_hits(arc_stats['hash_chains']))
- print()
-
- print('ARC misc:')
- prt_i1('Deleted:', f_hits(arc_stats['deleted']))
- prt_i1('Mutex misses:', f_hits(arc_stats['mutex_miss']))
- prt_i1('Eviction skips:', f_hits(arc_stats['evict_skip']))
- prt_i1('Eviction skips due to L2 writes:',
- f_hits(arc_stats['evict_l2_skip']))
- prt_i1('L2 cached evictions:', f_bytes(arc_stats['evict_l2_cached']))
- prt_i1('L2 eligible evictions:', f_bytes(arc_stats['evict_l2_eligible']))
- prt_i2('L2 eligible MFU evictions:',
- f_perc(arc_stats['evict_l2_eligible_mfu'],
- arc_stats['evict_l2_eligible']),
- f_bytes(arc_stats['evict_l2_eligible_mfu']))
- prt_i2('L2 eligible MRU evictions:',
- f_perc(arc_stats['evict_l2_eligible_mru'],
- arc_stats['evict_l2_eligible']),
- f_bytes(arc_stats['evict_l2_eligible_mru']))
- prt_i1('L2 ineligible evictions:',
- f_bytes(arc_stats['evict_l2_ineligible']))
- print()
-
-
-def section_archits(kstats_dict):
- """Print information on how the caches are accessed ("arc hits").
- """
-
- arc_stats = isolate_section('arcstats', kstats_dict)
- all_accesses = int(arc_stats['hits'])+int(arc_stats['misses'])
- actual_hits = int(arc_stats['mfu_hits'])+int(arc_stats['mru_hits'])
-
- prt_1('ARC total accesses (hits + misses):', f_hits(all_accesses))
- ta_todo = (('Cache hit ratio:', arc_stats['hits']),
- ('Cache miss ratio:', arc_stats['misses']),
- ('Actual hit ratio (MFU + MRU hits):', actual_hits))
-
- for title, value in ta_todo:
- prt_i2(title, f_perc(value, all_accesses), f_hits(value))
-
- dd_total = int(arc_stats['demand_data_hits']) +\
- int(arc_stats['demand_data_misses'])
- prt_i2('Data demand efficiency:',
- f_perc(arc_stats['demand_data_hits'], dd_total),
- f_hits(dd_total))
-
- dp_total = int(arc_stats['prefetch_data_hits']) +\
- int(arc_stats['prefetch_data_misses'])
- prt_i2('Data prefetch efficiency:',
- f_perc(arc_stats['prefetch_data_hits'], dp_total),
- f_hits(dp_total))
-
- known_hits = int(arc_stats['mfu_hits']) +\
- int(arc_stats['mru_hits']) +\
- int(arc_stats['mfu_ghost_hits']) +\
- int(arc_stats['mru_ghost_hits'])
-
- anon_hits = int(arc_stats['hits'])-known_hits
-
- print()
- print('Cache hits by cache type:')
- cl_todo = (('Most frequently used (MFU):', arc_stats['mfu_hits']),
- ('Most recently used (MRU):', arc_stats['mru_hits']),
- ('Most frequently used (MFU) ghost:',
- arc_stats['mfu_ghost_hits']),
- ('Most recently used (MRU) ghost:',
- arc_stats['mru_ghost_hits']))
-
- for title, value in cl_todo:
- prt_i2(title, f_perc(value, arc_stats['hits']), f_hits(value))
-
- # For some reason, anon_hits can turn negative, which is weird. Until we
- # have figured out why this happens, we just hide the problem, following
- # the behavior of the original arc_summary.
- if anon_hits >= 0:
- prt_i2('Anonymously used:',
- f_perc(anon_hits, arc_stats['hits']), f_hits(anon_hits))
-
- print()
- print('Cache hits by data type:')
- dt_todo = (('Demand data:', arc_stats['demand_data_hits']),
- ('Demand prefetch data:', arc_stats['prefetch_data_hits']),
- ('Demand metadata:', arc_stats['demand_metadata_hits']),
- ('Demand prefetch metadata:',
- arc_stats['prefetch_metadata_hits']))
-
- for title, value in dt_todo:
- prt_i2(title, f_perc(value, arc_stats['hits']), f_hits(value))
-
- print()
- print('Cache misses by data type:')
- dm_todo = (('Demand data:', arc_stats['demand_data_misses']),
- ('Demand prefetch data:',
- arc_stats['prefetch_data_misses']),
- ('Demand metadata:', arc_stats['demand_metadata_misses']),
- ('Demand prefetch metadata:',
- arc_stats['prefetch_metadata_misses']))
-
- for title, value in dm_todo:
- prt_i2(title, f_perc(value, arc_stats['misses']), f_hits(value))
-
- print()
-
-
-def section_dmu(kstats_dict):
- """Collect information on the DMU"""
-
- zfetch_stats = isolate_section('zfetchstats', kstats_dict)
-
- zfetch_access_total = int(zfetch_stats['hits'])+int(zfetch_stats['misses'])
-
- prt_1('DMU prefetch efficiency:', f_hits(zfetch_access_total))
- prt_i2('Hit ratio:', f_perc(zfetch_stats['hits'], zfetch_access_total),
- f_hits(zfetch_stats['hits']))
- prt_i2('Miss ratio:', f_perc(zfetch_stats['misses'], zfetch_access_total),
- f_hits(zfetch_stats['misses']))
- print()
-
-
-def section_l2arc(kstats_dict):
- """Collect information on L2ARC device if present. If not, tell user
- that we're skipping the section.
- """
-
- # The L2ARC statistics live in the same section as the normal ARC stuff
- arc_stats = isolate_section('arcstats', kstats_dict)
-
- if arc_stats['l2_size'] == '0':
- print('L2ARC not detected, skipping section\n')
- return
-
- l2_errors = int(arc_stats['l2_writes_error']) +\
- int(arc_stats['l2_cksum_bad']) +\
- int(arc_stats['l2_io_error'])
-
- l2_access_total = int(arc_stats['l2_hits'])+int(arc_stats['l2_misses'])
- health = 'HEALTHY'
-
- if l2_errors > 0:
- health = 'DEGRADED'
-
- prt_1('L2ARC status:', health)
-
- l2_todo = (('Low memory aborts:', 'l2_abort_lowmem'),
- ('Free on write:', 'l2_free_on_write'),
- ('R/W clashes:', 'l2_rw_clash'),
- ('Bad checksums:', 'l2_cksum_bad'),
- ('I/O errors:', 'l2_io_error'))
-
- for title, value in l2_todo:
- prt_i1(title, f_hits(arc_stats[value]))
-
- print()
- prt_1('L2ARC size (adaptive):', f_bytes(arc_stats['l2_size']))
- prt_i2('Compressed:', f_perc(arc_stats['l2_asize'], arc_stats['l2_size']),
- f_bytes(arc_stats['l2_asize']))
- prt_i2('Header size:',
- f_perc(arc_stats['l2_hdr_size'], arc_stats['l2_size']),
- f_bytes(arc_stats['l2_hdr_size']))
- prt_i2('MFU allocated size:',
- f_perc(arc_stats['l2_mfu_asize'], arc_stats['l2_asize']),
- f_bytes(arc_stats['l2_mfu_asize']))
- prt_i2('MRU allocated size:',
- f_perc(arc_stats['l2_mru_asize'], arc_stats['l2_asize']),
- f_bytes(arc_stats['l2_mru_asize']))
- prt_i2('Prefetch allocated size:',
- f_perc(arc_stats['l2_prefetch_asize'], arc_stats['l2_asize']),
- f_bytes(arc_stats['l2_prefetch_asize']))
- prt_i2('Data (buffer content) allocated size:',
- f_perc(arc_stats['l2_bufc_data_asize'], arc_stats['l2_asize']),
- f_bytes(arc_stats['l2_bufc_data_asize']))
- prt_i2('Metadata (buffer content) allocated size:',
- f_perc(arc_stats['l2_bufc_metadata_asize'], arc_stats['l2_asize']),
- f_bytes(arc_stats['l2_bufc_metadata_asize']))
-
- print()
- prt_1('L2ARC breakdown:', f_hits(l2_access_total))
- prt_i2('Hit ratio:',
- f_perc(arc_stats['l2_hits'], l2_access_total),
- f_hits(arc_stats['l2_hits']))
- prt_i2('Miss ratio:',
- f_perc(arc_stats['l2_misses'], l2_access_total),
- f_hits(arc_stats['l2_misses']))
- prt_i1('Feeds:', f_hits(arc_stats['l2_feeds']))
-
- print()
- print('L2ARC writes:')
-
- if arc_stats['l2_writes_done'] != arc_stats['l2_writes_sent']:
- prt_i2('Writes sent:', 'FAULTED', f_hits(arc_stats['l2_writes_sent']))
- prt_i2('Done ratio:',
- f_perc(arc_stats['l2_writes_done'],
- arc_stats['l2_writes_sent']),
- f_hits(arc_stats['l2_writes_done']))
- prt_i2('Error ratio:',
- f_perc(arc_stats['l2_writes_error'],
- arc_stats['l2_writes_sent']),
- f_hits(arc_stats['l2_writes_error']))
- else:
- prt_i2('Writes sent:', '100 %', f_hits(arc_stats['l2_writes_sent']))
-
- print()
- print('L2ARC evicts:')
- prt_i1('Lock retries:', f_hits(arc_stats['l2_evict_lock_retry']))
- prt_i1('Upon reading:', f_hits(arc_stats['l2_evict_reading']))
- print()
-
-
-def section_spl(*_):
- """Print the SPL parameters, if requested with alternative format
- and/or descriptions. This does not use kstats.
- """
-
- if sys.platform.startswith('freebsd'):
- # No SPL support in FreeBSD
- return
-
- spls = get_spl_params()
- keylist = sorted(spls.keys())
- print('Solaris Porting Layer (SPL):')
-
- if ARGS.desc:
- descriptions = get_descriptions('spl')
-
- for key in keylist:
- value = spls[key]
-
- if ARGS.desc:
- try:
- print(INDENT+'#', descriptions[key])
- except KeyError:
- print(INDENT+'# (No description found)') # paranoid
-
- print(format_raw_line(key, value))
-
- print()
-
-
-def section_tunables(*_):
- """Print the tunables, if requested with alternative format and/or
- descriptions. This does not use kstasts.
- """
-
- tunables = get_tunable_params()
- keylist = sorted(tunables.keys())
- print('Tunables:')
-
- if ARGS.desc:
- descriptions = get_descriptions('zfs')
-
- for key in keylist:
- value = tunables[key]
-
- if ARGS.desc:
- try:
- print(INDENT+'#', descriptions[key])
- except KeyError:
- print(INDENT+'# (No description found)') # paranoid
-
- print(format_raw_line(key, value))
-
- print()
-
-
-def section_vdev(kstats_dict):
- """Collect information on VDEV caches"""
-
- # Currently [Nov 2017] the VDEV cache is disabled, because it is actually
- # harmful. When this is the case, we just skip the whole entry. See
- # https://github.com/openzfs/zfs/blob/master/module/zfs/vdev_cache.c
- # for details
- tunables = get_vdev_params()
-
- if tunables[VDEV_CACHE_SIZE] == '0':
- print('VDEV cache disabled, skipping section\n')
- return
-
- vdev_stats = isolate_section('vdev_cache_stats', kstats_dict)
-
- vdev_cache_total = int(vdev_stats['hits']) +\
- int(vdev_stats['misses']) +\
- int(vdev_stats['delegations'])
-
- prt_1('VDEV cache summary:', f_hits(vdev_cache_total))
- prt_i2('Hit ratio:', f_perc(vdev_stats['hits'], vdev_cache_total),
- f_hits(vdev_stats['hits']))
- prt_i2('Miss ratio:', f_perc(vdev_stats['misses'], vdev_cache_total),
- f_hits(vdev_stats['misses']))
- prt_i2('Delegations:', f_perc(vdev_stats['delegations'], vdev_cache_total),
- f_hits(vdev_stats['delegations']))
- print()
-
-
-def section_zil(kstats_dict):
- """Collect information on the ZFS Intent Log. Some of the information
- taken from https://github.com/openzfs/zfs/blob/master/include/sys/zil.h
- """
-
- zil_stats = isolate_section('zil', kstats_dict)
-
- prt_1('ZIL committed transactions:',
- f_hits(zil_stats['zil_itx_count']))
- prt_i1('Commit requests:', f_hits(zil_stats['zil_commit_count']))
- prt_i1('Flushes to stable storage:',
- f_hits(zil_stats['zil_commit_writer_count']))
- prt_i2('Transactions to SLOG storage pool:',
- f_bytes(zil_stats['zil_itx_metaslab_slog_bytes']),
- f_hits(zil_stats['zil_itx_metaslab_slog_count']))
- prt_i2('Transactions to non-SLOG storage pool:',
- f_bytes(zil_stats['zil_itx_metaslab_normal_bytes']),
- f_hits(zil_stats['zil_itx_metaslab_normal_count']))
- print()
-
-
-section_calls = {'arc': section_arc,
- 'archits': section_archits,
- 'dmu': section_dmu,
- 'l2arc': section_l2arc,
- 'spl': section_spl,
- 'tunables': section_tunables,
- 'vdev': section_vdev,
- 'zil': section_zil}
-
-
-def main():
- """Run program. The options to draw a graph and to print all data raw are
- treated separately because they come with their own call.
- """
-
- kstats = get_kstats()
-
- if ARGS.graph:
- draw_graph(kstats)
- sys.exit(0)
-
- print_header()
-
- if ARGS.raw:
- print_raw(kstats)
-
- elif ARGS.section:
-
- try:
- section_calls[ARGS.section](kstats)
- except KeyError:
- print('Error: Section "{0}" unknown'.format(ARGS.section))
- sys.exit(1)
-
- elif ARGS.page:
- print('WARNING: Pages are deprecated, please use "--section"\n')
-
- pages_to_calls = {1: 'arc',
- 2: 'archits',
- 3: 'l2arc',
- 4: 'dmu',
- 5: 'vdev',
- 6: 'tunables'}
-
- try:
- call = pages_to_calls[ARGS.page]
- except KeyError:
- print('Error: Page "{0}" not supported'.format(ARGS.page))
- sys.exit(1)
- else:
- section_calls[call](kstats)
-
- else:
- # If no parameters were given, we print all sections. We might want to
- # change the sequence by hand
- calls = sorted(section_calls.keys())
-
- for section in calls:
- section_calls[section](kstats)
-
- sys.exit(0)
-
-
-if __name__ == '__main__':
- main()