aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/cmd/arc_summary
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/cmd/arc_summary')
-rwxr-xr-xsys/contrib/openzfs/cmd/arc_summary1070
1 files changed, 0 insertions, 1070 deletions
diff --git a/sys/contrib/openzfs/cmd/arc_summary b/sys/contrib/openzfs/cmd/arc_summary
deleted file mode 100755
index c1319573220c..000000000000
--- a/sys/contrib/openzfs/cmd/arc_summary
+++ /dev/null
@@ -1,1070 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>,
-# Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>,
-# Copyright (c) 2010-2011 Jason J. Hellenthal <jhell@DataIX.net>,
-# Copyright (c) 2017 Scot W. Stevenson <scot.stevenson@gmail.com>
-# 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
- '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
-
- 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'
-
- 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 = 70
- arc_max = int(arc_stats['c_max'])
- arc_size = f_bytes(arc_stats['size'])
- arc_perc = f_perc(arc_stats['size'], arc_max)
- data_size = f_bytes(arc_stats['data_size'])
- meta_size = f_bytes(arc_stats['metadata_size'])
- dnode_size = f_bytes(arc_stats['dnode_size'])
-
- info_form = ('ARC: {0} ({1}) Data: {2} Meta: {3} Dnode: {4}')
- info_line = info_form.format(arc_size, arc_perc, data_size, meta_size,
- dnode_size)
- info_spc = ' '*int((GRAPH_WIDTH-len(info_line))/2)
- info_line = GRAPH_INDENT+info_spc+info_line
-
- graph_line = GRAPH_INDENT+'+'+('-'*(GRAPH_WIDTH-2))+'+'
-
- arc_perc = float(int(arc_stats['size'])/arc_max)
- data_perc = float(int(arc_stats['data_size'])/arc_max)
- meta_perc = float(int(arc_stats['metadata_size'])/arc_max)
- dnode_perc = float(int(arc_stats['dnode_size'])/arc_max)
- total_ticks = float(arc_perc)*GRAPH_WIDTH
- data_ticks = data_perc*GRAPH_WIDTH
- meta_ticks = meta_perc*GRAPH_WIDTH
- dnode_ticks = dnode_perc*GRAPH_WIDTH
- other_ticks = total_ticks-(data_ticks+meta_ticks+dnode_ticks)
-
- core_form = 'D'*int(data_ticks)+'M'*int(meta_ticks)+'N'*int(dnode_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)
-
- memory_all = arc_stats['memory_all_bytes']
- memory_free = arc_stats['memory_free_bytes']
- memory_avail = arc_stats['memory_available_bytes']
- arc_size = arc_stats['size']
- arc_target_size = arc_stats['c']
- arc_max = arc_stats['c_max']
- arc_min = arc_stats['c_min']
- dnode_limit = arc_stats['arc_dnode_limit']
-
- print('ARC status:')
- prt_i1('Total memory size:', f_bytes(memory_all))
- prt_i2('Min target size:', f_perc(arc_min, memory_all), f_bytes(arc_min))
- prt_i2('Max target size:', f_perc(arc_max, memory_all), f_bytes(arc_max))
- prt_i2('Target size (adaptive):',
- f_perc(arc_size, arc_max), f_bytes(arc_target_size))
- prt_i2('Current size:', f_perc(arc_size, arc_max), f_bytes(arc_size))
- prt_i1('Free memory size:', f_bytes(memory_free))
- prt_i1('Available memory size:', f_bytes(memory_avail))
- print()
-
- compressed_size = arc_stats['compressed_size']
- overhead_size = arc_stats['overhead_size']
- bonus_size = arc_stats['bonus_size']
- dnode_size = arc_stats['dnode_size']
- dbuf_size = arc_stats['dbuf_size']
- hdr_size = arc_stats['hdr_size']
- l2_hdr_size = arc_stats['l2_hdr_size']
- abd_chunk_waste_size = arc_stats['abd_chunk_waste_size']
-
- prt_1('ARC structural breakdown (current size):', f_bytes(arc_size))
- prt_i2('Compressed size:',
- f_perc(compressed_size, arc_size), f_bytes(compressed_size))
- prt_i2('Overhead size:',
- f_perc(overhead_size, arc_size), f_bytes(overhead_size))
- prt_i2('Bonus size:',
- f_perc(bonus_size, arc_size), f_bytes(bonus_size))
- prt_i2('Dnode size:',
- f_perc(dnode_size, arc_size), f_bytes(dnode_size))
- prt_i2('Dbuf size:',
- f_perc(dbuf_size, arc_size), f_bytes(dbuf_size))
- prt_i2('Header size:',
- f_perc(hdr_size, arc_size), f_bytes(hdr_size))
- prt_i2('L2 header size:',
- f_perc(l2_hdr_size, arc_size), f_bytes(l2_hdr_size))
- prt_i2('ABD chunk waste size:',
- f_perc(abd_chunk_waste_size, arc_size), f_bytes(abd_chunk_waste_size))
- print()
-
- meta = arc_stats['meta']
- pd = arc_stats['pd']
- pm = arc_stats['pm']
- data_size = arc_stats['data_size']
- metadata_size = arc_stats['metadata_size']
- anon_data = arc_stats['anon_data']
- anon_metadata = arc_stats['anon_metadata']
- mfu_data = arc_stats['mfu_data']
- mfu_metadata = arc_stats['mfu_metadata']
- mfu_edata = arc_stats['mfu_evictable_data']
- mfu_emetadata = arc_stats['mfu_evictable_metadata']
- mru_data = arc_stats['mru_data']
- mru_metadata = arc_stats['mru_metadata']
- mru_edata = arc_stats['mru_evictable_data']
- mru_emetadata = arc_stats['mru_evictable_metadata']
- mfug_data = arc_stats['mfu_ghost_data']
- mfug_metadata = arc_stats['mfu_ghost_metadata']
- mrug_data = arc_stats['mru_ghost_data']
- mrug_metadata = arc_stats['mru_ghost_metadata']
- unc_data = arc_stats['uncached_data']
- unc_metadata = arc_stats['uncached_metadata']
- caches_size = int(anon_data)+int(anon_metadata)+\
- int(mfu_data)+int(mfu_metadata)+int(mru_data)+int(mru_metadata)+\
- int(unc_data)+int(unc_metadata)
-
- prt_1('ARC types breakdown (compressed + overhead):', f_bytes(caches_size))
- prt_i2('Data size:',
- f_perc(data_size, caches_size), f_bytes(data_size))
- prt_i2('Metadata size:',
- f_perc(metadata_size, caches_size), f_bytes(metadata_size))
- print()
-
- prt_1('ARC states breakdown (compressed + overhead):', f_bytes(caches_size))
- prt_i2('Anonymous data size:',
- f_perc(anon_data, caches_size), f_bytes(anon_data))
- prt_i2('Anonymous metadata size:',
- f_perc(anon_metadata, caches_size), f_bytes(anon_metadata))
- s = 4294967296
- v = (s-int(pd))*(s-int(meta))/s
- prt_i2('MFU data target:', f_perc(v, s),
- f_bytes(v / 65536 * caches_size / 65536))
- prt_i2('MFU data size:',
- f_perc(mfu_data, caches_size), f_bytes(mfu_data))
- prt_i2('MFU evictable data size:',
- f_perc(mfu_edata, caches_size), f_bytes(mfu_edata))
- prt_i1('MFU ghost data size:', f_bytes(mfug_data))
- v = (s-int(pm))*int(meta)/s
- prt_i2('MFU metadata target:', f_perc(v, s),
- f_bytes(v / 65536 * caches_size / 65536))
- prt_i2('MFU metadata size:',
- f_perc(mfu_metadata, caches_size), f_bytes(mfu_metadata))
- prt_i2('MFU evictable metadata size:',
- f_perc(mfu_emetadata, caches_size), f_bytes(mfu_emetadata))
- prt_i1('MFU ghost metadata size:', f_bytes(mfug_metadata))
- v = int(pd)*(s-int(meta))/s
- prt_i2('MRU data target:', f_perc(v, s),
- f_bytes(v / 65536 * caches_size / 65536))
- prt_i2('MRU data size:',
- f_perc(mru_data, caches_size), f_bytes(mru_data))
- prt_i2('MRU evictable data size:',
- f_perc(mru_edata, caches_size), f_bytes(mru_edata))
- prt_i1('MRU ghost data size:', f_bytes(mrug_data))
- v = int(pm)*int(meta)/s
- prt_i2('MRU metadata target:', f_perc(v, s),
- f_bytes(v / 65536 * caches_size / 65536))
- prt_i2('MRU metadata size:',
- f_perc(mru_metadata, caches_size), f_bytes(mru_metadata))
- prt_i2('MRU evictable metadata size:',
- f_perc(mru_emetadata, caches_size), f_bytes(mru_emetadata))
- prt_i1('MRU ghost metadata size:', f_bytes(mrug_metadata))
- prt_i2('Uncached data size:',
- f_perc(unc_data, caches_size), f_bytes(unc_data))
- prt_i2('Uncached metadata size:',
- f_perc(unc_metadata, caches_size), f_bytes(unc_metadata))
- print()
-
- print('ARC hash breakdown:')
- prt_i1('Elements:', 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('Memory throttles:', arc_stats['memory_throttle_count'])
- prt_i1('Memory direct reclaims:', arc_stats['memory_direct_count'])
- prt_i1('Memory indirect reclaims:', arc_stats['memory_indirect_count'])
- 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['iohits'])+\
- int(arc_stats['misses'])
-
- prt_1('ARC total accesses:', f_hits(all_accesses))
- ta_todo = (('Total hits:', arc_stats['hits']),
- ('Total I/O hits:', arc_stats['iohits']),
- ('Total misses:', arc_stats['misses']))
- for title, value in ta_todo:
- prt_i2(title, f_perc(value, all_accesses), f_hits(value))
- print()
-
- dd_total = int(arc_stats['demand_data_hits']) +\
- int(arc_stats['demand_data_iohits']) +\
- int(arc_stats['demand_data_misses'])
- prt_2('ARC demand data accesses:', f_perc(dd_total, all_accesses),
- f_hits(dd_total))
- dd_todo = (('Demand data hits:', arc_stats['demand_data_hits']),
- ('Demand data I/O hits:', arc_stats['demand_data_iohits']),
- ('Demand data misses:', arc_stats['demand_data_misses']))
- for title, value in dd_todo:
- prt_i2(title, f_perc(value, dd_total), f_hits(value))
- print()
-
- dm_total = int(arc_stats['demand_metadata_hits']) +\
- int(arc_stats['demand_metadata_iohits']) +\
- int(arc_stats['demand_metadata_misses'])
- prt_2('ARC demand metadata accesses:', f_perc(dm_total, all_accesses),
- f_hits(dm_total))
- dm_todo = (('Demand metadata hits:', arc_stats['demand_metadata_hits']),
- ('Demand metadata I/O hits:',
- arc_stats['demand_metadata_iohits']),
- ('Demand metadata misses:', arc_stats['demand_metadata_misses']))
- for title, value in dm_todo:
- prt_i2(title, f_perc(value, dm_total), f_hits(value))
- print()
-
- pd_total = int(arc_stats['prefetch_data_hits']) +\
- int(arc_stats['prefetch_data_iohits']) +\
- int(arc_stats['prefetch_data_misses'])
- prt_2('ARC prefetch data accesses:', f_perc(pd_total, all_accesses),
- f_hits(pd_total))
- pd_todo = (('Prefetch data hits:', arc_stats['prefetch_data_hits']),
- ('Prefetch data I/O hits:', arc_stats['prefetch_data_iohits']),
- ('Prefetch data misses:', arc_stats['prefetch_data_misses']))
- for title, value in pd_todo:
- prt_i2(title, f_perc(value, pd_total), f_hits(value))
- print()
-
- pm_total = int(arc_stats['prefetch_metadata_hits']) +\
- int(arc_stats['prefetch_metadata_iohits']) +\
- int(arc_stats['prefetch_metadata_misses'])
- prt_2('ARC prefetch metadata accesses:', f_perc(pm_total, all_accesses),
- f_hits(pm_total))
- pm_todo = (('Prefetch metadata hits:',
- arc_stats['prefetch_metadata_hits']),
- ('Prefetch metadata I/O hits:',
- arc_stats['prefetch_metadata_iohits']),
- ('Prefetch metadata misses:',
- arc_stats['prefetch_metadata_misses']))
- for title, value in pm_todo:
- prt_i2(title, f_perc(value, pm_total), f_hits(value))
- print()
-
- all_prefetches = int(arc_stats['predictive_prefetch'])+\
- int(arc_stats['prescient_prefetch'])
- prt_2('ARC predictive prefetches:',
- f_perc(arc_stats['predictive_prefetch'], all_prefetches),
- f_hits(arc_stats['predictive_prefetch']))
- prt_i2('Demand hits after predictive:',
- f_perc(arc_stats['demand_hit_predictive_prefetch'],
- arc_stats['predictive_prefetch']),
- f_hits(arc_stats['demand_hit_predictive_prefetch']))
- prt_i2('Demand I/O hits after predictive:',
- f_perc(arc_stats['demand_iohit_predictive_prefetch'],
- arc_stats['predictive_prefetch']),
- f_hits(arc_stats['demand_iohit_predictive_prefetch']))
- never = int(arc_stats['predictive_prefetch']) -\
- int(arc_stats['demand_hit_predictive_prefetch']) -\
- int(arc_stats['demand_iohit_predictive_prefetch'])
- prt_i2('Never demanded after predictive:',
- f_perc(never, arc_stats['predictive_prefetch']),
- f_hits(never))
- print()
-
- prt_2('ARC prescient prefetches:',
- f_perc(arc_stats['prescient_prefetch'], all_prefetches),
- f_hits(arc_stats['prescient_prefetch']))
- prt_i2('Demand hits after prescient:',
- f_perc(arc_stats['demand_hit_prescient_prefetch'],
- arc_stats['prescient_prefetch']),
- f_hits(arc_stats['demand_hit_prescient_prefetch']))
- prt_i2('Demand I/O hits after prescient:',
- f_perc(arc_stats['demand_iohit_prescient_prefetch'],
- arc_stats['prescient_prefetch']),
- f_hits(arc_stats['demand_iohit_prescient_prefetch']))
- never = int(arc_stats['prescient_prefetch'])-\
- int(arc_stats['demand_hit_prescient_prefetch'])-\
- int(arc_stats['demand_iohit_prescient_prefetch'])
- prt_i2('Never demanded after prescient:',
- f_perc(never, arc_stats['prescient_prefetch']),
- f_hits(never))
- print()
-
- print('ARC states hits of all accesses:')
- 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']),
- ('Uncached:', arc_stats['uncached_hits']))
- for title, value in cl_todo:
- prt_i2(title, f_perc(value, all_accesses), 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['future']) + int(zfetch_stats['stride']) +\
- int(zfetch_stats['past']) + int(zfetch_stats['misses'])
-
- prt_1('DMU predictive prefetcher calls:', f_hits(zfetch_access_total))
- prt_i2('Stream hits:',
- f_perc(zfetch_stats['hits'], zfetch_access_total),
- f_hits(zfetch_stats['hits']))
- future = int(zfetch_stats['future']) + int(zfetch_stats['stride'])
- prt_i2('Hits ahead of stream:', f_perc(future, zfetch_access_total),
- f_hits(future))
- prt_i2('Hits behind stream:',
- f_perc(zfetch_stats['past'], zfetch_access_total),
- f_hits(zfetch_stats['past']))
- prt_i2('Stream misses:',
- f_perc(zfetch_stats['misses'], zfetch_access_total),
- f_hits(zfetch_stats['misses']))
- prt_i2('Streams limit reached:',
- f_perc(zfetch_stats['max_streams'], zfetch_stats['misses']),
- f_hits(zfetch_stats['max_streams']))
- prt_i1('Stream strides:', f_hits(zfetch_stats['stride']))
- prt_i1('Prefetches issued', f_hits(zfetch_stats['io_issued']))
- 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'),
- ('Read errors:', 'l2_io_error'),
- ('Write errors:', 'l2_writes_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']))
-
- print()
- print('L2ARC I/O:')
- prt_i2('Reads:',
- f_bytes(arc_stats['l2_read_bytes']),
- f_hits(arc_stats['l2_hits']))
- prt_i2('Writes:',
- f_bytes(arc_stats['l2_write_bytes']),
- f_hits(arc_stats['l2_writes_sent']))
-
- print()
- print('L2ARC evicts:')
- prt_i1('L1 cached:', f_hits(arc_stats['l2_evict_l1cached']))
- prt_i1('While 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_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,
- '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()