diff options
author | наб <[email protected]> | 2022-04-10 22:47:56 +0200 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2022-05-10 10:20:34 -0700 |
commit | 0a9aaa7f0cc16f1771f492cce665bc5c5eb8e735 (patch) | |
tree | 685a29bda113f8264935eb2d1e2238ef2de31765 /cmd/arc_summary/arc_summary3 | |
parent | eaf94bda6cc31eb11822be5c782b8b43279212ee (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-x | cmd/arc_summary/arc_summary3 | 978 |
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() |