3 # $Id: arc_summary.pl,v 388:e27800740aa2 2011-07-08 02:53:29Z jhell $
5 # Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>,
6 # Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>,
7 # Copyright (c) 2010-2011 Jason J. Hellenthal <jhell@DataIX.net>,
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions
14 # 1. Redistributions of source code must retain the above copyright
15 # notice, this list of conditions and the following disclaimer.
16 # 2. Redistributions in binary form must reproduce the above copyright
17 # notice, this list of conditions and the following disclaimer in the
18 # documentation and/or other materials provided with the distribution.
20 # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 # If you are having troubles when using this script from cron(8) please try
33 # adjusting your PATH before reporting problems.
35 # Note some of this code uses older code (eg getopt instead of argparse,
36 # subprocess.Popen() instead of subprocess.run()) because we need to support
37 # some very old versions of Python.
40 """Print statistics on the ZFS Adjustable Replacement Cache (ARC)
42 Provides basic information on the ARC, its efficiency, the L2ARC (if present),
43 the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the
44 in-source documentation and code at
45 https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
54 from subprocess import Popen, PIPE
55 from decimal import Decimal as D
58 if sys.platform.startswith('freebsd'):
59 # Requires py27-sysctl on FreeBSD
62 def load_kstats(namespace):
63 """Collect information on a specific subsystem of the ARC"""
65 base = 'kstat.zfs.misc.%s.' % namespace
66 return [(kstat.name, D(kstat.value)) for kstat in sysctl.filter(base)]
69 return dict((ctl.name, ctl.value) for ctl in sysctl.filter('vfs.zfs'))
71 elif sys.platform.startswith('linux'):
73 def load_kstats(namespace):
74 """Collect information on a specific subsystem of the ARC"""
76 kstat = 'kstat.zfs.misc.%s.%%s' % namespace
77 path = '/proc/spl/kstat/zfs/%s' % namespace
79 entries = [line.strip().split() for line in f][2:] # Skip header
80 return [(kstat % name, D(value)) for name, _, value in entries]
83 basepath = '/sys/module/zfs/parameters'
85 for name in os.listdir(basepath):
88 path = '%s/%s' % (basepath, name)
91 tunables[name] = value.strip()
95 show_tunable_descriptions = False
96 alternate_tunable_layout = False
99 def handle_Exception(ex_cls, ex, tb):
101 if ex.errno == errno.EPIPE:
104 if ex is KeyboardInterrupt:
108 sys.excepthook = handle_Exception
112 """Collect information on the ZFS subsystem from the /proc virtual
113 file system. The name "kstat" is a holdover from the Solaris utility
118 Kstat.update(load_kstats('arcstats'))
119 Kstat.update(load_kstats('zfetchstats'))
120 Kstat.update(load_kstats('vdev_cache_stats'))
125 """Return human-readable representation of a byte value in
126 powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
127 points. Values smaller than one KiB are returned without
132 [2**80, "YiB"], # yobibytes (yotta)
133 [2**70, "ZiB"], # zebibytes (zetta)
134 [2**60, "EiB"], # exbibytes (exa)
135 [2**50, "PiB"], # pebibytes (peta)
136 [2**40, "TiB"], # tebibytes (tera)
137 [2**30, "GiB"], # gibibytes (giga)
138 [2**20, "MiB"], # mebibytes (mega)
139 [2**10, "KiB"]] # kibibytes (kilo)
143 for limit, unit in prefixes:
149 result = "%0.2f\t%s" % (value, unit)
153 result = "%d\tBytes" % b
159 """Create a human-readable representation of the number of hits.
160 The single-letter symbols used are SI to avoid the confusion caused
161 by the different "short scale" and "long scale" representations in
162 English, which use the same words for different values. See
163 https://en.wikipedia.org/wiki/Names_of_large_numbers and
164 https://physics.nist.gov/cuu/Units/prefixes.html
168 [10**24, 'Y'], # yotta (septillion)
169 [10**21, 'Z'], # zetta (sextillion)
170 [10**18, 'E'], # exa (quintrillion)
171 [10**15, 'P'], # peta (quadrillion)
172 [10**12, 'T'], # tera (trillion)
173 [10**9, 'G'], # giga (billion)
174 [10**6, 'M'], # mega (million)
175 [10**3, 'k']] # kilo (thousand)
179 for limit, symbol in numbers:
185 result = "%0.2f%s" % (value, symbol)
194 def fPerc(lVal=0, rVal=0, Decimal=2):
195 """Calculate percentage value and return in human-readable format"""
198 return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%"
200 return str("%0." + str(Decimal) + "f") % 100 + "%"
203 def get_arc_summary(Kstat):
204 """Collect general data on the ARC"""
207 memory_throttle_count = Kstat[
208 "kstat.zfs.misc.arcstats.memory_throttle_count"
211 if memory_throttle_count > 0:
212 output['health'] = 'THROTTLED'
214 output['health'] = 'HEALTHY'
216 output['memory_throttle_count'] = fHits(memory_throttle_count)
219 deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
220 mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
221 evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"]
224 output["arc_misc"] = {}
225 output["arc_misc"]["deleted"] = fHits(deleted)
226 output["arc_misc"]['mutex_miss'] = fHits(mutex_miss)
227 output["arc_misc"]['evict_skips'] = fHits(evict_skip)
230 arc_size = Kstat["kstat.zfs.misc.arcstats.size"]
231 mru_size = Kstat["kstat.zfs.misc.arcstats.mru_size"]
232 mfu_size = Kstat["kstat.zfs.misc.arcstats.mfu_size"]
233 meta_limit = Kstat["kstat.zfs.misc.arcstats.arc_meta_limit"]
234 meta_size = Kstat["kstat.zfs.misc.arcstats.arc_meta_used"]
235 dnode_limit = Kstat["kstat.zfs.misc.arcstats.arc_dnode_limit"]
236 dnode_size = Kstat["kstat.zfs.misc.arcstats.dnode_size"]
237 target_max_size = Kstat["kstat.zfs.misc.arcstats.c_max"]
238 target_min_size = Kstat["kstat.zfs.misc.arcstats.c_min"]
239 target_size = Kstat["kstat.zfs.misc.arcstats.c"]
241 target_size_ratio = (target_max_size / target_min_size)
244 output['arc_sizing'] = {}
245 output['arc_sizing']['arc_size'] = {
246 'per': fPerc(arc_size, target_max_size),
247 'num': fBytes(arc_size),
249 output['arc_sizing']['target_max_size'] = {
250 'ratio': target_size_ratio,
251 'num': fBytes(target_max_size),
253 output['arc_sizing']['target_min_size'] = {
254 'per': fPerc(target_min_size, target_max_size),
255 'num': fBytes(target_min_size),
257 output['arc_sizing']['target_size'] = {
258 'per': fPerc(target_size, target_max_size),
259 'num': fBytes(target_size),
261 output['arc_sizing']['meta_limit'] = {
262 'per': fPerc(meta_limit, target_max_size),
263 'num': fBytes(meta_limit),
265 output['arc_sizing']['meta_size'] = {
266 'per': fPerc(meta_size, meta_limit),
267 'num': fBytes(meta_size),
269 output['arc_sizing']['dnode_limit'] = {
270 'per': fPerc(dnode_limit, meta_limit),
271 'num': fBytes(dnode_limit),
273 output['arc_sizing']['dnode_size'] = {
274 'per': fPerc(dnode_size, dnode_limit),
275 'num': fBytes(dnode_size),
279 output['arc_hash_break'] = {}
280 output['arc_hash_break']['hash_chain_max'] = Kstat[
281 "kstat.zfs.misc.arcstats.hash_chain_max"
283 output['arc_hash_break']['hash_chains'] = Kstat[
284 "kstat.zfs.misc.arcstats.hash_chains"
286 output['arc_hash_break']['hash_collisions'] = Kstat[
287 "kstat.zfs.misc.arcstats.hash_collisions"
289 output['arc_hash_break']['hash_elements'] = Kstat[
290 "kstat.zfs.misc.arcstats.hash_elements"
292 output['arc_hash_break']['hash_elements_max'] = Kstat[
293 "kstat.zfs.misc.arcstats.hash_elements_max"
296 output['arc_size_break'] = {}
297 output['arc_size_break']['recently_used_cache_size'] = {
298 'per': fPerc(mru_size, mru_size + mfu_size),
299 'num': fBytes(mru_size),
301 output['arc_size_break']['frequently_used_cache_size'] = {
302 'per': fPerc(mfu_size, mru_size + mfu_size),
303 'num': fBytes(mfu_size),
307 hash_chain_max = Kstat["kstat.zfs.misc.arcstats.hash_chain_max"]
308 hash_chains = Kstat["kstat.zfs.misc.arcstats.hash_chains"]
309 hash_collisions = Kstat["kstat.zfs.misc.arcstats.hash_collisions"]
310 hash_elements = Kstat["kstat.zfs.misc.arcstats.hash_elements"]
311 hash_elements_max = Kstat["kstat.zfs.misc.arcstats.hash_elements_max"]
313 output['arc_hash_break'] = {}
314 output['arc_hash_break']['elements_max'] = fHits(hash_elements_max)
315 output['arc_hash_break']['elements_current'] = {
316 'per': fPerc(hash_elements, hash_elements_max),
317 'num': fHits(hash_elements),
319 output['arc_hash_break']['collisions'] = fHits(hash_collisions)
320 output['arc_hash_break']['chain_max'] = fHits(hash_chain_max)
321 output['arc_hash_break']['chains'] = fHits(hash_chains)
326 def _arc_summary(Kstat):
327 """Print information on the ARC"""
330 arc = get_arc_summary(Kstat)
332 sys.stdout.write("ARC Summary: (%s)\n" % arc['health'])
334 sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" %
335 arc['memory_throttle_count'])
336 sys.stdout.write("\n")
339 sys.stdout.write("ARC Misc:\n")
340 sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
341 sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
342 arc['arc_misc']['mutex_miss'])
343 sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" %
344 arc['arc_misc']['evict_skips'])
345 sys.stdout.write("\n")
348 sys.stdout.write("ARC Size:\t\t\t\t%s\t%s\n" % (
349 arc['arc_sizing']['arc_size']['per'],
350 arc['arc_sizing']['arc_size']['num']
353 sys.stdout.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % (
354 arc['arc_sizing']['target_size']['per'],
355 arc['arc_sizing']['target_size']['num'],
359 sys.stdout.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % (
360 arc['arc_sizing']['target_min_size']['per'],
361 arc['arc_sizing']['target_min_size']['num'],
365 sys.stdout.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % (
366 arc['arc_sizing']['target_max_size']['ratio'],
367 arc['arc_sizing']['target_max_size']['num'],
371 sys.stdout.write("\nARC Size Breakdown:\n")
372 sys.stdout.write("\tRecently Used Cache Size:\t%s\t%s\n" % (
373 arc['arc_size_break']['recently_used_cache_size']['per'],
374 arc['arc_size_break']['recently_used_cache_size']['num'],
377 sys.stdout.write("\tFrequently Used Cache Size:\t%s\t%s\n" % (
378 arc['arc_size_break']['frequently_used_cache_size']['per'],
379 arc['arc_size_break']['frequently_used_cache_size']['num'],
382 sys.stdout.write("\tMetadata Size (Hard Limit):\t%s\t%s\n" % (
383 arc['arc_sizing']['meta_limit']['per'],
384 arc['arc_sizing']['meta_limit']['num'],
387 sys.stdout.write("\tMetadata Size:\t\t\t%s\t%s\n" % (
388 arc['arc_sizing']['meta_size']['per'],
389 arc['arc_sizing']['meta_size']['num'],
392 sys.stdout.write("\tDnode Size (Hard Limit):\t%s\t%s\n" % (
393 arc['arc_sizing']['dnode_limit']['per'],
394 arc['arc_sizing']['dnode_limit']['num'],
397 sys.stdout.write("\tDnode Size:\t\t\t%s\t%s\n" % (
398 arc['arc_sizing']['dnode_size']['per'],
399 arc['arc_sizing']['dnode_size']['num'],
403 sys.stdout.write("\n")
406 sys.stdout.write("ARC Hash Breakdown:\n")
407 sys.stdout.write("\tElements Max:\t\t\t\t%s\n" %
408 arc['arc_hash_break']['elements_max'])
409 sys.stdout.write("\tElements Current:\t\t%s\t%s\n" % (
410 arc['arc_hash_break']['elements_current']['per'],
411 arc['arc_hash_break']['elements_current']['num'],
414 sys.stdout.write("\tCollisions:\t\t\t\t%s\n" %
415 arc['arc_hash_break']['collisions'])
416 sys.stdout.write("\tChain Max:\t\t\t\t%s\n" %
417 arc['arc_hash_break']['chain_max'])
418 sys.stdout.write("\tChains:\t\t\t\t\t%s\n" %
419 arc['arc_hash_break']['chains'])
422 def get_arc_efficiency(Kstat):
423 """Collect information on the efficiency of the ARC"""
427 arc_hits = Kstat["kstat.zfs.misc.arcstats.hits"]
428 arc_misses = Kstat["kstat.zfs.misc.arcstats.misses"]
429 demand_data_hits = Kstat["kstat.zfs.misc.arcstats.demand_data_hits"]
430 demand_data_misses = Kstat["kstat.zfs.misc.arcstats.demand_data_misses"]
431 demand_metadata_hits = Kstat[
432 "kstat.zfs.misc.arcstats.demand_metadata_hits"
434 demand_metadata_misses = Kstat[
435 "kstat.zfs.misc.arcstats.demand_metadata_misses"
437 mfu_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mfu_ghost_hits"]
438 mfu_hits = Kstat["kstat.zfs.misc.arcstats.mfu_hits"]
439 mru_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mru_ghost_hits"]
440 mru_hits = Kstat["kstat.zfs.misc.arcstats.mru_hits"]
441 prefetch_data_hits = Kstat["kstat.zfs.misc.arcstats.prefetch_data_hits"]
442 prefetch_data_misses = Kstat[
443 "kstat.zfs.misc.arcstats.prefetch_data_misses"
445 prefetch_metadata_hits = Kstat[
446 "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
448 prefetch_metadata_misses = Kstat[
449 "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
452 anon_hits = arc_hits - (
453 mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits
455 arc_accesses_total = (arc_hits + arc_misses)
456 demand_data_total = (demand_data_hits + demand_data_misses)
457 prefetch_data_total = (prefetch_data_hits + prefetch_data_misses)
458 real_hits = (mfu_hits + mru_hits)
460 output["total_accesses"] = fHits(arc_accesses_total)
461 output["cache_hit_ratio"] = {
462 'per': fPerc(arc_hits, arc_accesses_total),
463 'num': fHits(arc_hits),
465 output["cache_miss_ratio"] = {
466 'per': fPerc(arc_misses, arc_accesses_total),
467 'num': fHits(arc_misses),
469 output["actual_hit_ratio"] = {
470 'per': fPerc(real_hits, arc_accesses_total),
471 'num': fHits(real_hits),
473 output["data_demand_efficiency"] = {
474 'per': fPerc(demand_data_hits, demand_data_total),
475 'num': fHits(demand_data_total),
478 if prefetch_data_total > 0:
479 output["data_prefetch_efficiency"] = {
480 'per': fPerc(prefetch_data_hits, prefetch_data_total),
481 'num': fHits(prefetch_data_total),
485 output["cache_hits_by_cache_list"] = {}
486 output["cache_hits_by_cache_list"]["anonymously_used"] = {
487 'per': fPerc(anon_hits, arc_hits),
488 'num': fHits(anon_hits),
491 output["most_recently_used"] = {
492 'per': fPerc(mru_hits, arc_hits),
493 'num': fHits(mru_hits),
495 output["most_frequently_used"] = {
496 'per': fPerc(mfu_hits, arc_hits),
497 'num': fHits(mfu_hits),
499 output["most_recently_used_ghost"] = {
500 'per': fPerc(mru_ghost_hits, arc_hits),
501 'num': fHits(mru_ghost_hits),
503 output["most_frequently_used_ghost"] = {
504 'per': fPerc(mfu_ghost_hits, arc_hits),
505 'num': fHits(mfu_ghost_hits),
508 output["cache_hits_by_data_type"] = {}
509 output["cache_hits_by_data_type"]["demand_data"] = {
510 'per': fPerc(demand_data_hits, arc_hits),
511 'num': fHits(demand_data_hits),
513 output["cache_hits_by_data_type"]["prefetch_data"] = {
514 'per': fPerc(prefetch_data_hits, arc_hits),
515 'num': fHits(prefetch_data_hits),
517 output["cache_hits_by_data_type"]["demand_metadata"] = {
518 'per': fPerc(demand_metadata_hits, arc_hits),
519 'num': fHits(demand_metadata_hits),
521 output["cache_hits_by_data_type"]["prefetch_metadata"] = {
522 'per': fPerc(prefetch_metadata_hits, arc_hits),
523 'num': fHits(prefetch_metadata_hits),
526 output["cache_misses_by_data_type"] = {}
527 output["cache_misses_by_data_type"]["demand_data"] = {
528 'per': fPerc(demand_data_misses, arc_misses),
529 'num': fHits(demand_data_misses),
531 output["cache_misses_by_data_type"]["prefetch_data"] = {
532 'per': fPerc(prefetch_data_misses, arc_misses),
533 'num': fHits(prefetch_data_misses),
535 output["cache_misses_by_data_type"]["demand_metadata"] = {
536 'per': fPerc(demand_metadata_misses, arc_misses),
537 'num': fHits(demand_metadata_misses),
539 output["cache_misses_by_data_type"]["prefetch_metadata"] = {
540 'per': fPerc(prefetch_metadata_misses, arc_misses),
541 'num': fHits(prefetch_metadata_misses),
547 def _arc_efficiency(Kstat):
548 """Print information on the efficiency of the ARC"""
550 arc = get_arc_efficiency(Kstat)
552 sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" %
553 arc['total_accesses'])
554 sys.stdout.write("\tCache Hit Ratio:\t\t%s\t%s\n" % (
555 arc['cache_hit_ratio']['per'],
556 arc['cache_hit_ratio']['num'],
559 sys.stdout.write("\tCache Miss Ratio:\t\t%s\t%s\n" % (
560 arc['cache_miss_ratio']['per'],
561 arc['cache_miss_ratio']['num'],
565 sys.stdout.write("\tActual Hit Ratio:\t\t%s\t%s\n" % (
566 arc['actual_hit_ratio']['per'],
567 arc['actual_hit_ratio']['num'],
571 sys.stdout.write("\n")
572 sys.stdout.write("\tData Demand Efficiency:\t\t%s\t%s\n" % (
573 arc['data_demand_efficiency']['per'],
574 arc['data_demand_efficiency']['num'],
578 if 'data_prefetch_efficiency' in arc:
579 sys.stdout.write("\tData Prefetch Efficiency:\t%s\t%s\n" % (
580 arc['data_prefetch_efficiency']['per'],
581 arc['data_prefetch_efficiency']['num'],
584 sys.stdout.write("\n")
586 sys.stdout.write("\tCACHE HITS BY CACHE LIST:\n")
587 if 'cache_hits_by_cache_list' in arc:
588 sys.stdout.write("\t Anonymously Used:\t\t%s\t%s\n" % (
589 arc['cache_hits_by_cache_list']['anonymously_used']['per'],
590 arc['cache_hits_by_cache_list']['anonymously_used']['num'],
593 sys.stdout.write("\t Most Recently Used:\t\t%s\t%s\n" % (
594 arc['most_recently_used']['per'],
595 arc['most_recently_used']['num'],
598 sys.stdout.write("\t Most Frequently Used:\t\t%s\t%s\n" % (
599 arc['most_frequently_used']['per'],
600 arc['most_frequently_used']['num'],
603 sys.stdout.write("\t Most Recently Used Ghost:\t%s\t%s\n" % (
604 arc['most_recently_used_ghost']['per'],
605 arc['most_recently_used_ghost']['num'],
608 sys.stdout.write("\t Most Frequently Used Ghost:\t%s\t%s\n" % (
609 arc['most_frequently_used_ghost']['per'],
610 arc['most_frequently_used_ghost']['num'],
614 sys.stdout.write("\n\tCACHE HITS BY DATA TYPE:\n")
615 sys.stdout.write("\t Demand Data:\t\t\t%s\t%s\n" % (
616 arc["cache_hits_by_data_type"]['demand_data']['per'],
617 arc["cache_hits_by_data_type"]['demand_data']['num'],
620 sys.stdout.write("\t Prefetch Data:\t\t%s\t%s\n" % (
621 arc["cache_hits_by_data_type"]['prefetch_data']['per'],
622 arc["cache_hits_by_data_type"]['prefetch_data']['num'],
625 sys.stdout.write("\t Demand Metadata:\t\t%s\t%s\n" % (
626 arc["cache_hits_by_data_type"]['demand_metadata']['per'],
627 arc["cache_hits_by_data_type"]['demand_metadata']['num'],
630 sys.stdout.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
631 arc["cache_hits_by_data_type"]['prefetch_metadata']['per'],
632 arc["cache_hits_by_data_type"]['prefetch_metadata']['num'],
636 sys.stdout.write("\n\tCACHE MISSES BY DATA TYPE:\n")
637 sys.stdout.write("\t Demand Data:\t\t\t%s\t%s\n" % (
638 arc["cache_misses_by_data_type"]['demand_data']['per'],
639 arc["cache_misses_by_data_type"]['demand_data']['num'],
642 sys.stdout.write("\t Prefetch Data:\t\t%s\t%s\n" % (
643 arc["cache_misses_by_data_type"]['prefetch_data']['per'],
644 arc["cache_misses_by_data_type"]['prefetch_data']['num'],
647 sys.stdout.write("\t Demand Metadata:\t\t%s\t%s\n" % (
648 arc["cache_misses_by_data_type"]['demand_metadata']['per'],
649 arc["cache_misses_by_data_type"]['demand_metadata']['num'],
652 sys.stdout.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
653 arc["cache_misses_by_data_type"]['prefetch_metadata']['per'],
654 arc["cache_misses_by_data_type"]['prefetch_metadata']['num'],
659 def get_l2arc_summary(Kstat):
660 """Collection information on the L2ARC"""
664 l2_abort_lowmem = Kstat["kstat.zfs.misc.arcstats.l2_abort_lowmem"]
665 l2_cksum_bad = Kstat["kstat.zfs.misc.arcstats.l2_cksum_bad"]
666 l2_evict_lock_retry = Kstat["kstat.zfs.misc.arcstats.l2_evict_lock_retry"]
667 l2_evict_reading = Kstat["kstat.zfs.misc.arcstats.l2_evict_reading"]
668 l2_feeds = Kstat["kstat.zfs.misc.arcstats.l2_feeds"]
669 l2_free_on_write = Kstat["kstat.zfs.misc.arcstats.l2_free_on_write"]
670 l2_hdr_size = Kstat["kstat.zfs.misc.arcstats.l2_hdr_size"]
671 l2_hits = Kstat["kstat.zfs.misc.arcstats.l2_hits"]
672 l2_io_error = Kstat["kstat.zfs.misc.arcstats.l2_io_error"]
673 l2_misses = Kstat["kstat.zfs.misc.arcstats.l2_misses"]
674 l2_rw_clash = Kstat["kstat.zfs.misc.arcstats.l2_rw_clash"]
675 l2_size = Kstat["kstat.zfs.misc.arcstats.l2_size"]
676 l2_asize = Kstat["kstat.zfs.misc.arcstats.l2_asize"]
677 l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"]
678 l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"]
679 l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"]
681 l2_access_total = (l2_hits + l2_misses)
682 output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error)
684 output['l2_access_total'] = l2_access_total
685 output['l2_size'] = l2_size
686 output['l2_asize'] = l2_asize
688 if l2_size > 0 and l2_access_total > 0:
690 if output['l2_health_count'] > 0:
691 output["health"] = "DEGRADED"
693 output["health"] = "HEALTHY"
695 output["low_memory_aborts"] = fHits(l2_abort_lowmem)
696 output["free_on_write"] = fHits(l2_free_on_write)
697 output["rw_clashes"] = fHits(l2_rw_clash)
698 output["bad_checksums"] = fHits(l2_cksum_bad)
699 output["io_errors"] = fHits(l2_io_error)
701 output["l2_arc_size"] = {}
702 output["l2_arc_size"]["adative"] = fBytes(l2_size)
703 output["l2_arc_size"]["actual"] = {
704 'per': fPerc(l2_asize, l2_size),
705 'num': fBytes(l2_asize)
707 output["l2_arc_size"]["head_size"] = {
708 'per': fPerc(l2_hdr_size, l2_size),
709 'num': fBytes(l2_hdr_size),
712 output["l2_arc_evicts"] = {}
713 output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry)
714 output["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading)
716 output['l2_arc_breakdown'] = {}
717 output['l2_arc_breakdown']['value'] = fHits(l2_access_total)
718 output['l2_arc_breakdown']['hit_ratio'] = {
719 'per': fPerc(l2_hits, l2_access_total),
720 'num': fHits(l2_hits),
722 output['l2_arc_breakdown']['miss_ratio'] = {
723 'per': fPerc(l2_misses, l2_access_total),
724 'num': fHits(l2_misses),
726 output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds)
728 output['l2_arc_buffer'] = {}
730 output['l2_arc_writes'] = {}
731 output['l2_writes_done'] = l2_writes_done
732 output['l2_writes_sent'] = l2_writes_sent
733 if l2_writes_done != l2_writes_sent:
734 output['l2_arc_writes']['writes_sent'] = {
736 'num': fHits(l2_writes_sent),
738 output['l2_arc_writes']['done_ratio'] = {
739 'per': fPerc(l2_writes_done, l2_writes_sent),
740 'num': fHits(l2_writes_done),
742 output['l2_arc_writes']['error_ratio'] = {
743 'per': fPerc(l2_writes_error, l2_writes_sent),
744 'num': fHits(l2_writes_error),
747 output['l2_arc_writes']['writes_sent'] = {
749 'num': fHits(l2_writes_sent),
755 def _l2arc_summary(Kstat):
756 """Print information on the L2ARC"""
758 arc = get_l2arc_summary(Kstat)
760 if arc['l2_size'] > 0 and arc['l2_access_total'] > 0:
761 sys.stdout.write("L2 ARC Summary: ")
762 if arc['l2_health_count'] > 0:
763 sys.stdout.write("(DEGRADED)\n")
765 sys.stdout.write("(HEALTHY)\n")
766 sys.stdout.write("\tLow Memory Aborts:\t\t\t%s\n" %
767 arc['low_memory_aborts'])
768 sys.stdout.write("\tFree on Write:\t\t\t\t%s\n" % arc['free_on_write'])
769 sys.stdout.write("\tR/W Clashes:\t\t\t\t%s\n" % arc['rw_clashes'])
770 sys.stdout.write("\tBad Checksums:\t\t\t\t%s\n" % arc['bad_checksums'])
771 sys.stdout.write("\tIO Errors:\t\t\t\t%s\n" % arc['io_errors'])
772 sys.stdout.write("\n")
774 sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
775 arc["l2_arc_size"]["adative"])
776 sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % (
777 arc["l2_arc_size"]["actual"]["per"],
778 arc["l2_arc_size"]["actual"]["num"],
781 sys.stdout.write("\tHeader Size:\t\t\t%s\t%s\n" % (
782 arc["l2_arc_size"]["head_size"]["per"],
783 arc["l2_arc_size"]["head_size"]["num"],
786 sys.stdout.write("\n")
788 if arc["l2_arc_evicts"]['lock_retries'] != '0' or \
789 arc["l2_arc_evicts"]["reading"] != '0':
790 sys.stdout.write("L2 ARC Evicts:\n")
791 sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" %
792 arc["l2_arc_evicts"]['lock_retries'])
793 sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" %
794 arc["l2_arc_evicts"]["reading"])
795 sys.stdout.write("\n")
797 sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
798 arc['l2_arc_breakdown']['value'])
799 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
800 arc['l2_arc_breakdown']['hit_ratio']['per'],
801 arc['l2_arc_breakdown']['hit_ratio']['num'],
805 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
806 arc['l2_arc_breakdown']['miss_ratio']['per'],
807 arc['l2_arc_breakdown']['miss_ratio']['num'],
811 sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" %
812 arc['l2_arc_breakdown']['feeds'])
813 sys.stdout.write("\n")
815 sys.stdout.write("L2 ARC Writes:\n")
816 if arc['l2_writes_done'] != arc['l2_writes_sent']:
817 sys.stdout.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % (
818 arc['l2_arc_writes']['writes_sent']['value'],
819 arc['l2_arc_writes']['writes_sent']['num'],
822 sys.stdout.write("\t Done Ratio:\t\t\t%s\t%s\n" % (
823 arc['l2_arc_writes']['done_ratio']['per'],
824 arc['l2_arc_writes']['done_ratio']['num'],
827 sys.stdout.write("\t Error Ratio:\t\t\t%s\t%s\n" % (
828 arc['l2_arc_writes']['error_ratio']['per'],
829 arc['l2_arc_writes']['error_ratio']['num'],
833 sys.stdout.write("\tWrites Sent:\t\t\t%s\t%s\n" % (
834 arc['l2_arc_writes']['writes_sent']['per'],
835 arc['l2_arc_writes']['writes_sent']['num'],
840 def get_dmu_summary(Kstat):
841 """Collect information on the DMU"""
845 zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"]
846 zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"]
848 zfetch_access_total = (zfetch_hits + zfetch_misses)
849 output['zfetch_access_total'] = zfetch_access_total
851 if zfetch_access_total > 0:
853 output['dmu']['efficiency'] = {}
854 output['dmu']['efficiency']['value'] = fHits(zfetch_access_total)
855 output['dmu']['efficiency']['hit_ratio'] = {
856 'per': fPerc(zfetch_hits, zfetch_access_total),
857 'num': fHits(zfetch_hits),
859 output['dmu']['efficiency']['miss_ratio'] = {
860 'per': fPerc(zfetch_misses, zfetch_access_total),
861 'num': fHits(zfetch_misses),
867 def _dmu_summary(Kstat):
868 """Print information on the DMU"""
870 arc = get_dmu_summary(Kstat)
872 if arc['zfetch_access_total'] > 0:
873 sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
874 arc['dmu']['efficiency']['value'])
875 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
876 arc['dmu']['efficiency']['hit_ratio']['per'],
877 arc['dmu']['efficiency']['hit_ratio']['num'],
880 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
881 arc['dmu']['efficiency']['miss_ratio']['per'],
882 arc['dmu']['efficiency']['miss_ratio']['num'],
886 sys.stdout.write("\n")
889 def get_vdev_summary(Kstat):
890 """Collect information on the VDEVs"""
894 vdev_cache_delegations = \
895 Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"]
896 vdev_cache_misses = Kstat["kstat.zfs.misc.vdev_cache_stats.misses"]
897 vdev_cache_hits = Kstat["kstat.zfs.misc.vdev_cache_stats.hits"]
898 vdev_cache_total = (vdev_cache_misses + vdev_cache_hits +
899 vdev_cache_delegations)
901 output['vdev_cache_total'] = vdev_cache_total
903 if vdev_cache_total > 0:
904 output['summary'] = fHits(vdev_cache_total)
905 output['hit_ratio'] = {
906 'per': fPerc(vdev_cache_hits, vdev_cache_total),
907 'num': fHits(vdev_cache_hits),
909 output['miss_ratio'] = {
910 'per': fPerc(vdev_cache_misses, vdev_cache_total),
911 'num': fHits(vdev_cache_misses),
913 output['delegations'] = {
914 'per': fPerc(vdev_cache_delegations, vdev_cache_total),
915 'num': fHits(vdev_cache_delegations),
921 def _vdev_summary(Kstat):
922 """Print information on the VDEVs"""
924 arc = get_vdev_summary(Kstat)
926 if arc['vdev_cache_total'] > 0:
927 sys.stdout.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc['summary'])
928 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
929 arc['hit_ratio']['per'],
930 arc['hit_ratio']['num'],
932 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
933 arc['miss_ratio']['per'],
934 arc['miss_ratio']['num'],
936 sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % (
937 arc['delegations']['per'],
938 arc['delegations']['num'],
942 def _tunable_summary(Kstat):
943 """Print information on tunables, including descriptions if requested"""
945 global show_tunable_descriptions
946 global alternate_tunable_layout
948 tunables = load_tunables()
951 if show_tunable_descriptions:
953 command = ["/sbin/modinfo", "zfs", "-0"]
956 p = Popen(command, stdin=PIPE, stdout=PIPE,
957 stderr=PIPE, shell=False, close_fds=True)
960 # By default, Python 2 returns a string as the first element of the
961 # tuple from p.communicate(), while Python 3 returns bytes which
962 # must be decoded first. The better way to do this would be with
963 # subprocess.run() or at least .check_output(), but this fails on
964 # CentOS 6 because of its old version of Python 2
965 desc = bytes.decode(p.communicate()[0])
966 description_list = desc.strip().split('\0')
968 if p.returncode == 0:
969 for tunable in description_list:
970 if tunable[0:5] == 'parm:':
971 tunable = tunable[5:].strip()
972 name, description = tunable.split(':', 1)
974 description = "Description unavailable"
975 descriptions[name] = description
977 sys.stderr.write("%s: '%s' exited with code %i\n" %
978 (sys.argv[0], command[0], p.returncode))
979 sys.stderr.write("Tunable descriptions will be disabled.\n")
981 sys.stderr.write("%s: Cannot run '%s': %s\n" %
982 (sys.argv[0], command[0], e.strerror))
983 sys.stderr.write("Tunable descriptions will be disabled.\n")
985 sys.stdout.write("ZFS Tunables:\n")
987 if alternate_tunable_layout:
992 for name in sorted(tunables.keys()):
993 if show_tunable_descriptions and name in descriptions:
994 sys.stdout.write("\t# %s\n" % descriptions[name])
996 sys.stdout.write(fmt % (name, tunables[name]))
1010 """Print title string with date"""
1012 daydate = time.strftime('%a %b %d %H:%M:%S %Y')
1014 sys.stdout.write('\n'+'-'*72+'\n')
1015 sys.stdout.write('ZFS Subsystem Report\t\t\t\t%s' % daydate)
1016 sys.stdout.write('\n')
1020 """Print usage information"""
1022 sys.stdout.write("Usage: arc_summary [-h] [-a] [-d] [-p PAGE]\n\n")
1023 sys.stdout.write("\t -h, --help : "
1024 "Print this help message and exit\n")
1025 sys.stdout.write("\t -a, --alternate : "
1026 "Show an alternate sysctl layout\n")
1027 sys.stdout.write("\t -d, --description : "
1028 "Show the sysctl descriptions\n")
1029 sys.stdout.write("\t -p PAGE, --page=PAGE : "
1030 "Select a single output page to display,\n")
1031 sys.stdout.write("\t "
1032 "should be an integer between 1 and " +
1033 str(len(unSub)) + "\n\n")
1034 sys.stdout.write("Examples:\n")
1035 sys.stdout.write("\tarc_summary -a\n")
1036 sys.stdout.write("\tarc_summary -p 4\n")
1037 sys.stdout.write("\tarc_summary -ad\n")
1038 sys.stdout.write("\tarc_summary --page=2\n")
1044 global show_tunable_descriptions
1045 global alternate_tunable_layout
1048 opts, args = getopt.getopt(
1050 "adp:h", ["alternate", "description", "page=", "help"]
1052 except getopt.error as e:
1053 sys.stderr.write("Error: %s\n" % e.msg)
1058 for opt, arg in opts:
1059 if opt in ('-a', '--alternate'):
1061 if opt in ('-d', '--description'):
1063 if opt in ('-p', '--page'):
1065 if opt in ('-h', '--help'):
1071 alternate_tunable_layout = 'a' in args
1072 show_tunable_descriptions = 'd' in args
1078 pages.append(unSub[int(args['p']) - 1])
1080 sys.stderr.write('the argument to -p must be between 1 and ' +
1081 str(len(unSub)) + '\n')
1089 sys.stdout.write("\n")
1092 if __name__ == '__main__':