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/zfsonlinux/zfs/blob/master/module/zfs/arc.c for details.
54 from subprocess import Popen, PIPE
55 from decimal import Decimal as D
58 show_tunable_descriptions = False
59 alternate_tunable_layout = False
62 def handle_Exception(ex_cls, ex, tb):
64 if ex.errno == errno.EPIPE:
67 if ex is KeyboardInterrupt:
71 sys.excepthook = handle_Exception
75 """Collect information on the ZFS subsystem from the /proc virtual
76 file system. The name "kstat" is a holdover from the Solaris utility
80 def load_kstats(namespace):
81 """Collect information on a specific subsystem of the ARC"""
83 kstat = 'kstat.zfs.misc.%s.%%s' % namespace
84 path = '/proc/spl/kstat/zfs/%s' % namespace
86 entries = [line.strip().split() for line in f][2:] # Skip header
87 return [(kstat % name, D(value)) for name, _, value in entries]
90 Kstat.update(load_kstats('arcstats'))
91 Kstat.update(load_kstats('zfetchstats'))
92 Kstat.update(load_kstats('vdev_cache_stats'))
97 """Return human-readable representation of a byte value in
98 powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
99 points. Values smaller than one KiB are returned without
104 [2**80, "YiB"], # yobibytes (yotta)
105 [2**70, "ZiB"], # zebibytes (zetta)
106 [2**60, "EiB"], # exbibytes (exa)
107 [2**50, "PiB"], # pebibytes (peta)
108 [2**40, "TiB"], # tebibytes (tera)
109 [2**30, "GiB"], # gibibytes (giga)
110 [2**20, "MiB"], # mebibytes (mega)
111 [2**10, "KiB"]] # kibibytes (kilo)
115 for limit, unit in prefixes:
121 result = "%0.2f\t%s" % (value, unit)
125 result = "%d\tBytes" % b
131 """Create a human-readable representation of the number of hits.
132 The single-letter symbols used are SI to avoid the confusion caused
133 by the different "short scale" and "long scale" representations in
134 English, which use the same words for different values. See
135 https://en.wikipedia.org/wiki/Names_of_large_numbers and
136 https://physics.nist.gov/cuu/Units/prefixes.html
140 [10**24, 'Y'], # yotta (septillion)
141 [10**21, 'Z'], # zetta (sextillion)
142 [10**18, 'E'], # exa (quintrillion)
143 [10**15, 'P'], # peta (quadrillion)
144 [10**12, 'T'], # tera (trillion)
145 [10**9, 'G'], # giga (billion)
146 [10**6, 'M'], # mega (million)
147 [10**3, 'k']] # kilo (thousand)
151 for limit, symbol in numbers:
157 result = "%0.2f%s" % (value, symbol)
166 def fPerc(lVal=0, rVal=0, Decimal=2):
167 """Calculate percentage value and return in human-readable format"""
170 return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%"
172 return str("%0." + str(Decimal) + "f") % 100 + "%"
175 def get_arc_summary(Kstat):
176 """Collect general data on the ARC"""
179 memory_throttle_count = Kstat[
180 "kstat.zfs.misc.arcstats.memory_throttle_count"
183 if memory_throttle_count > 0:
184 output['health'] = 'THROTTLED'
186 output['health'] = 'HEALTHY'
188 output['memory_throttle_count'] = fHits(memory_throttle_count)
191 deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
192 mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
193 evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"]
196 output["arc_misc"] = {}
197 output["arc_misc"]["deleted"] = fHits(deleted)
198 output["arc_misc"]['mutex_miss'] = fHits(mutex_miss)
199 output["arc_misc"]['evict_skips'] = fHits(evict_skip)
202 arc_size = Kstat["kstat.zfs.misc.arcstats.size"]
203 mru_size = Kstat["kstat.zfs.misc.arcstats.mru_size"]
204 mfu_size = Kstat["kstat.zfs.misc.arcstats.mfu_size"]
205 meta_limit = Kstat["kstat.zfs.misc.arcstats.arc_meta_limit"]
206 meta_size = Kstat["kstat.zfs.misc.arcstats.arc_meta_used"]
207 dnode_limit = Kstat["kstat.zfs.misc.arcstats.arc_dnode_limit"]
208 dnode_size = Kstat["kstat.zfs.misc.arcstats.dnode_size"]
209 target_max_size = Kstat["kstat.zfs.misc.arcstats.c_max"]
210 target_min_size = Kstat["kstat.zfs.misc.arcstats.c_min"]
211 target_size = Kstat["kstat.zfs.misc.arcstats.c"]
213 target_size_ratio = (target_max_size / target_min_size)
216 output['arc_sizing'] = {}
217 output['arc_sizing']['arc_size'] = {
218 'per': fPerc(arc_size, target_max_size),
219 'num': fBytes(arc_size),
221 output['arc_sizing']['target_max_size'] = {
222 'ratio': target_size_ratio,
223 'num': fBytes(target_max_size),
225 output['arc_sizing']['target_min_size'] = {
226 'per': fPerc(target_min_size, target_max_size),
227 'num': fBytes(target_min_size),
229 output['arc_sizing']['target_size'] = {
230 'per': fPerc(target_size, target_max_size),
231 'num': fBytes(target_size),
233 output['arc_sizing']['meta_limit'] = {
234 'per': fPerc(meta_limit, target_max_size),
235 'num': fBytes(meta_limit),
237 output['arc_sizing']['meta_size'] = {
238 'per': fPerc(meta_size, meta_limit),
239 'num': fBytes(meta_size),
241 output['arc_sizing']['dnode_limit'] = {
242 'per': fPerc(dnode_limit, meta_limit),
243 'num': fBytes(dnode_limit),
245 output['arc_sizing']['dnode_size'] = {
246 'per': fPerc(dnode_size, dnode_limit),
247 'num': fBytes(dnode_size),
251 output['arc_hash_break'] = {}
252 output['arc_hash_break']['hash_chain_max'] = Kstat[
253 "kstat.zfs.misc.arcstats.hash_chain_max"
255 output['arc_hash_break']['hash_chains'] = Kstat[
256 "kstat.zfs.misc.arcstats.hash_chains"
258 output['arc_hash_break']['hash_collisions'] = Kstat[
259 "kstat.zfs.misc.arcstats.hash_collisions"
261 output['arc_hash_break']['hash_elements'] = Kstat[
262 "kstat.zfs.misc.arcstats.hash_elements"
264 output['arc_hash_break']['hash_elements_max'] = Kstat[
265 "kstat.zfs.misc.arcstats.hash_elements_max"
268 output['arc_size_break'] = {}
269 output['arc_size_break']['recently_used_cache_size'] = {
270 'per': fPerc(mru_size, mru_size + mfu_size),
271 'num': fBytes(mru_size),
273 output['arc_size_break']['frequently_used_cache_size'] = {
274 'per': fPerc(mfu_size, mru_size + mfu_size),
275 'num': fBytes(mfu_size),
279 hash_chain_max = Kstat["kstat.zfs.misc.arcstats.hash_chain_max"]
280 hash_chains = Kstat["kstat.zfs.misc.arcstats.hash_chains"]
281 hash_collisions = Kstat["kstat.zfs.misc.arcstats.hash_collisions"]
282 hash_elements = Kstat["kstat.zfs.misc.arcstats.hash_elements"]
283 hash_elements_max = Kstat["kstat.zfs.misc.arcstats.hash_elements_max"]
285 output['arc_hash_break'] = {}
286 output['arc_hash_break']['elements_max'] = fHits(hash_elements_max)
287 output['arc_hash_break']['elements_current'] = {
288 'per': fPerc(hash_elements, hash_elements_max),
289 'num': fHits(hash_elements),
291 output['arc_hash_break']['collisions'] = fHits(hash_collisions)
292 output['arc_hash_break']['chain_max'] = fHits(hash_chain_max)
293 output['arc_hash_break']['chains'] = fHits(hash_chains)
298 def _arc_summary(Kstat):
299 """Print information on the ARC"""
302 arc = get_arc_summary(Kstat)
304 sys.stdout.write("ARC Summary: (%s)\n" % arc['health'])
306 sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" %
307 arc['memory_throttle_count'])
308 sys.stdout.write("\n")
311 sys.stdout.write("ARC Misc:\n")
312 sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
313 sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
314 arc['arc_misc']['mutex_miss'])
315 sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" %
316 arc['arc_misc']['evict_skips'])
317 sys.stdout.write("\n")
320 sys.stdout.write("ARC Size:\t\t\t\t%s\t%s\n" % (
321 arc['arc_sizing']['arc_size']['per'],
322 arc['arc_sizing']['arc_size']['num']
325 sys.stdout.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % (
326 arc['arc_sizing']['target_size']['per'],
327 arc['arc_sizing']['target_size']['num'],
331 sys.stdout.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % (
332 arc['arc_sizing']['target_min_size']['per'],
333 arc['arc_sizing']['target_min_size']['num'],
337 sys.stdout.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % (
338 arc['arc_sizing']['target_max_size']['ratio'],
339 arc['arc_sizing']['target_max_size']['num'],
343 sys.stdout.write("\nARC Size Breakdown:\n")
344 sys.stdout.write("\tRecently Used Cache Size:\t%s\t%s\n" % (
345 arc['arc_size_break']['recently_used_cache_size']['per'],
346 arc['arc_size_break']['recently_used_cache_size']['num'],
349 sys.stdout.write("\tFrequently Used Cache Size:\t%s\t%s\n" % (
350 arc['arc_size_break']['frequently_used_cache_size']['per'],
351 arc['arc_size_break']['frequently_used_cache_size']['num'],
354 sys.stdout.write("\tMetadata Size (Hard Limit):\t%s\t%s\n" % (
355 arc['arc_sizing']['meta_limit']['per'],
356 arc['arc_sizing']['meta_limit']['num'],
359 sys.stdout.write("\tMetadata Size:\t\t\t%s\t%s\n" % (
360 arc['arc_sizing']['meta_size']['per'],
361 arc['arc_sizing']['meta_size']['num'],
364 sys.stdout.write("\tDnode Size (Hard Limit):\t%s\t%s\n" % (
365 arc['arc_sizing']['dnode_limit']['per'],
366 arc['arc_sizing']['dnode_limit']['num'],
369 sys.stdout.write("\tDnode Size:\t\t\t%s\t%s\n" % (
370 arc['arc_sizing']['dnode_size']['per'],
371 arc['arc_sizing']['dnode_size']['num'],
375 sys.stdout.write("\n")
378 sys.stdout.write("ARC Hash Breakdown:\n")
379 sys.stdout.write("\tElements Max:\t\t\t\t%s\n" %
380 arc['arc_hash_break']['elements_max'])
381 sys.stdout.write("\tElements Current:\t\t%s\t%s\n" % (
382 arc['arc_hash_break']['elements_current']['per'],
383 arc['arc_hash_break']['elements_current']['num'],
386 sys.stdout.write("\tCollisions:\t\t\t\t%s\n" %
387 arc['arc_hash_break']['collisions'])
388 sys.stdout.write("\tChain Max:\t\t\t\t%s\n" %
389 arc['arc_hash_break']['chain_max'])
390 sys.stdout.write("\tChains:\t\t\t\t\t%s\n" %
391 arc['arc_hash_break']['chains'])
394 def get_arc_efficiency(Kstat):
395 """Collect information on the efficiency of the ARC"""
399 arc_hits = Kstat["kstat.zfs.misc.arcstats.hits"]
400 arc_misses = Kstat["kstat.zfs.misc.arcstats.misses"]
401 demand_data_hits = Kstat["kstat.zfs.misc.arcstats.demand_data_hits"]
402 demand_data_misses = Kstat["kstat.zfs.misc.arcstats.demand_data_misses"]
403 demand_metadata_hits = Kstat[
404 "kstat.zfs.misc.arcstats.demand_metadata_hits"
406 demand_metadata_misses = Kstat[
407 "kstat.zfs.misc.arcstats.demand_metadata_misses"
409 mfu_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mfu_ghost_hits"]
410 mfu_hits = Kstat["kstat.zfs.misc.arcstats.mfu_hits"]
411 mru_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mru_ghost_hits"]
412 mru_hits = Kstat["kstat.zfs.misc.arcstats.mru_hits"]
413 prefetch_data_hits = Kstat["kstat.zfs.misc.arcstats.prefetch_data_hits"]
414 prefetch_data_misses = Kstat[
415 "kstat.zfs.misc.arcstats.prefetch_data_misses"
417 prefetch_metadata_hits = Kstat[
418 "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
420 prefetch_metadata_misses = Kstat[
421 "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
424 anon_hits = arc_hits - (
425 mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits
427 arc_accesses_total = (arc_hits + arc_misses)
428 demand_data_total = (demand_data_hits + demand_data_misses)
429 prefetch_data_total = (prefetch_data_hits + prefetch_data_misses)
430 real_hits = (mfu_hits + mru_hits)
432 output["total_accesses"] = fHits(arc_accesses_total)
433 output["cache_hit_ratio"] = {
434 'per': fPerc(arc_hits, arc_accesses_total),
435 'num': fHits(arc_hits),
437 output["cache_miss_ratio"] = {
438 'per': fPerc(arc_misses, arc_accesses_total),
439 'num': fHits(arc_misses),
441 output["actual_hit_ratio"] = {
442 'per': fPerc(real_hits, arc_accesses_total),
443 'num': fHits(real_hits),
445 output["data_demand_efficiency"] = {
446 'per': fPerc(demand_data_hits, demand_data_total),
447 'num': fHits(demand_data_total),
450 if prefetch_data_total > 0:
451 output["data_prefetch_efficiency"] = {
452 'per': fPerc(prefetch_data_hits, prefetch_data_total),
453 'num': fHits(prefetch_data_total),
457 output["cache_hits_by_cache_list"] = {}
458 output["cache_hits_by_cache_list"]["anonymously_used"] = {
459 'per': fPerc(anon_hits, arc_hits),
460 'num': fHits(anon_hits),
463 output["most_recently_used"] = {
464 'per': fPerc(mru_hits, arc_hits),
465 'num': fHits(mru_hits),
467 output["most_frequently_used"] = {
468 'per': fPerc(mfu_hits, arc_hits),
469 'num': fHits(mfu_hits),
471 output["most_recently_used_ghost"] = {
472 'per': fPerc(mru_ghost_hits, arc_hits),
473 'num': fHits(mru_ghost_hits),
475 output["most_frequently_used_ghost"] = {
476 'per': fPerc(mfu_ghost_hits, arc_hits),
477 'num': fHits(mfu_ghost_hits),
480 output["cache_hits_by_data_type"] = {}
481 output["cache_hits_by_data_type"]["demand_data"] = {
482 'per': fPerc(demand_data_hits, arc_hits),
483 'num': fHits(demand_data_hits),
485 output["cache_hits_by_data_type"]["prefetch_data"] = {
486 'per': fPerc(prefetch_data_hits, arc_hits),
487 'num': fHits(prefetch_data_hits),
489 output["cache_hits_by_data_type"]["demand_metadata"] = {
490 'per': fPerc(demand_metadata_hits, arc_hits),
491 'num': fHits(demand_metadata_hits),
493 output["cache_hits_by_data_type"]["prefetch_metadata"] = {
494 'per': fPerc(prefetch_metadata_hits, arc_hits),
495 'num': fHits(prefetch_metadata_hits),
498 output["cache_misses_by_data_type"] = {}
499 output["cache_misses_by_data_type"]["demand_data"] = {
500 'per': fPerc(demand_data_misses, arc_misses),
501 'num': fHits(demand_data_misses),
503 output["cache_misses_by_data_type"]["prefetch_data"] = {
504 'per': fPerc(prefetch_data_misses, arc_misses),
505 'num': fHits(prefetch_data_misses),
507 output["cache_misses_by_data_type"]["demand_metadata"] = {
508 'per': fPerc(demand_metadata_misses, arc_misses),
509 'num': fHits(demand_metadata_misses),
511 output["cache_misses_by_data_type"]["prefetch_metadata"] = {
512 'per': fPerc(prefetch_metadata_misses, arc_misses),
513 'num': fHits(prefetch_metadata_misses),
519 def _arc_efficiency(Kstat):
520 """Print information on the efficiency of the ARC"""
522 arc = get_arc_efficiency(Kstat)
524 sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" %
525 arc['total_accesses'])
526 sys.stdout.write("\tCache Hit Ratio:\t\t%s\t%s\n" % (
527 arc['cache_hit_ratio']['per'],
528 arc['cache_hit_ratio']['num'],
531 sys.stdout.write("\tCache Miss Ratio:\t\t%s\t%s\n" % (
532 arc['cache_miss_ratio']['per'],
533 arc['cache_miss_ratio']['num'],
537 sys.stdout.write("\tActual Hit Ratio:\t\t%s\t%s\n" % (
538 arc['actual_hit_ratio']['per'],
539 arc['actual_hit_ratio']['num'],
543 sys.stdout.write("\n")
544 sys.stdout.write("\tData Demand Efficiency:\t\t%s\t%s\n" % (
545 arc['data_demand_efficiency']['per'],
546 arc['data_demand_efficiency']['num'],
550 if 'data_prefetch_efficiency' in arc:
551 sys.stdout.write("\tData Prefetch Efficiency:\t%s\t%s\n" % (
552 arc['data_prefetch_efficiency']['per'],
553 arc['data_prefetch_efficiency']['num'],
556 sys.stdout.write("\n")
558 sys.stdout.write("\tCACHE HITS BY CACHE LIST:\n")
559 if 'cache_hits_by_cache_list' in arc:
560 sys.stdout.write("\t Anonymously Used:\t\t%s\t%s\n" % (
561 arc['cache_hits_by_cache_list']['anonymously_used']['per'],
562 arc['cache_hits_by_cache_list']['anonymously_used']['num'],
565 sys.stdout.write("\t Most Recently Used:\t\t%s\t%s\n" % (
566 arc['most_recently_used']['per'],
567 arc['most_recently_used']['num'],
570 sys.stdout.write("\t Most Frequently Used:\t\t%s\t%s\n" % (
571 arc['most_frequently_used']['per'],
572 arc['most_frequently_used']['num'],
575 sys.stdout.write("\t Most Recently Used Ghost:\t%s\t%s\n" % (
576 arc['most_recently_used_ghost']['per'],
577 arc['most_recently_used_ghost']['num'],
580 sys.stdout.write("\t Most Frequently Used Ghost:\t%s\t%s\n" % (
581 arc['most_frequently_used_ghost']['per'],
582 arc['most_frequently_used_ghost']['num'],
586 sys.stdout.write("\n\tCACHE HITS BY DATA TYPE:\n")
587 sys.stdout.write("\t Demand Data:\t\t\t%s\t%s\n" % (
588 arc["cache_hits_by_data_type"]['demand_data']['per'],
589 arc["cache_hits_by_data_type"]['demand_data']['num'],
592 sys.stdout.write("\t Prefetch Data:\t\t%s\t%s\n" % (
593 arc["cache_hits_by_data_type"]['prefetch_data']['per'],
594 arc["cache_hits_by_data_type"]['prefetch_data']['num'],
597 sys.stdout.write("\t Demand Metadata:\t\t%s\t%s\n" % (
598 arc["cache_hits_by_data_type"]['demand_metadata']['per'],
599 arc["cache_hits_by_data_type"]['demand_metadata']['num'],
602 sys.stdout.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
603 arc["cache_hits_by_data_type"]['prefetch_metadata']['per'],
604 arc["cache_hits_by_data_type"]['prefetch_metadata']['num'],
608 sys.stdout.write("\n\tCACHE MISSES BY DATA TYPE:\n")
609 sys.stdout.write("\t Demand Data:\t\t\t%s\t%s\n" % (
610 arc["cache_misses_by_data_type"]['demand_data']['per'],
611 arc["cache_misses_by_data_type"]['demand_data']['num'],
614 sys.stdout.write("\t Prefetch Data:\t\t%s\t%s\n" % (
615 arc["cache_misses_by_data_type"]['prefetch_data']['per'],
616 arc["cache_misses_by_data_type"]['prefetch_data']['num'],
619 sys.stdout.write("\t Demand Metadata:\t\t%s\t%s\n" % (
620 arc["cache_misses_by_data_type"]['demand_metadata']['per'],
621 arc["cache_misses_by_data_type"]['demand_metadata']['num'],
624 sys.stdout.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
625 arc["cache_misses_by_data_type"]['prefetch_metadata']['per'],
626 arc["cache_misses_by_data_type"]['prefetch_metadata']['num'],
631 def get_l2arc_summary(Kstat):
632 """Collection information on the L2ARC"""
636 l2_abort_lowmem = Kstat["kstat.zfs.misc.arcstats.l2_abort_lowmem"]
637 l2_cksum_bad = Kstat["kstat.zfs.misc.arcstats.l2_cksum_bad"]
638 l2_evict_lock_retry = Kstat["kstat.zfs.misc.arcstats.l2_evict_lock_retry"]
639 l2_evict_reading = Kstat["kstat.zfs.misc.arcstats.l2_evict_reading"]
640 l2_feeds = Kstat["kstat.zfs.misc.arcstats.l2_feeds"]
641 l2_free_on_write = Kstat["kstat.zfs.misc.arcstats.l2_free_on_write"]
642 l2_hdr_size = Kstat["kstat.zfs.misc.arcstats.l2_hdr_size"]
643 l2_hits = Kstat["kstat.zfs.misc.arcstats.l2_hits"]
644 l2_io_error = Kstat["kstat.zfs.misc.arcstats.l2_io_error"]
645 l2_misses = Kstat["kstat.zfs.misc.arcstats.l2_misses"]
646 l2_rw_clash = Kstat["kstat.zfs.misc.arcstats.l2_rw_clash"]
647 l2_size = Kstat["kstat.zfs.misc.arcstats.l2_size"]
648 l2_asize = Kstat["kstat.zfs.misc.arcstats.l2_asize"]
649 l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"]
650 l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"]
651 l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"]
653 l2_access_total = (l2_hits + l2_misses)
654 output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error)
656 output['l2_access_total'] = l2_access_total
657 output['l2_size'] = l2_size
658 output['l2_asize'] = l2_asize
660 if l2_size > 0 and l2_access_total > 0:
662 if output['l2_health_count'] > 0:
663 output["health"] = "DEGRADED"
665 output["health"] = "HEALTHY"
667 output["low_memory_aborts"] = fHits(l2_abort_lowmem)
668 output["free_on_write"] = fHits(l2_free_on_write)
669 output["rw_clashes"] = fHits(l2_rw_clash)
670 output["bad_checksums"] = fHits(l2_cksum_bad)
671 output["io_errors"] = fHits(l2_io_error)
673 output["l2_arc_size"] = {}
674 output["l2_arc_size"]["adative"] = fBytes(l2_size)
675 output["l2_arc_size"]["actual"] = {
676 'per': fPerc(l2_asize, l2_size),
677 'num': fBytes(l2_asize)
679 output["l2_arc_size"]["head_size"] = {
680 'per': fPerc(l2_hdr_size, l2_size),
681 'num': fBytes(l2_hdr_size),
684 output["l2_arc_evicts"] = {}
685 output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry)
686 output["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading)
688 output['l2_arc_breakdown'] = {}
689 output['l2_arc_breakdown']['value'] = fHits(l2_access_total)
690 output['l2_arc_breakdown']['hit_ratio'] = {
691 'per': fPerc(l2_hits, l2_access_total),
692 'num': fHits(l2_hits),
694 output['l2_arc_breakdown']['miss_ratio'] = {
695 'per': fPerc(l2_misses, l2_access_total),
696 'num': fHits(l2_misses),
698 output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds)
700 output['l2_arc_buffer'] = {}
702 output['l2_arc_writes'] = {}
703 output['l2_writes_done'] = l2_writes_done
704 output['l2_writes_sent'] = l2_writes_sent
705 if l2_writes_done != l2_writes_sent:
706 output['l2_arc_writes']['writes_sent'] = {
708 'num': fHits(l2_writes_sent),
710 output['l2_arc_writes']['done_ratio'] = {
711 'per': fPerc(l2_writes_done, l2_writes_sent),
712 'num': fHits(l2_writes_done),
714 output['l2_arc_writes']['error_ratio'] = {
715 'per': fPerc(l2_writes_error, l2_writes_sent),
716 'num': fHits(l2_writes_error),
719 output['l2_arc_writes']['writes_sent'] = {
721 'num': fHits(l2_writes_sent),
727 def _l2arc_summary(Kstat):
728 """Print information on the L2ARC"""
730 arc = get_l2arc_summary(Kstat)
732 if arc['l2_size'] > 0 and arc['l2_access_total'] > 0:
733 sys.stdout.write("L2 ARC Summary: ")
734 if arc['l2_health_count'] > 0:
735 sys.stdout.write("(DEGRADED)\n")
737 sys.stdout.write("(HEALTHY)\n")
738 sys.stdout.write("\tLow Memory Aborts:\t\t\t%s\n" %
739 arc['low_memory_aborts'])
740 sys.stdout.write("\tFree on Write:\t\t\t\t%s\n" % arc['free_on_write'])
741 sys.stdout.write("\tR/W Clashes:\t\t\t\t%s\n" % arc['rw_clashes'])
742 sys.stdout.write("\tBad Checksums:\t\t\t\t%s\n" % arc['bad_checksums'])
743 sys.stdout.write("\tIO Errors:\t\t\t\t%s\n" % arc['io_errors'])
744 sys.stdout.write("\n")
746 sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
747 arc["l2_arc_size"]["adative"])
748 sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % (
749 arc["l2_arc_size"]["actual"]["per"],
750 arc["l2_arc_size"]["actual"]["num"],
753 sys.stdout.write("\tHeader Size:\t\t\t%s\t%s\n" % (
754 arc["l2_arc_size"]["head_size"]["per"],
755 arc["l2_arc_size"]["head_size"]["num"],
758 sys.stdout.write("\n")
760 if arc["l2_arc_evicts"]['lock_retries'] != '0' or \
761 arc["l2_arc_evicts"]["reading"] != '0':
762 sys.stdout.write("L2 ARC Evicts:\n")
763 sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" %
764 arc["l2_arc_evicts"]['lock_retries'])
765 sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" %
766 arc["l2_arc_evicts"]["reading"])
767 sys.stdout.write("\n")
769 sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
770 arc['l2_arc_breakdown']['value'])
771 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
772 arc['l2_arc_breakdown']['hit_ratio']['per'],
773 arc['l2_arc_breakdown']['hit_ratio']['num'],
777 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
778 arc['l2_arc_breakdown']['miss_ratio']['per'],
779 arc['l2_arc_breakdown']['miss_ratio']['num'],
783 sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" %
784 arc['l2_arc_breakdown']['feeds'])
785 sys.stdout.write("\n")
787 sys.stdout.write("L2 ARC Writes:\n")
788 if arc['l2_writes_done'] != arc['l2_writes_sent']:
789 sys.stdout.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % (
790 arc['l2_arc_writes']['writes_sent']['value'],
791 arc['l2_arc_writes']['writes_sent']['num'],
794 sys.stdout.write("\t Done Ratio:\t\t\t%s\t%s\n" % (
795 arc['l2_arc_writes']['done_ratio']['per'],
796 arc['l2_arc_writes']['done_ratio']['num'],
799 sys.stdout.write("\t Error Ratio:\t\t\t%s\t%s\n" % (
800 arc['l2_arc_writes']['error_ratio']['per'],
801 arc['l2_arc_writes']['error_ratio']['num'],
805 sys.stdout.write("\tWrites Sent:\t\t\t%s\t%s\n" % (
806 arc['l2_arc_writes']['writes_sent']['per'],
807 arc['l2_arc_writes']['writes_sent']['num'],
812 def get_dmu_summary(Kstat):
813 """Collect information on the DMU"""
817 zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"]
818 zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"]
820 zfetch_access_total = (zfetch_hits + zfetch_misses)
821 output['zfetch_access_total'] = zfetch_access_total
823 if zfetch_access_total > 0:
825 output['dmu']['efficiency'] = {}
826 output['dmu']['efficiency']['value'] = fHits(zfetch_access_total)
827 output['dmu']['efficiency']['hit_ratio'] = {
828 'per': fPerc(zfetch_hits, zfetch_access_total),
829 'num': fHits(zfetch_hits),
831 output['dmu']['efficiency']['miss_ratio'] = {
832 'per': fPerc(zfetch_misses, zfetch_access_total),
833 'num': fHits(zfetch_misses),
839 def _dmu_summary(Kstat):
840 """Print information on the DMU"""
842 arc = get_dmu_summary(Kstat)
844 if arc['zfetch_access_total'] > 0:
845 sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
846 arc['dmu']['efficiency']['value'])
847 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
848 arc['dmu']['efficiency']['hit_ratio']['per'],
849 arc['dmu']['efficiency']['hit_ratio']['num'],
852 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
853 arc['dmu']['efficiency']['miss_ratio']['per'],
854 arc['dmu']['efficiency']['miss_ratio']['num'],
858 sys.stdout.write("\n")
861 def get_vdev_summary(Kstat):
862 """Collect information on the VDEVs"""
866 vdev_cache_delegations = \
867 Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"]
868 vdev_cache_misses = Kstat["kstat.zfs.misc.vdev_cache_stats.misses"]
869 vdev_cache_hits = Kstat["kstat.zfs.misc.vdev_cache_stats.hits"]
870 vdev_cache_total = (vdev_cache_misses + vdev_cache_hits +
871 vdev_cache_delegations)
873 output['vdev_cache_total'] = vdev_cache_total
875 if vdev_cache_total > 0:
876 output['summary'] = fHits(vdev_cache_total)
877 output['hit_ratio'] = {
878 'per': fPerc(vdev_cache_hits, vdev_cache_total),
879 'num': fHits(vdev_cache_hits),
881 output['miss_ratio'] = {
882 'per': fPerc(vdev_cache_misses, vdev_cache_total),
883 'num': fHits(vdev_cache_misses),
885 output['delegations'] = {
886 'per': fPerc(vdev_cache_delegations, vdev_cache_total),
887 'num': fHits(vdev_cache_delegations),
893 def _vdev_summary(Kstat):
894 """Print information on the VDEVs"""
896 arc = get_vdev_summary(Kstat)
898 if arc['vdev_cache_total'] > 0:
899 sys.stdout.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc['summary'])
900 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
901 arc['hit_ratio']['per'],
902 arc['hit_ratio']['num'],
904 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
905 arc['miss_ratio']['per'],
906 arc['miss_ratio']['num'],
908 sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % (
909 arc['delegations']['per'],
910 arc['delegations']['num'],
914 def _tunable_summary(Kstat):
915 """Print information on tunables, including descriptions if requested"""
917 global show_tunable_descriptions
918 global alternate_tunable_layout
921 basepath = '/sys/module/zfs/parameters'
923 for name in os.listdir(basepath):
926 path = '%s/%s' % (basepath, name)
927 with open(path) as f:
929 tunables[name] = value.strip()
932 tunables = load_tunables()
935 if show_tunable_descriptions:
937 command = ["/sbin/modinfo", "zfs", "-0"]
940 p = Popen(command, stdin=PIPE, stdout=PIPE,
941 stderr=PIPE, shell=False, close_fds=True)
944 # By default, Python 2 returns a string as the first element of the
945 # tuple from p.communicate(), while Python 3 returns bytes which
946 # must be decoded first. The better way to do this would be with
947 # subprocess.run() or at least .check_output(), but this fails on
948 # CentOS 6 because of its old version of Python 2
949 desc = bytes.decode(p.communicate()[0])
950 description_list = desc.strip().split('\0')
952 if p.returncode == 0:
953 for tunable in description_list:
954 if tunable[0:5] == 'parm:':
955 tunable = tunable[5:].strip()
956 name, description = tunable.split(':', 1)
958 description = "Description unavailable"
959 descriptions[name] = description
961 sys.stderr.write("%s: '%s' exited with code %i\n" %
962 (sys.argv[0], command[0], p.returncode))
963 sys.stderr.write("Tunable descriptions will be disabled.\n")
965 sys.stderr.write("%s: Cannot run '%s': %s\n" %
966 (sys.argv[0], command[0], e.strerror))
967 sys.stderr.write("Tunable descriptions will be disabled.\n")
969 sys.stdout.write("ZFS Tunables:\n")
971 if alternate_tunable_layout:
976 for name in sorted(tunables.keys()):
977 if show_tunable_descriptions and name in descriptions:
978 sys.stdout.write("\t# %s\n" % descriptions[name])
980 sys.stdout.write(fmt % (name, tunables[name]))
994 """Print title string with date"""
996 daydate = time.strftime('%a %b %d %H:%M:%S %Y')
998 sys.stdout.write('\n'+'-'*72+'\n')
999 sys.stdout.write('ZFS Subsystem Report\t\t\t\t%s' % daydate)
1000 sys.stdout.write('\n')
1004 """Print usage information"""
1006 sys.stdout.write("Usage: arc_summary [-h] [-a] [-d] [-p PAGE]\n\n")
1007 sys.stdout.write("\t -h, --help : "
1008 "Print this help message and exit\n")
1009 sys.stdout.write("\t -a, --alternate : "
1010 "Show an alternate sysctl layout\n")
1011 sys.stdout.write("\t -d, --description : "
1012 "Show the sysctl descriptions\n")
1013 sys.stdout.write("\t -p PAGE, --page=PAGE : "
1014 "Select a single output page to display,\n")
1015 sys.stdout.write("\t "
1016 "should be an integer between 1 and " +
1017 str(len(unSub)) + "\n\n")
1018 sys.stdout.write("Examples:\n")
1019 sys.stdout.write("\tarc_summary -a\n")
1020 sys.stdout.write("\tarc_summary -p 4\n")
1021 sys.stdout.write("\tarc_summary -ad\n")
1022 sys.stdout.write("\tarc_summary --page=2\n")
1028 global show_tunable_descriptions
1029 global alternate_tunable_layout
1032 opts, args = getopt.getopt(
1034 "adp:h", ["alternate", "description", "page=", "help"]
1036 except getopt.error as e:
1037 sys.stderr.write("Error: %s\n" % e.msg)
1042 for opt, arg in opts:
1043 if opt in ('-a', '--alternate'):
1045 if opt in ('-d', '--description'):
1047 if opt in ('-p', '--page'):
1049 if opt in ('-h', '--help'):
1055 alternate_tunable_layout = 'a' in args
1056 show_tunable_descriptions = 'd' in args
1062 pages.append(unSub[int(args['p']) - 1])
1064 sys.stderr.write('the argument to -p must be between 1 and ' +
1065 str(len(unSub)) + '\n')
1073 sys.stdout.write("\n")
1076 if __name__ == '__main__':