]>
git.proxmox.com Git - mirror_zfs.git/blob - cmd/arc_summary/arc_summary3.py
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>
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions
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.
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
30 """Print statistics on the ZFS ARC Cache and other information
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/zfsonlinux/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
46 DECRIPTION
= 'Print ARC and other statistics for ZFS on Linux'
49 PROC_PATH
= '/proc/spl/kstat/zfs/'
50 SPL_PATH
= '/sys/module/spl/parameters/'
51 TUNABLES_PATH
= '/sys/module/zfs/parameters/'
52 DATE_FORMAT
= '%a %b %d %H:%M:%S %Y'
53 TITLE
= 'ZFS Subsystem Report'
55 SECTIONS
= 'arc archits dmu l2arc spl tunables vdev zil'.split()
56 SECTION_HELP
= 'print info from one section ('+' '.join(SECTIONS
)+')'
58 # Tunables and SPL are handled separately because they come from
60 SECTION_PATHS
= {'arc': 'arcstats',
62 'l2arc': 'arcstats', # L2ARC stuff lives in arcstats
63 'vdev': 'vdev_cache_stats',
65 'zfetch': 'zfetchstats',
68 parser
= argparse
.ArgumentParser(description
=DECRIPTION
)
69 parser
.add_argument('-a', '--alternate', action
='store_true', default
=False,
70 help='use alternate formatting for tunables and SPL',
72 parser
.add_argument('-d', '--description', action
='store_true', default
=False,
73 help='print descriptions with tunables and SPL',
75 parser
.add_argument('-g', '--graph', action
='store_true', default
=False,
76 help='print graph on ARC use and exit', dest
='graph')
77 parser
.add_argument('-p', '--page', type=int, dest
='page',
78 help='print page by number (DEPRECATED, use "-s")')
79 parser
.add_argument('-r', '--raw', action
='store_true', default
=False,
80 help='dump all available data with minimal formatting',
82 parser
.add_argument('-s', '--section', dest
='section', help=SECTION_HELP
)
83 ARGS
= parser
.parse_args()
86 def cleanup_line(single_line
):
87 """Format a raw line of data from /proc and isolate the name value
88 part, returning a tuple with each. Currently, this gets rid of the
89 middle '4'. For example "arc_no_grow 4 0" returns the tuple
92 name
, _
, value
= single_line
.split()
97 def draw_graph(kstats_dict
):
98 """Draw a primitive graph representing the basic information on the
99 ARC -- its size and the proportion used by MFU and MRU -- and quit.
100 We use max size of the ARC to calculate how full it is. This is a
101 very rough representation.
104 arc_stats
= isolate_section('arcstats', kstats_dict
)
108 arc_size
= f_bytes(arc_stats
['size'])
109 arc_perc
= f_perc(arc_stats
['size'], arc_stats
['c_max'])
110 mfu_size
= f_bytes(arc_stats
['mfu_size'])
111 mru_size
= f_bytes(arc_stats
['mru_size'])
113 info_form
= 'ARC: {0} ({1}) MFU: {2} MRU: {3}'
114 info_line
= info_form
.format(arc_size
, arc_perc
, mfu_size
, mru_size
)
115 info_spc
= ' '*int((GRAPH_WIDTH
-len(info_line
))/2)
116 info_line
= GRAPH_INDENT
+info_spc
+info_line
118 graph_line
= GRAPH_INDENT
+'+'+('-'*(GRAPH_WIDTH
-2))+'+'
120 mfu_perc
= float(int(arc_stats
['mfu_size'])/int(arc_stats
['c_max']))
121 mru_perc
= float(int(arc_stats
['mru_size'])/int(arc_stats
['c_max']))
122 arc_perc
= float(int(arc_stats
['size'])/int(arc_stats
['c_max']))
123 total_ticks
= float(arc_perc
)*GRAPH_WIDTH
124 mfu_ticks
= mfu_perc
*GRAPH_WIDTH
125 mru_ticks
= mru_perc
*GRAPH_WIDTH
126 other_ticks
= total_ticks
-(mfu_ticks
+mru_ticks
)
128 core_form
= 'F'*int(mfu_ticks
)+'R'*int(mru_ticks
)+'O'*int(other_ticks
)
129 core_spc
= ' '*(GRAPH_WIDTH
-(2+len(core_form
)))
130 core_line
= GRAPH_INDENT
+'|'+core_form
+core_spc
+'|'
132 for line
in ('', info_line
, graph_line
, core_line
, graph_line
, ''):
136 def f_bytes(byte_string
):
137 """Return human-readable representation of a byte value in
138 powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
139 points. Values smaller than one KiB are returned without
140 decimal points. Note "bytes" is a reserved keyword.
143 prefixes
= ([2**80, "YiB"], # yobibytes (yotta)
144 [2**70, "ZiB"], # zebibytes (zetta)
145 [2**60, "EiB"], # exbibytes (exa)
146 [2**50, "PiB"], # pebibytes (peta)
147 [2**40, "TiB"], # tebibytes (tera)
148 [2**30, "GiB"], # gibibytes (giga)
149 [2**20, "MiB"], # mebibytes (mega)
150 [2**10, "KiB"]) # kibibytes (kilo)
152 bites
= int(byte_string
)
155 for limit
, unit
in prefixes
:
158 value
= bites
/ limit
161 result
= '{0:.1f} {1}'.format(value
, unit
)
163 result
= '{0} Bytes'.format(bites
)
168 def f_hits(hits_string
):
169 """Create a human-readable representation of the number of hits.
170 The single-letter symbols used are SI to avoid the confusion caused
171 by the different "short scale" and "long scale" representations in
172 English, which use the same words for different values. See
173 https://en.wikipedia.org/wiki/Names_of_large_numbers and:
174 https://physics.nist.gov/cuu/Units/prefixes.html
177 numbers
= ([10**24, 'Y'], # yotta (septillion)
178 [10**21, 'Z'], # zetta (sextillion)
179 [10**18, 'E'], # exa (quintrillion)
180 [10**15, 'P'], # peta (quadrillion)
181 [10**12, 'T'], # tera (trillion)
182 [10**9, 'G'], # giga (billion)
183 [10**6, 'M'], # mega (million)
184 [10**3, 'k']) # kilo (thousand)
186 hits
= int(hits_string
)
189 for limit
, symbol
in numbers
:
195 result
= "%0.1f%s" % (value
, symbol
)
202 def f_perc(value1
, value2
):
203 """Calculate percentage and return in human-readable form. If
204 rounding produces the result '0.0' though the first number is
205 not zero, include a 'less-than' symbol to avoid confusion.
206 Division by zero is handled by returning 'n/a'; no error
215 except ZeroDivisionError:
218 result
= '{0:0.1f} %'.format(perc
)
220 if result
== '0.0 %' and v1
> 0:
226 def format_raw_line(name
, value
):
227 """For the --raw option for the tunable and SPL outputs, decide on the
228 correct formatting based on the --alternate flag.
232 result
= '{0}{1}={2}'.format(INDENT
, name
, value
)
234 spc
= LINE_LENGTH
-(len(INDENT
)+len(value
))
235 result
= '{0}{1:<{spc}}{2}'.format(INDENT
, name
, value
, spc
=spc
)
241 """Collect information on the ZFS subsystem from the /proc Linux virtual
242 file system. The step does not perform any further processing, giving us
243 the option to only work on what is actually needed. The name "kstat" is a
244 holdover from the Solaris utility of the same name.
248 secs
= SECTION_PATHS
.values()
252 with
open(PROC_PATH
+section
, 'r') as proc_location
:
253 lines
= [line
for line
in proc_location
]
255 del lines
[0:2] # Get rid of header
256 result
[section
] = lines
261 def get_spl_tunables(PATH
):
262 """Collect information on the Solaris Porting Layer (SPL) or the
263 tunables, depending on the PATH given. Does not check if PATH is
268 parameters
= os
.listdir(PATH
)
270 for name
in parameters
:
272 with
open(PATH
+name
, 'r') as para_file
:
273 value
= para_file
.read()
274 result
[name
] = value
.strip()
279 def get_descriptions(request
):
280 """Get the decriptions of the Solaris Porting Layer (SPL) or the
281 tunables, return with minimal formatting.
284 if request
not in ('spl', 'zfs'):
285 print('ERROR: description of "{0}" requested)'.format(request
))
289 target_prefix
= 'parm:'
291 # We would prefer to do this with /sys/modules -- see the discussion at
292 # get_version() -- but there isn't a way to get the descriptions from
293 # there, so we fall back on modinfo
294 command
= ["/sbin/modinfo", request
, "-0"]
296 # The recommended way to do this is with subprocess.run(). However,
297 # some installed versions of Python are < 3.5, so we offer them
298 # the option of doing it the old way (for now)
303 if 'run' in dir(subprocess
):
304 info
= subprocess
.run(command
, stdout
=subprocess
.PIPE
,
305 universal_newlines
=True)
306 raw_output
= info
.stdout
.split('\0')
308 info
= subprocess
.check_output(command
, universal_newlines
=True)
309 raw_output
= info
.split('\0')
311 except subprocess
.CalledProcessError
:
312 print("Error: Descriptions not available (can't access kernel module)")
315 for line
in raw_output
:
317 if not line
.startswith(target_prefix
):
320 line
= line
[len(target_prefix
):].strip()
321 name
, raw_desc
= line
.split(':', 1)
322 desc
= raw_desc
.rsplit('(', 1)[0]
325 desc
= '(No description found)'
327 descs
[name
.strip()] = desc
.strip()
332 def get_version(request
):
333 """Get the version number of ZFS or SPL on this machine for header.
334 Returns an error string, but does not raise an error, if we can't
335 get the ZFS/SPL version via modinfo.
338 if request
not in ('spl', 'zfs'):
339 error_msg
= '(ERROR: "{0}" requested)'.format(request
)
342 # The original arc_summary.py called /sbin/modinfo/{spl,zfs} to get
343 # the version information. We switch to /sys/module/{spl,zfs}/version
344 # to make sure we get what is really loaded in the kernel
345 command
= ["cat", "/sys/module/{0}/version".format(request
)]
346 req
= request
.upper()
347 version
= "(Can't get {0} version)".format(req
)
349 # The recommended way to do this is with subprocess.run(). However,
350 # some installed versions of Python are < 3.5, so we offer them
351 # the option of doing it the old way (for now)
353 if 'run' in dir(subprocess
):
354 info
= subprocess
.run(command
, stdout
=subprocess
.PIPE
,
355 universal_newlines
=True)
356 version
= info
.stdout
.strip()
358 info
= subprocess
.check_output(command
, universal_newlines
=True)
359 version
= info
.strip()
365 """Print the initial heading with date and time as well as info on the
366 Linux and ZFS versions. This is not called for the graph.
369 # datetime is now recommended over time but we keep the exact formatting
370 # from the older version of arc_summary.py in case there are scripts
371 # that expect it in this way
372 daydate
= time
.strftime(DATE_FORMAT
)
373 spc_date
= LINE_LENGTH
-len(daydate
)
374 sys_version
= os
.uname()
376 sys_msg
= sys_version
.sysname
+' '+sys_version
.release
377 zfs
= get_version('zfs')
378 spc_zfs
= LINE_LENGTH
-len(zfs
)
380 machine_msg
= 'Machine: '+sys_version
.nodename
+' ('+sys_version
.machine
+')'
381 spl
= get_version('spl')
382 spc_spl
= LINE_LENGTH
-len(spl
)
384 print('\n'+('-'*LINE_LENGTH
))
385 print('{0:<{spc}}{1}'.format(TITLE
, daydate
, spc
=spc_date
))
386 print('{0:<{spc}}{1}'.format(sys_msg
, zfs
, spc
=spc_zfs
))
387 print('{0:<{spc}}{1}\n'.format(machine_msg
, spl
, spc
=spc_spl
))
390 def print_raw(kstats_dict
):
391 """Print all available data from the system in a minimally sorted format.
392 This can be used as a source to be piped through 'grep'.
395 sections
= sorted(kstats_dict
.keys())
397 for section
in sections
:
399 print('\n{0}:'.format(section
.upper()))
400 lines
= sorted(kstats_dict
[section
])
403 name
, value
= cleanup_line(line
)
404 print(format_raw_line(name
, value
))
406 # Tunables and SPL must be handled separately because they come from a
407 # different source and have descriptions the user might request
413 def isolate_section(section_name
, kstats_dict
):
414 """From the complete information on all sections, retrieve only those
419 section_data
= kstats_dict
[section_name
]
421 print('ERROR: Data on {0} not available'.format(section_data
))
424 section_dict
= dict(cleanup_line(l
) for l
in section_data
)
429 # Formatted output helper functions
432 def prt_1(text
, value
):
433 """Print text and one value, no indent"""
434 spc
= ' '*(LINE_LENGTH
-(len(text
)+len(value
)))
435 print('{0}{spc}{1}'.format(text
, value
, spc
=spc
))
438 def prt_i1(text
, value
):
439 """Print text and one value, with indent"""
440 spc
= ' '*(LINE_LENGTH
-(len(INDENT
)+len(text
)+len(value
)))
441 print(INDENT
+'{0}{spc}{1}'.format(text
, value
, spc
=spc
))
444 def prt_2(text
, value1
, value2
):
445 """Print text and two values, no indent"""
446 values
= '{0:>9} {1:>9}'.format(value1
, value2
)
447 spc
= ' '*(LINE_LENGTH
-(len(text
)+len(values
)+2))
448 print('{0}{spc} {1}'.format(text
, values
, spc
=spc
))
451 def prt_i2(text
, value1
, value2
):
452 """Print text and two values, with indent"""
453 values
= '{0:>9} {1:>9}'.format(value1
, value2
)
454 spc
= ' '*(LINE_LENGTH
-(len(INDENT
)+len(text
)+len(values
)+2))
455 print(INDENT
+'{0}{spc} {1}'.format(text
, values
, spc
=spc
))
458 # The section output concentrates on important parameters instead of
459 # being exhaustive (that is what the --raw parameter is for)
462 def section_arc(kstats_dict
):
463 """Give basic information on the ARC, MRU and MFU. This is the first
464 and most used section.
467 arc_stats
= isolate_section('arcstats', kstats_dict
)
469 throttle
= arc_stats
['memory_throttle_count']
476 prt_1('ARC status:', health
)
477 prt_i1('Memory throttle count:', throttle
)
480 arc_size
= arc_stats
['size']
481 arc_target_size
= arc_stats
['c']
482 arc_max
= arc_stats
['c_max']
483 arc_min
= arc_stats
['c_min']
484 mfu_size
= arc_stats
['mfu_size']
485 mru_size
= arc_stats
['mru_size']
486 target_size_ratio
= '{0}:1'.format(int(arc_max
) // int(arc_min
))
488 prt_2('ARC size (current):',
489 f_perc(arc_size
, arc_max
), f_bytes(arc_size
))
490 prt_i2('Target size (adaptive):',
491 f_perc(arc_target_size
, arc_max
), f_bytes(arc_target_size
))
492 prt_i2('Min size (hard limit):',
493 f_perc(arc_min
, arc_max
), f_bytes(arc_min
))
494 prt_i2('Max size (high water):',
495 target_size_ratio
, f_bytes(arc_max
))
496 caches_size
= int(mfu_size
)+int(mru_size
)
497 prt_i2('Most Frequently Used (MFU) cache size:',
498 f_perc(mfu_size
, caches_size
), f_bytes(mfu_size
))
499 prt_i2('Most Recently Used (MRU) cache size:',
500 f_perc(mru_size
, caches_size
), f_bytes(mru_size
))
503 print('ARC hash breakdown:')
504 prt_i1('Elements max:', f_hits(arc_stats
['hash_elements_max']))
505 prt_i2('Elements current:',
506 f_perc(arc_stats
['hash_elements'], arc_stats
['hash_elements_max']),
507 f_hits(arc_stats
['hash_elements']))
508 prt_i1('Collisions:', f_hits(arc_stats
['hash_collisions']))
510 prt_i1('Chain max:', f_hits(arc_stats
['hash_chain_max']))
511 prt_i1('Chains:', f_hits(arc_stats
['hash_chains']))
515 prt_i1('Deleted:', f_hits(arc_stats
['deleted']))
516 prt_i1('Mutex misses:', f_hits(arc_stats
['mutex_miss']))
517 prt_i1('Eviction skips:', f_hits(arc_stats
['evict_skip']))
521 def section_archits(kstats_dict
):
522 """Print information on how the caches are accessed ("arc hits").
525 arc_stats
= isolate_section('arcstats', kstats_dict
)
526 all_accesses
= int(arc_stats
['hits'])+int(arc_stats
['misses'])
527 actual_hits
= int(arc_stats
['mfu_hits'])+int(arc_stats
['mru_hits'])
529 prt_1('ARC total accesses (hits + misses):', f_hits(all_accesses
))
530 ta_todo
= (('Cache hit ratio:', arc_stats
['hits']),
531 ('Cache miss ratio:', arc_stats
['misses']),
532 ('Actual hit ratio (MFU + MRU hits):', actual_hits
))
534 for title
, value
in ta_todo
:
535 prt_i2(title
, f_perc(value
, all_accesses
), f_hits(value
))
537 dd_total
= int(arc_stats
['demand_data_hits']) +\
538 int(arc_stats
['demand_data_misses'])
539 prt_i2('Data demand efficiency:',
540 f_perc(arc_stats
['demand_data_hits'], dd_total
),
543 dp_total
= int(arc_stats
['prefetch_data_hits']) +\
544 int(arc_stats
['prefetch_data_misses'])
545 prt_i2('Data prefetch efficiency:',
546 f_perc(arc_stats
['prefetch_data_hits'], dp_total
),
549 known_hits
= int(arc_stats
['mfu_hits']) +\
550 int(arc_stats
['mru_hits']) +\
551 int(arc_stats
['mfu_ghost_hits']) +\
552 int(arc_stats
['mru_ghost_hits'])
554 anon_hits
= int(arc_stats
['hits'])-known_hits
557 print('Cache hits by cache type:')
558 cl_todo
= (('Most frequently used (MFU):', arc_stats
['mfu_hits']),
559 ('Most recently used (MRU):', arc_stats
['mru_hits']),
560 ('Most frequently used (MFU) ghost:',
561 arc_stats
['mfu_ghost_hits']),
562 ('Most recently used (MRU) ghost:',
563 arc_stats
['mru_ghost_hits']))
565 for title
, value
in cl_todo
:
566 prt_i2(title
, f_perc(value
, arc_stats
['hits']), f_hits(value
))
568 # For some reason, anon_hits can turn negative, which is weird. Until we
569 # have figured out why this happens, we just hide the problem, following
570 # the behavior of the original arc_summary.py
572 prt_i2('Anonymously used:',
573 f_perc(anon_hits
, arc_stats
['hits']), f_hits(anon_hits
))
576 print('Cache hits by data type:')
577 dt_todo
= (('Demand data:', arc_stats
['demand_data_hits']),
578 ('Demand perfetch data:', arc_stats
['prefetch_data_hits']),
579 ('Demand metadata:', arc_stats
['demand_metadata_hits']),
580 ('Demand prefetch metadata:',
581 arc_stats
['prefetch_metadata_hits']))
583 for title
, value
in dt_todo
:
584 prt_i2(title
, f_perc(value
, arc_stats
['hits']), f_hits(value
))
587 print('Cache misses by data type:')
588 dm_todo
= (('Demand data:', arc_stats
['demand_data_misses']),
589 ('Demand prefetch data:',
590 arc_stats
['prefetch_data_misses']),
591 ('Demand metadata:', arc_stats
['demand_metadata_misses']),
592 ('Demand prefetch metadata:',
593 arc_stats
['prefetch_metadata_misses']))
595 for title
, value
in dm_todo
:
596 prt_i2(title
, f_perc(value
, arc_stats
['misses']), f_hits(value
))
601 def section_dmu(kstats_dict
):
602 """Collect information on the DMU"""
604 zfetch_stats
= isolate_section('zfetchstats', kstats_dict
)
606 zfetch_access_total
= int(zfetch_stats
['hits'])+int(zfetch_stats
['misses'])
608 prt_1('DMU prefetch efficiency:', f_hits(zfetch_access_total
))
609 prt_i2('Hit ratio:', f_perc(zfetch_stats
['hits'], zfetch_access_total
),
610 f_hits(zfetch_stats
['hits']))
611 prt_i2('Miss ratio:', f_perc(zfetch_stats
['misses'], zfetch_access_total
),
612 f_hits(zfetch_stats
['misses']))
616 def section_l2arc(kstats_dict
):
617 """Collect information on L2ARC device if present. If not, tell user
618 that we're skipping the section.
621 # The L2ARC statistics live in the same section as the normal ARC stuff
622 arc_stats
= isolate_section('arcstats', kstats_dict
)
624 if arc_stats
['l2_size'] == '0':
625 print('L2ARC not detected, skipping section\n')
628 l2_errors
= int(arc_stats
['l2_writes_error']) +\
629 int(arc_stats
['l2_cksum_bad']) +\
630 int(arc_stats
['l2_io_error'])
632 l2_access_total
= int(arc_stats
['l2_hits'])+int(arc_stats
['l2_misses'])
638 prt_1('L2ARC status:', health
)
640 l2_todo
= (('Low memory aborts:', 'l2_abort_lowmem'),
641 ('Free on write:', 'l2_free_on_write'),
642 ('R/W clashes:', 'l2_rw_clash'),
643 ('Bad checksums:', 'l2_cksum_bad'),
644 ('I/O errors:', 'l2_io_error'))
646 for title
, value
in l2_todo
:
647 prt_i1(title
, f_hits(arc_stats
[value
]))
650 prt_1('L2ARC size (adaptive):', f_bytes(arc_stats
['l2_size']))
651 prt_i2('Compressed:', f_perc(arc_stats
['l2_asize'], arc_stats
['l2_size']),
652 f_bytes(arc_stats
['l2_asize']))
653 prt_i2('Header size:',
654 f_perc(arc_stats
['l2_hdr_size'], arc_stats
['l2_size']),
655 f_bytes(arc_stats
['l2_hdr_size']))
658 prt_1('L2ARC breakdown:', f_hits(l2_access_total
))
660 f_perc(arc_stats
['l2_hits'], l2_access_total
),
661 f_bytes(arc_stats
['l2_hits']))
662 prt_i2('Miss ratio:',
663 f_perc(arc_stats
['l2_misses'], l2_access_total
),
664 f_bytes(arc_stats
['l2_misses']))
665 prt_i1('Feeds:', f_hits(arc_stats
['l2_feeds']))
668 print('L2ARC writes:')
670 if arc_stats
['l2_writes_done'] != arc_stats
['l2_writes_sent']:
671 prt_i2('Writes sent:', 'FAULTED', f_hits(arc_stats
['l2_writes_sent']))
672 prt_i2('Done ratio:',
673 f_perc(arc_stats
['l2_writes_done'],
674 arc_stats
['l2_writes_sent']),
675 f_bytes(arc_stats
['l2_writes_done']))
676 prt_i2('Error ratio:',
677 f_perc(arc_stats
['l2_writes_error'],
678 arc_stats
['l2_writes_sent']),
679 f_bytes(arc_stats
['l2_writes_error']))
681 prt_i2('Writes sent:', '100 %', f_bytes(arc_stats
['l2_writes_sent']))
684 print('L2ARC evicts:')
685 prt_i1('Lock retries:', f_hits(arc_stats
['l2_evict_lock_retry']))
686 prt_i1('Upon reading:', f_hits(arc_stats
['l2_evict_reading']))
691 """Print the SPL parameters, if requested with alternative format
692 and/or decriptions. This does not use kstats.
695 spls
= get_spl_tunables(SPL_PATH
)
696 keylist
= sorted(spls
.keys())
697 print('Solaris Porting Layer (SPL):')
700 descriptions
= get_descriptions('spl')
707 print(INDENT
+'#', descriptions
[key
])
709 print(INDENT
+'# (No decription found)') # paranoid
711 print(format_raw_line(key
, value
))
716 def section_tunables(*_
):
717 """Print the tunables, if requested with alternative format and/or
718 decriptions. This does not use kstasts.
721 tunables
= get_spl_tunables(TUNABLES_PATH
)
722 keylist
= sorted(tunables
.keys())
726 descriptions
= get_descriptions('zfs')
729 value
= tunables
[key
]
733 print(INDENT
+'#', descriptions
[key
])
735 print(INDENT
+'# (No decription found)') # paranoid
737 print(format_raw_line(key
, value
))
742 def section_vdev(kstats_dict
):
743 """Collect information on VDEV caches"""
745 # Currently [Nov 2017] the VDEV cache is disabled, because it is actually
746 # harmful. When this is the case, we just skip the whole entry. See
747 # https://github.com/zfsonlinux/zfs/blob/master/module/zfs/vdev_cache.c
749 tunables
= get_spl_tunables(TUNABLES_PATH
)
751 if tunables
['zfs_vdev_cache_size'] == '0':
752 print('VDEV cache disabled, skipping section\n')
755 vdev_stats
= isolate_section('vdev_cache_stats', kstats_dict
)
757 vdev_cache_total
= int(vdev_stats
['hits']) +\
758 int(vdev_stats
['misses']) +\
759 int(vdev_stats
['delegations'])
761 prt_1('VDEV cache summary:', f_hits(vdev_cache_total
))
762 prt_i2('Hit ratio:', f_perc(vdev_stats
['hits'], vdev_cache_total
),
763 f_hits(vdev_stats
['hits']))
764 prt_i2('Miss ratio:', f_perc(vdev_stats
['misses'], vdev_cache_total
),
765 f_hits(vdev_stats
['misses']))
766 prt_i2('Delegations:', f_perc(vdev_stats
['delegations'], vdev_cache_total
),
767 f_hits(vdev_stats
['delegations']))
771 def section_zil(kstats_dict
):
772 """Collect information on the ZFS Intent Log. Some of the information
773 taken from https://github.com/zfsonlinux/zfs/blob/master/include/sys/zil.h
776 zil_stats
= isolate_section('zil', kstats_dict
)
778 prt_1('ZIL committed transactions:',
779 f_hits(zil_stats
['zil_itx_count']))
780 prt_i1('Commit requests:', f_hits(zil_stats
['zil_commit_count']))
781 prt_i1('Flushes to stable storage:',
782 f_hits(zil_stats
['zil_commit_writer_count']))
783 prt_i2('Transactions to SLOG storage pool:',
784 f_bytes(zil_stats
['zil_itx_metaslab_slog_bytes']),
785 f_hits(zil_stats
['zil_itx_metaslab_slog_count']))
786 prt_i2('Transactions to non-SLOG storage pool:',
787 f_bytes(zil_stats
['zil_itx_metaslab_normal_bytes']),
788 f_hits(zil_stats
['zil_itx_metaslab_normal_count']))
792 section_calls
= {'arc': section_arc
,
793 'archits': section_archits
,
795 'l2arc': section_l2arc
,
797 'tunables': section_tunables
,
798 'vdev': section_vdev
,
803 """Run program. The options to draw a graph and to print all data raw are
804 treated separately because they come with their own call.
807 kstats
= get_kstats()
821 section_calls
[ARGS
.section
](kstats
)
823 print('Error: Section "{0}" unknown'.format(ARGS
.section
))
827 print('WARNING: Pages are deprecated, please use "--section"\n')
829 pages_to_calls
= {1: 'arc',
837 call
= pages_to_calls
[ARGS
.page
]
839 print('Error: Page "{0}" not supported'.format(ARGS
.page
))
842 section_calls
[call
](kstats
)
845 # If no parameters were given, we print all sections. We might want to
846 # change the sequence by hand
847 calls
= sorted(section_calls
.keys())
849 for section
in calls
:
850 section_calls
[section
](kstats
)
855 if __name__
== '__main__':