The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/cmd/arc_summary

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 158e421d3df3b7d98b99c08e40b6d313


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.