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.
38 """Print statistics on the ZFS Adjustable Replacement Cache (ARC)
40 Provides basic information on the ARC, its efficiency, the L2ARC (if present),
41 the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the
42 in-source documentation and code at
43 https://github.com/zfsonlinux/zfs/blob/master/module/zfs/arc.c for details.
52 from subprocess
import Popen
, PIPE
53 from decimal
import Decimal
as D
55 show_tunable_descriptions
= False
56 alternate_tunable_layout
= False
59 def handle_Exception(ex_cls
, ex
, tb
):
61 if ex
.errno
== errno
.EPIPE
:
64 if ex
is KeyboardInterrupt:
68 sys
.excepthook
= handle_Exception
72 """Collect information on the ZFS subsystem from the /proc virtual
73 file system. The name "kstat" is a holdover from the Solaris utility
77 def load_proc_kstats(fn
, namespace
):
78 """Collect information on a specific subsystem of the ARC"""
80 kstats
= [line
.strip() for line
in open(fn
)]
84 name
, _
, value
= kstat
.split()
85 Kstat
[namespace
+ name
] = D(value
)
88 load_proc_kstats('/proc/spl/kstat/zfs/arcstats',
89 'kstat.zfs.misc.arcstats.')
90 load_proc_kstats('/proc/spl/kstat/zfs/zfetchstats',
91 'kstat.zfs.misc.zfetchstats.')
92 load_proc_kstats('/proc/spl/kstat/zfs/vdev_cache_stats',
93 'kstat.zfs.misc.vdev_cache_stats.')
99 """Return human-readable representation of a byte value in
100 powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
101 points. Values smaller than one KiB are returned without
106 [2**80, "YiB"], # yobibytes (yotta)
107 [2**70, "ZiB"], # zebibytes (zetta)
108 [2**60, "EiB"], # exbibytes (exa)
109 [2**50, "PiB"], # pebibytes (peta)
110 [2**40, "TiB"], # tebibytes (tera)
111 [2**30, "GiB"], # gibibytes (giga)
112 [2**20, "MiB"], # mebibytes (mega)
113 [2**10, "KiB"]] # kibibytes (kilo)
117 for limit
, unit
in prefixes
:
123 result
= "%0.2f\t%s" % (value
, unit
)
127 result
= "%d\tBytes" % b
133 """Create a human-readable representation of the number of hits.
134 The single-letter symbols used are SI to avoid the confusion caused
135 by the different "short scale" and "long scale" representations in
136 English, which use the same words for different values. See
137 https://en.wikipedia.org/wiki/Names_of_large_numbers and
138 https://physics.nist.gov/cuu/Units/prefixes.html
142 [10**24, 'Y'], # yotta (septillion)
143 [10**21, 'Z'], # zetta (sextillion)
144 [10**18, 'E'], # exa (quintrillion)
145 [10**15, 'P'], # peta (quadrillion)
146 [10**12, 'T'], # tera (trillion)
147 [10**9, 'G'], # giga (billion)
148 [10**6, 'M'], # mega (million)
149 [10**3, 'k']] # kilo (thousand)
153 for limit
, symbol
in numbers
:
159 result
= "%0.2f%s" % (value
, symbol
)
168 def fPerc(lVal
=0, rVal
=0, Decimal
=2):
169 """Calculate percentage value and return in human-readable format"""
172 return str("%0." + str(Decimal
) + "f") % (100 * (lVal
/ rVal
)) + "%"
174 return str("%0." + str(Decimal
) + "f") % 100 + "%"
177 def get_arc_summary(Kstat
):
178 """Collect general data on the ARC"""
181 memory_throttle_count
= Kstat
[
182 "kstat.zfs.misc.arcstats.memory_throttle_count"
185 if memory_throttle_count
> 0:
186 output
['health'] = 'THROTTLED'
188 output
['health'] = 'HEALTHY'
190 output
['memory_throttle_count'] = fHits(memory_throttle_count
)
193 deleted
= Kstat
["kstat.zfs.misc.arcstats.deleted"]
194 mutex_miss
= Kstat
["kstat.zfs.misc.arcstats.mutex_miss"]
195 evict_skip
= Kstat
["kstat.zfs.misc.arcstats.evict_skip"]
198 output
["arc_misc"] = {}
199 output
["arc_misc"]["deleted"] = fHits(deleted
)
200 output
["arc_misc"]['mutex_miss'] = fHits(mutex_miss
)
201 output
["arc_misc"]['evict_skips'] = fHits(evict_skip
)
204 arc_size
= Kstat
["kstat.zfs.misc.arcstats.size"]
205 mru_size
= Kstat
["kstat.zfs.misc.arcstats.mru_size"]
206 mfu_size
= Kstat
["kstat.zfs.misc.arcstats.mfu_size"]
207 target_max_size
= Kstat
["kstat.zfs.misc.arcstats.c_max"]
208 target_min_size
= Kstat
["kstat.zfs.misc.arcstats.c_min"]
209 target_size
= Kstat
["kstat.zfs.misc.arcstats.c"]
211 target_size_ratio
= (target_max_size
/ target_min_size
)
214 output
['arc_sizing'] = {}
215 output
['arc_sizing']['arc_size'] = {
216 'per': fPerc(arc_size
, target_max_size
),
217 'num': fBytes(arc_size
),
219 output
['arc_sizing']['target_max_size'] = {
220 'ratio': target_size_ratio
,
221 'num': fBytes(target_max_size
),
223 output
['arc_sizing']['target_min_size'] = {
224 'per': fPerc(target_min_size
, target_max_size
),
225 'num': fBytes(target_min_size
),
227 output
['arc_sizing']['target_size'] = {
228 'per': fPerc(target_size
, target_max_size
),
229 'num': fBytes(target_size
),
233 output
['arc_hash_break'] = {}
234 output
['arc_hash_break']['hash_chain_max'] = Kstat
[
235 "kstat.zfs.misc.arcstats.hash_chain_max"
237 output
['arc_hash_break']['hash_chains'] = Kstat
[
238 "kstat.zfs.misc.arcstats.hash_chains"
240 output
['arc_hash_break']['hash_collisions'] = Kstat
[
241 "kstat.zfs.misc.arcstats.hash_collisions"
243 output
['arc_hash_break']['hash_elements'] = Kstat
[
244 "kstat.zfs.misc.arcstats.hash_elements"
246 output
['arc_hash_break']['hash_elements_max'] = Kstat
[
247 "kstat.zfs.misc.arcstats.hash_elements_max"
250 output
['arc_size_break'] = {}
251 output
['arc_size_break']['recently_used_cache_size'] = {
252 'per': fPerc(mru_size
, mru_size
+ mfu_size
),
253 'num': fBytes(mru_size
),
255 output
['arc_size_break']['frequently_used_cache_size'] = {
256 'per': fPerc(mfu_size
, mru_size
+ mfu_size
),
257 'num': fBytes(mfu_size
),
261 hash_chain_max
= Kstat
["kstat.zfs.misc.arcstats.hash_chain_max"]
262 hash_chains
= Kstat
["kstat.zfs.misc.arcstats.hash_chains"]
263 hash_collisions
= Kstat
["kstat.zfs.misc.arcstats.hash_collisions"]
264 hash_elements
= Kstat
["kstat.zfs.misc.arcstats.hash_elements"]
265 hash_elements_max
= Kstat
["kstat.zfs.misc.arcstats.hash_elements_max"]
267 output
['arc_hash_break'] = {}
268 output
['arc_hash_break']['elements_max'] = fHits(hash_elements_max
)
269 output
['arc_hash_break']['elements_current'] = {
270 'per': fPerc(hash_elements
, hash_elements_max
),
271 'num': fHits(hash_elements
),
273 output
['arc_hash_break']['collisions'] = fHits(hash_collisions
)
274 output
['arc_hash_break']['chain_max'] = fHits(hash_chain_max
)
275 output
['arc_hash_break']['chains'] = fHits(hash_chains
)
280 def _arc_summary(Kstat
):
281 """Print information on the ARC"""
284 arc
= get_arc_summary(Kstat
)
286 sys
.stdout
.write("ARC Summary: (%s)\n" % arc
['health'])
288 sys
.stdout
.write("\tMemory Throttle Count:\t\t\t%s\n" %
289 arc
['memory_throttle_count'])
290 sys
.stdout
.write("\n")
293 sys
.stdout
.write("ARC Misc:\n")
294 sys
.stdout
.write("\tDeleted:\t\t\t\t%s\n" % arc
['arc_misc']['deleted'])
295 sys
.stdout
.write("\tMutex Misses:\t\t\t\t%s\n" %
296 arc
['arc_misc']['mutex_miss'])
297 sys
.stdout
.write("\tEvict Skips:\t\t\t\t%s\n" %
298 arc
['arc_misc']['evict_skips'])
299 sys
.stdout
.write("\n")
302 sys
.stdout
.write("ARC Size:\t\t\t\t%s\t%s\n" % (
303 arc
['arc_sizing']['arc_size']['per'],
304 arc
['arc_sizing']['arc_size']['num']
307 sys
.stdout
.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % (
308 arc
['arc_sizing']['target_size']['per'],
309 arc
['arc_sizing']['target_size']['num'],
313 sys
.stdout
.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % (
314 arc
['arc_sizing']['target_min_size']['per'],
315 arc
['arc_sizing']['target_min_size']['num'],
319 sys
.stdout
.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % (
320 arc
['arc_sizing']['target_max_size']['ratio'],
321 arc
['arc_sizing']['target_max_size']['num'],
325 sys
.stdout
.write("\nARC Size Breakdown:\n")
326 sys
.stdout
.write("\tRecently Used Cache Size:\t%s\t%s\n" % (
327 arc
['arc_size_break']['recently_used_cache_size']['per'],
328 arc
['arc_size_break']['recently_used_cache_size']['num'],
331 sys
.stdout
.write("\tFrequently Used Cache Size:\t%s\t%s\n" % (
332 arc
['arc_size_break']['frequently_used_cache_size']['per'],
333 arc
['arc_size_break']['frequently_used_cache_size']['num'],
337 sys
.stdout
.write("\n")
340 sys
.stdout
.write("ARC Hash Breakdown:\n")
341 sys
.stdout
.write("\tElements Max:\t\t\t\t%s\n" %
342 arc
['arc_hash_break']['elements_max'])
343 sys
.stdout
.write("\tElements Current:\t\t%s\t%s\n" % (
344 arc
['arc_hash_break']['elements_current']['per'],
345 arc
['arc_hash_break']['elements_current']['num'],
348 sys
.stdout
.write("\tCollisions:\t\t\t\t%s\n" %
349 arc
['arc_hash_break']['collisions'])
350 sys
.stdout
.write("\tChain Max:\t\t\t\t%s\n" %
351 arc
['arc_hash_break']['chain_max'])
352 sys
.stdout
.write("\tChains:\t\t\t\t\t%s\n" %
353 arc
['arc_hash_break']['chains'])
356 def get_arc_efficiency(Kstat
):
357 """Collect information on the efficiency of the ARC"""
361 arc_hits
= Kstat
["kstat.zfs.misc.arcstats.hits"]
362 arc_misses
= Kstat
["kstat.zfs.misc.arcstats.misses"]
363 demand_data_hits
= Kstat
["kstat.zfs.misc.arcstats.demand_data_hits"]
364 demand_data_misses
= Kstat
["kstat.zfs.misc.arcstats.demand_data_misses"]
365 demand_metadata_hits
= Kstat
[
366 "kstat.zfs.misc.arcstats.demand_metadata_hits"
368 demand_metadata_misses
= Kstat
[
369 "kstat.zfs.misc.arcstats.demand_metadata_misses"
371 mfu_ghost_hits
= Kstat
["kstat.zfs.misc.arcstats.mfu_ghost_hits"]
372 mfu_hits
= Kstat
["kstat.zfs.misc.arcstats.mfu_hits"]
373 mru_ghost_hits
= Kstat
["kstat.zfs.misc.arcstats.mru_ghost_hits"]
374 mru_hits
= Kstat
["kstat.zfs.misc.arcstats.mru_hits"]
375 prefetch_data_hits
= Kstat
["kstat.zfs.misc.arcstats.prefetch_data_hits"]
376 prefetch_data_misses
= Kstat
[
377 "kstat.zfs.misc.arcstats.prefetch_data_misses"
379 prefetch_metadata_hits
= Kstat
[
380 "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
382 prefetch_metadata_misses
= Kstat
[
383 "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
386 anon_hits
= arc_hits
- (
387 mfu_hits
+ mru_hits
+ mfu_ghost_hits
+ mru_ghost_hits
389 arc_accesses_total
= (arc_hits
+ arc_misses
)
390 demand_data_total
= (demand_data_hits
+ demand_data_misses
)
391 prefetch_data_total
= (prefetch_data_hits
+ prefetch_data_misses
)
392 real_hits
= (mfu_hits
+ mru_hits
)
394 output
["total_accesses"] = fHits(arc_accesses_total
)
395 output
["cache_hit_ratio"] = {
396 'per': fPerc(arc_hits
, arc_accesses_total
),
397 'num': fHits(arc_hits
),
399 output
["cache_miss_ratio"] = {
400 'per': fPerc(arc_misses
, arc_accesses_total
),
401 'num': fHits(arc_misses
),
403 output
["actual_hit_ratio"] = {
404 'per': fPerc(real_hits
, arc_accesses_total
),
405 'num': fHits(real_hits
),
407 output
["data_demand_efficiency"] = {
408 'per': fPerc(demand_data_hits
, demand_data_total
),
409 'num': fHits(demand_data_total
),
412 if prefetch_data_total
> 0:
413 output
["data_prefetch_efficiency"] = {
414 'per': fPerc(prefetch_data_hits
, prefetch_data_total
),
415 'num': fHits(prefetch_data_total
),
419 output
["cache_hits_by_cache_list"] = {}
420 output
["cache_hits_by_cache_list"]["anonymously_used"] = {
421 'per': fPerc(anon_hits
, arc_hits
),
422 'num': fHits(anon_hits
),
425 output
["most_recently_used"] = {
426 'per': fPerc(mru_hits
, arc_hits
),
427 'num': fHits(mru_hits
),
429 output
["most_frequently_used"] = {
430 'per': fPerc(mfu_hits
, arc_hits
),
431 'num': fHits(mfu_hits
),
433 output
["most_recently_used_ghost"] = {
434 'per': fPerc(mru_ghost_hits
, arc_hits
),
435 'num': fHits(mru_ghost_hits
),
437 output
["most_frequently_used_ghost"] = {
438 'per': fPerc(mfu_ghost_hits
, arc_hits
),
439 'num': fHits(mfu_ghost_hits
),
442 output
["cache_hits_by_data_type"] = {}
443 output
["cache_hits_by_data_type"]["demand_data"] = {
444 'per': fPerc(demand_data_hits
, arc_hits
),
445 'num': fHits(demand_data_hits
),
447 output
["cache_hits_by_data_type"]["prefetch_data"] = {
448 'per': fPerc(prefetch_data_hits
, arc_hits
),
449 'num': fHits(prefetch_data_hits
),
451 output
["cache_hits_by_data_type"]["demand_metadata"] = {
452 'per': fPerc(demand_metadata_hits
, arc_hits
),
453 'num': fHits(demand_metadata_hits
),
455 output
["cache_hits_by_data_type"]["prefetch_metadata"] = {
456 'per': fPerc(prefetch_metadata_hits
, arc_hits
),
457 'num': fHits(prefetch_metadata_hits
),
460 output
["cache_misses_by_data_type"] = {}
461 output
["cache_misses_by_data_type"]["demand_data"] = {
462 'per': fPerc(demand_data_misses
, arc_misses
),
463 'num': fHits(demand_data_misses
),
465 output
["cache_misses_by_data_type"]["prefetch_data"] = {
466 'per': fPerc(prefetch_data_misses
, arc_misses
),
467 'num': fHits(prefetch_data_misses
),
469 output
["cache_misses_by_data_type"]["demand_metadata"] = {
470 'per': fPerc(demand_metadata_misses
, arc_misses
),
471 'num': fHits(demand_metadata_misses
),
473 output
["cache_misses_by_data_type"]["prefetch_metadata"] = {
474 'per': fPerc(prefetch_metadata_misses
, arc_misses
),
475 'num': fHits(prefetch_metadata_misses
),
481 def _arc_efficiency(Kstat
):
482 """Print information on the efficiency of the ARC"""
484 arc
= get_arc_efficiency(Kstat
)
486 sys
.stdout
.write("ARC Total accesses:\t\t\t\t\t%s\n" %
487 arc
['total_accesses'])
488 sys
.stdout
.write("\tCache Hit Ratio:\t\t%s\t%s\n" % (
489 arc
['cache_hit_ratio']['per'],
490 arc
['cache_hit_ratio']['num'],
493 sys
.stdout
.write("\tCache Miss Ratio:\t\t%s\t%s\n" % (
494 arc
['cache_miss_ratio']['per'],
495 arc
['cache_miss_ratio']['num'],
499 sys
.stdout
.write("\tActual Hit Ratio:\t\t%s\t%s\n" % (
500 arc
['actual_hit_ratio']['per'],
501 arc
['actual_hit_ratio']['num'],
505 sys
.stdout
.write("\n")
506 sys
.stdout
.write("\tData Demand Efficiency:\t\t%s\t%s\n" % (
507 arc
['data_demand_efficiency']['per'],
508 arc
['data_demand_efficiency']['num'],
512 if 'data_prefetch_efficiency' in arc
:
513 sys
.stdout
.write("\tData Prefetch Efficiency:\t%s\t%s\n" % (
514 arc
['data_prefetch_efficiency']['per'],
515 arc
['data_prefetch_efficiency']['num'],
518 sys
.stdout
.write("\n")
520 sys
.stdout
.write("\tCACHE HITS BY CACHE LIST:\n")
521 if 'cache_hits_by_cache_list' in arc
:
522 sys
.stdout
.write("\t Anonymously Used:\t\t%s\t%s\n" % (
523 arc
['cache_hits_by_cache_list']['anonymously_used']['per'],
524 arc
['cache_hits_by_cache_list']['anonymously_used']['num'],
527 sys
.stdout
.write("\t Most Recently Used:\t\t%s\t%s\n" % (
528 arc
['most_recently_used']['per'],
529 arc
['most_recently_used']['num'],
532 sys
.stdout
.write("\t Most Frequently Used:\t\t%s\t%s\n" % (
533 arc
['most_frequently_used']['per'],
534 arc
['most_frequently_used']['num'],
537 sys
.stdout
.write("\t Most Recently Used Ghost:\t%s\t%s\n" % (
538 arc
['most_recently_used_ghost']['per'],
539 arc
['most_recently_used_ghost']['num'],
542 sys
.stdout
.write("\t Most Frequently Used Ghost:\t%s\t%s\n" % (
543 arc
['most_frequently_used_ghost']['per'],
544 arc
['most_frequently_used_ghost']['num'],
548 sys
.stdout
.write("\n\tCACHE HITS BY DATA TYPE:\n")
549 sys
.stdout
.write("\t Demand Data:\t\t\t%s\t%s\n" % (
550 arc
["cache_hits_by_data_type"]['demand_data']['per'],
551 arc
["cache_hits_by_data_type"]['demand_data']['num'],
554 sys
.stdout
.write("\t Prefetch Data:\t\t%s\t%s\n" % (
555 arc
["cache_hits_by_data_type"]['prefetch_data']['per'],
556 arc
["cache_hits_by_data_type"]['prefetch_data']['num'],
559 sys
.stdout
.write("\t Demand Metadata:\t\t%s\t%s\n" % (
560 arc
["cache_hits_by_data_type"]['demand_metadata']['per'],
561 arc
["cache_hits_by_data_type"]['demand_metadata']['num'],
564 sys
.stdout
.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
565 arc
["cache_hits_by_data_type"]['prefetch_metadata']['per'],
566 arc
["cache_hits_by_data_type"]['prefetch_metadata']['num'],
570 sys
.stdout
.write("\n\tCACHE MISSES BY DATA TYPE:\n")
571 sys
.stdout
.write("\t Demand Data:\t\t\t%s\t%s\n" % (
572 arc
["cache_misses_by_data_type"]['demand_data']['per'],
573 arc
["cache_misses_by_data_type"]['demand_data']['num'],
576 sys
.stdout
.write("\t Prefetch Data:\t\t%s\t%s\n" % (
577 arc
["cache_misses_by_data_type"]['prefetch_data']['per'],
578 arc
["cache_misses_by_data_type"]['prefetch_data']['num'],
581 sys
.stdout
.write("\t Demand Metadata:\t\t%s\t%s\n" % (
582 arc
["cache_misses_by_data_type"]['demand_metadata']['per'],
583 arc
["cache_misses_by_data_type"]['demand_metadata']['num'],
586 sys
.stdout
.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
587 arc
["cache_misses_by_data_type"]['prefetch_metadata']['per'],
588 arc
["cache_misses_by_data_type"]['prefetch_metadata']['num'],
593 def get_l2arc_summary(Kstat
):
594 """Collection information on the L2ARC"""
598 l2_abort_lowmem
= Kstat
["kstat.zfs.misc.arcstats.l2_abort_lowmem"]
599 l2_cksum_bad
= Kstat
["kstat.zfs.misc.arcstats.l2_cksum_bad"]
600 l2_evict_lock_retry
= Kstat
["kstat.zfs.misc.arcstats.l2_evict_lock_retry"]
601 l2_evict_reading
= Kstat
["kstat.zfs.misc.arcstats.l2_evict_reading"]
602 l2_feeds
= Kstat
["kstat.zfs.misc.arcstats.l2_feeds"]
603 l2_free_on_write
= Kstat
["kstat.zfs.misc.arcstats.l2_free_on_write"]
604 l2_hdr_size
= Kstat
["kstat.zfs.misc.arcstats.l2_hdr_size"]
605 l2_hits
= Kstat
["kstat.zfs.misc.arcstats.l2_hits"]
606 l2_io_error
= Kstat
["kstat.zfs.misc.arcstats.l2_io_error"]
607 l2_misses
= Kstat
["kstat.zfs.misc.arcstats.l2_misses"]
608 l2_rw_clash
= Kstat
["kstat.zfs.misc.arcstats.l2_rw_clash"]
609 l2_size
= Kstat
["kstat.zfs.misc.arcstats.l2_size"]
610 l2_asize
= Kstat
["kstat.zfs.misc.arcstats.l2_asize"]
611 l2_writes_done
= Kstat
["kstat.zfs.misc.arcstats.l2_writes_done"]
612 l2_writes_error
= Kstat
["kstat.zfs.misc.arcstats.l2_writes_error"]
613 l2_writes_sent
= Kstat
["kstat.zfs.misc.arcstats.l2_writes_sent"]
615 l2_access_total
= (l2_hits
+ l2_misses
)
616 output
['l2_health_count'] = (l2_writes_error
+ l2_cksum_bad
+ l2_io_error
)
618 output
['l2_access_total'] = l2_access_total
619 output
['l2_size'] = l2_size
620 output
['l2_asize'] = l2_asize
622 if l2_size
> 0 and l2_access_total
> 0:
624 if output
['l2_health_count'] > 0:
625 output
["health"] = "DEGRADED"
627 output
["health"] = "HEALTHY"
629 output
["low_memory_aborts"] = fHits(l2_abort_lowmem
)
630 output
["free_on_write"] = fHits(l2_free_on_write
)
631 output
["rw_clashes"] = fHits(l2_rw_clash
)
632 output
["bad_checksums"] = fHits(l2_cksum_bad
)
633 output
["io_errors"] = fHits(l2_io_error
)
635 output
["l2_arc_size"] = {}
636 output
["l2_arc_size"]["adative"] = fBytes(l2_size
)
637 output
["l2_arc_size"]["actual"] = {
638 'per': fPerc(l2_asize
, l2_size
),
639 'num': fBytes(l2_asize
)
641 output
["l2_arc_size"]["head_size"] = {
642 'per': fPerc(l2_hdr_size
, l2_size
),
643 'num': fBytes(l2_hdr_size
),
646 output
["l2_arc_evicts"] = {}
647 output
["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry
)
648 output
["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading
)
650 output
['l2_arc_breakdown'] = {}
651 output
['l2_arc_breakdown']['value'] = fHits(l2_access_total
)
652 output
['l2_arc_breakdown']['hit_ratio'] = {
653 'per': fPerc(l2_hits
, l2_access_total
),
654 'num': fHits(l2_hits
),
656 output
['l2_arc_breakdown']['miss_ratio'] = {
657 'per': fPerc(l2_misses
, l2_access_total
),
658 'num': fHits(l2_misses
),
660 output
['l2_arc_breakdown']['feeds'] = fHits(l2_feeds
)
662 output
['l2_arc_buffer'] = {}
664 output
['l2_arc_writes'] = {}
665 output
['l2_writes_done'] = l2_writes_done
666 output
['l2_writes_sent'] = l2_writes_sent
667 if l2_writes_done
!= l2_writes_sent
:
668 output
['l2_arc_writes']['writes_sent'] = {
670 'num': fHits(l2_writes_sent
),
672 output
['l2_arc_writes']['done_ratio'] = {
673 'per': fPerc(l2_writes_done
, l2_writes_sent
),
674 'num': fHits(l2_writes_done
),
676 output
['l2_arc_writes']['error_ratio'] = {
677 'per': fPerc(l2_writes_error
, l2_writes_sent
),
678 'num': fHits(l2_writes_error
),
681 output
['l2_arc_writes']['writes_sent'] = {
683 'num': fHits(l2_writes_sent
),
689 def _l2arc_summary(Kstat
):
690 """Print information on the L2ARC"""
692 arc
= get_l2arc_summary(Kstat
)
694 if arc
['l2_size'] > 0 and arc
['l2_access_total'] > 0:
695 sys
.stdout
.write("L2 ARC Summary: ")
696 if arc
['l2_health_count'] > 0:
697 sys
.stdout
.write("(DEGRADED)\n")
699 sys
.stdout
.write("(HEALTHY)\n")
700 sys
.stdout
.write("\tLow Memory Aborts:\t\t\t%s\n" %
701 arc
['low_memory_aborts'])
702 sys
.stdout
.write("\tFree on Write:\t\t\t\t%s\n" % arc
['free_on_write'])
703 sys
.stdout
.write("\tR/W Clashes:\t\t\t\t%s\n" % arc
['rw_clashes'])
704 sys
.stdout
.write("\tBad Checksums:\t\t\t\t%s\n" % arc
['bad_checksums'])
705 sys
.stdout
.write("\tIO Errors:\t\t\t\t%s\n" % arc
['io_errors'])
706 sys
.stdout
.write("\n")
708 sys
.stdout
.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
709 arc
["l2_arc_size"]["adative"])
710 sys
.stdout
.write("\tCompressed:\t\t\t%s\t%s\n" % (
711 arc
["l2_arc_size"]["actual"]["per"],
712 arc
["l2_arc_size"]["actual"]["num"],
715 sys
.stdout
.write("\tHeader Size:\t\t\t%s\t%s\n" % (
716 arc
["l2_arc_size"]["head_size"]["per"],
717 arc
["l2_arc_size"]["head_size"]["num"],
720 sys
.stdout
.write("\n")
722 if arc
["l2_arc_evicts"]['lock_retries'] != '0' or \
723 arc
["l2_arc_evicts"]["reading"] != '0':
724 sys
.stdout
.write("L2 ARC Evicts:\n")
725 sys
.stdout
.write("\tLock Retries:\t\t\t\t%s\n" %
726 arc
["l2_arc_evicts"]['lock_retries'])
727 sys
.stdout
.write("\tUpon Reading:\t\t\t\t%s\n" %
728 arc
["l2_arc_evicts"]["reading"])
729 sys
.stdout
.write("\n")
731 sys
.stdout
.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
732 arc
['l2_arc_breakdown']['value'])
733 sys
.stdout
.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
734 arc
['l2_arc_breakdown']['hit_ratio']['per'],
735 arc
['l2_arc_breakdown']['hit_ratio']['num'],
739 sys
.stdout
.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
740 arc
['l2_arc_breakdown']['miss_ratio']['per'],
741 arc
['l2_arc_breakdown']['miss_ratio']['num'],
745 sys
.stdout
.write("\tFeeds:\t\t\t\t\t%s\n" %
746 arc
['l2_arc_breakdown']['feeds'])
747 sys
.stdout
.write("\n")
749 sys
.stdout
.write("L2 ARC Writes:\n")
750 if arc
['l2_writes_done'] != arc
['l2_writes_sent']:
751 sys
.stdout
.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % (
752 arc
['l2_arc_writes']['writes_sent']['value'],
753 arc
['l2_arc_writes']['writes_sent']['num'],
756 sys
.stdout
.write("\t Done Ratio:\t\t\t%s\t%s\n" % (
757 arc
['l2_arc_writes']['done_ratio']['per'],
758 arc
['l2_arc_writes']['done_ratio']['num'],
761 sys
.stdout
.write("\t Error Ratio:\t\t\t%s\t%s\n" % (
762 arc
['l2_arc_writes']['error_ratio']['per'],
763 arc
['l2_arc_writes']['error_ratio']['num'],
767 sys
.stdout
.write("\tWrites Sent:\t\t\t%s\t%s\n" % (
768 arc
['l2_arc_writes']['writes_sent']['per'],
769 arc
['l2_arc_writes']['writes_sent']['num'],
774 def get_dmu_summary(Kstat
):
775 """Collect information on the DMU"""
779 zfetch_hits
= Kstat
["kstat.zfs.misc.zfetchstats.hits"]
780 zfetch_misses
= Kstat
["kstat.zfs.misc.zfetchstats.misses"]
782 zfetch_access_total
= (zfetch_hits
+ zfetch_misses
)
783 output
['zfetch_access_total'] = zfetch_access_total
785 if zfetch_access_total
> 0:
787 output
['dmu']['efficiency'] = {}
788 output
['dmu']['efficiency']['value'] = fHits(zfetch_access_total
)
789 output
['dmu']['efficiency']['hit_ratio'] = {
790 'per': fPerc(zfetch_hits
, zfetch_access_total
),
791 'num': fHits(zfetch_hits
),
793 output
['dmu']['efficiency']['miss_ratio'] = {
794 'per': fPerc(zfetch_misses
, zfetch_access_total
),
795 'num': fHits(zfetch_misses
),
801 def _dmu_summary(Kstat
):
802 """Print information on the DMU"""
804 arc
= get_dmu_summary(Kstat
)
806 if arc
['zfetch_access_total'] > 0:
807 sys
.stdout
.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
808 arc
['dmu']['efficiency']['value'])
809 sys
.stdout
.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
810 arc
['dmu']['efficiency']['hit_ratio']['per'],
811 arc
['dmu']['efficiency']['hit_ratio']['num'],
814 sys
.stdout
.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
815 arc
['dmu']['efficiency']['miss_ratio']['per'],
816 arc
['dmu']['efficiency']['miss_ratio']['num'],
820 sys
.stdout
.write("\n")
823 def get_vdev_summary(Kstat
):
824 """Collect information on the VDEVs"""
828 vdev_cache_delegations
= \
829 Kstat
["kstat.zfs.misc.vdev_cache_stats.delegations"]
830 vdev_cache_misses
= Kstat
["kstat.zfs.misc.vdev_cache_stats.misses"]
831 vdev_cache_hits
= Kstat
["kstat.zfs.misc.vdev_cache_stats.hits"]
832 vdev_cache_total
= (vdev_cache_misses
+ vdev_cache_hits
+
833 vdev_cache_delegations
)
835 output
['vdev_cache_total'] = vdev_cache_total
837 if vdev_cache_total
> 0:
838 output
['summary'] = fHits(vdev_cache_total
)
839 output
['hit_ratio'] = {
840 'per': fPerc(vdev_cache_hits
, vdev_cache_total
),
841 'num': fHits(vdev_cache_hits
),
843 output
['miss_ratio'] = {
844 'per': fPerc(vdev_cache_misses
, vdev_cache_total
),
845 'num': fHits(vdev_cache_misses
),
847 output
['delegations'] = {
848 'per': fPerc(vdev_cache_delegations
, vdev_cache_total
),
849 'num': fHits(vdev_cache_delegations
),
855 def _vdev_summary(Kstat
):
856 """Print information on the VDEVs"""
858 arc
= get_vdev_summary(Kstat
)
860 if arc
['vdev_cache_total'] > 0:
861 sys
.stdout
.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc
['summary'])
862 sys
.stdout
.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
863 arc
['hit_ratio']['per'],
864 arc
['hit_ratio']['num'],
866 sys
.stdout
.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
867 arc
['miss_ratio']['per'],
868 arc
['miss_ratio']['num'],
870 sys
.stdout
.write("\tDelegations:\t\t\t%s\t%s\n" % (
871 arc
['delegations']['per'],
872 arc
['delegations']['num'],
876 def _tunable_summary(Kstat
):
877 """Print information on tunables, including descriptions if requested"""
879 global show_tunable_descriptions
880 global alternate_tunable_layout
882 names
= os
.listdir("/sys/module/zfs/parameters/")
886 with
open("/sys/module/zfs/parameters/" + name
) as f
:
888 values
[name
] = value
.strip()
892 if show_tunable_descriptions
:
894 command
= ["/sbin/modinfo", "zfs", "-0"]
897 p
= Popen(command
, stdin
=PIPE
, stdout
=PIPE
,
898 stderr
=PIPE
, shell
=False, close_fds
=True)
901 # By default, Python 2 returns a string as the first element of the
902 # tuple from p.communicate(), while Python 3 returns bytes which
903 # must be decoded first. The better way to do this would be with
904 # subprocess.run() or at least .check_output(), but this fails on
905 # CentOS 6 because of its old version of Python 2
906 desc
= bytes
.decode(p
.communicate()[0])
907 description_list
= desc
.strip().split('\0')
909 if p
.returncode
== 0:
910 for tunable
in description_list
:
911 if tunable
[0:5] == 'parm:':
912 tunable
= tunable
[5:].strip()
913 name
, description
= tunable
.split(':', 1)
915 description
= "Description unavailable"
916 descriptions
[name
] = description
918 sys
.stderr
.write("%s: '%s' exited with code %i\n" %
919 (sys
.argv
[0], command
[0], p
.returncode
))
920 sys
.stderr
.write("Tunable descriptions will be disabled.\n")
922 sys
.stderr
.write("%s: Cannot run '%s': %s\n" %
923 (sys
.argv
[0], command
[0], e
.strerror
))
924 sys
.stderr
.write("Tunable descriptions will be disabled.\n")
926 sys
.stdout
.write("ZFS Tunables:\n")
929 if alternate_tunable_layout
:
939 if show_tunable_descriptions
and name
in descriptions
:
940 sys
.stdout
.write("\t# %s\n" % descriptions
[name
])
942 sys
.stdout
.write(fmt
% (name
, values
[name
]))
956 """Print title string with date"""
958 daydate
= time
.strftime('%a %b %d %H:%M:%S %Y')
960 sys
.stdout
.write('\n'+'-'*72+'\n')
961 sys
.stdout
.write('ZFS Subsystem Report\t\t\t\t%s' % daydate
)
962 sys
.stdout
.write('\n')
966 """Print usage information"""
968 sys
.stdout
.write("Usage: arc_summary.py [-h] [-a] [-d] [-p PAGE]\n\n")
969 sys
.stdout
.write("\t -h, --help : "
970 "Print this help message and exit\n")
971 sys
.stdout
.write("\t -a, --alternate : "
972 "Show an alternate sysctl layout\n")
973 sys
.stdout
.write("\t -d, --description : "
974 "Show the sysctl descriptions\n")
975 sys
.stdout
.write("\t -p PAGE, --page=PAGE : "
976 "Select a single output page to display,\n")
977 sys
.stdout
.write("\t "
978 "should be an integer between 1 and " +
979 str(len(unSub
)) + "\n\n")
980 sys
.stdout
.write("Examples:\n")
981 sys
.stdout
.write("\tarc_summary.py -a\n")
982 sys
.stdout
.write("\tarc_summary.py -p 4\n")
983 sys
.stdout
.write("\tarc_summary.py -ad\n")
984 sys
.stdout
.write("\tarc_summary.py --page=2\n")
990 global show_tunable_descriptions
991 global alternate_tunable_layout
994 opts
, args
= getopt
.getopt(
996 "adp:h", ["alternate", "description", "page=", "help"]
998 except getopt
.error
as e
:
999 sys
.stderr
.write("Error: %s\n" % e
.msg
)
1004 for opt
, arg
in opts
:
1005 if opt
in ('-a', '--alternate'):
1007 if opt
in ('-d', '--description'):
1009 if opt
in ('-p', '--page'):
1011 if opt
in ('-h', '--help'):
1017 alternate_tunable_layout
= 'a' in args
1018 show_tunable_descriptions
= 'd' in args
1024 pages
.append(unSub
[int(args
['p']) - 1])
1026 sys
.stderr
.write('the argument to -p must be between 1 and ' +
1027 str(len(unSub
)) + '\n')
1035 sys
.stdout
.write("\n")
1038 if __name__
== '__main__':