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
57 show_tunable_descriptions
= False
58 alternate_tunable_layout
= False
61 def handle_Exception(ex_cls
, ex
, tb
):
63 if ex
.errno
== errno
.EPIPE
:
66 if ex
is KeyboardInterrupt:
70 sys
.excepthook
= handle_Exception
74 """Collect information on the ZFS subsystem from the /proc virtual
75 file system. The name "kstat" is a holdover from the Solaris utility
79 def load_proc_kstats(fn
, namespace
):
80 """Collect information on a specific subsystem of the ARC"""
82 kstats
= [line
.strip() for line
in open(fn
)]
86 name
, _
, value
= kstat
.split()
87 Kstat
[namespace
+ name
] = D(value
)
90 load_proc_kstats('/proc/spl/kstat/zfs/arcstats',
91 'kstat.zfs.misc.arcstats.')
92 load_proc_kstats('/proc/spl/kstat/zfs/zfetchstats',
93 'kstat.zfs.misc.zfetchstats.')
94 load_proc_kstats('/proc/spl/kstat/zfs/vdev_cache_stats',
95 'kstat.zfs.misc.vdev_cache_stats.')
101 """Return human-readable representation of a byte value in
102 powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
103 points. Values smaller than one KiB are returned without
108 [2**80, "YiB"], # yobibytes (yotta)
109 [2**70, "ZiB"], # zebibytes (zetta)
110 [2**60, "EiB"], # exbibytes (exa)
111 [2**50, "PiB"], # pebibytes (peta)
112 [2**40, "TiB"], # tebibytes (tera)
113 [2**30, "GiB"], # gibibytes (giga)
114 [2**20, "MiB"], # mebibytes (mega)
115 [2**10, "KiB"]] # kibibytes (kilo)
119 for limit
, unit
in prefixes
:
125 result
= "%0.2f\t%s" % (value
, unit
)
129 result
= "%d\tBytes" % b
135 """Create a human-readable representation of the number of hits.
136 The single-letter symbols used are SI to avoid the confusion caused
137 by the different "short scale" and "long scale" representations in
138 English, which use the same words for different values. See
139 https://en.wikipedia.org/wiki/Names_of_large_numbers and
140 https://physics.nist.gov/cuu/Units/prefixes.html
144 [10**24, 'Y'], # yotta (septillion)
145 [10**21, 'Z'], # zetta (sextillion)
146 [10**18, 'E'], # exa (quintrillion)
147 [10**15, 'P'], # peta (quadrillion)
148 [10**12, 'T'], # tera (trillion)
149 [10**9, 'G'], # giga (billion)
150 [10**6, 'M'], # mega (million)
151 [10**3, 'k']] # kilo (thousand)
155 for limit
, symbol
in numbers
:
161 result
= "%0.2f%s" % (value
, symbol
)
170 def fPerc(lVal
=0, rVal
=0, Decimal
=2):
171 """Calculate percentage value and return in human-readable format"""
174 return str("%0." + str(Decimal
) + "f") % (100 * (lVal
/ rVal
)) + "%"
176 return str("%0." + str(Decimal
) + "f") % 100 + "%"
179 def get_arc_summary(Kstat
):
180 """Collect general data on the ARC"""
183 memory_throttle_count
= Kstat
[
184 "kstat.zfs.misc.arcstats.memory_throttle_count"
187 if memory_throttle_count
> 0:
188 output
['health'] = 'THROTTLED'
190 output
['health'] = 'HEALTHY'
192 output
['memory_throttle_count'] = fHits(memory_throttle_count
)
195 deleted
= Kstat
["kstat.zfs.misc.arcstats.deleted"]
196 mutex_miss
= Kstat
["kstat.zfs.misc.arcstats.mutex_miss"]
197 evict_skip
= Kstat
["kstat.zfs.misc.arcstats.evict_skip"]
200 output
["arc_misc"] = {}
201 output
["arc_misc"]["deleted"] = fHits(deleted
)
202 output
["arc_misc"]['mutex_miss'] = fHits(mutex_miss
)
203 output
["arc_misc"]['evict_skips'] = fHits(evict_skip
)
206 arc_size
= Kstat
["kstat.zfs.misc.arcstats.size"]
207 mru_size
= Kstat
["kstat.zfs.misc.arcstats.mru_size"]
208 mfu_size
= Kstat
["kstat.zfs.misc.arcstats.mfu_size"]
209 meta_limit
= Kstat
["kstat.zfs.misc.arcstats.arc_meta_limit"]
210 meta_size
= Kstat
["kstat.zfs.misc.arcstats.arc_meta_used"]
211 dnode_limit
= Kstat
["kstat.zfs.misc.arcstats.arc_dnode_limit"]
212 dnode_size
= Kstat
["kstat.zfs.misc.arcstats.dnode_size"]
213 target_max_size
= Kstat
["kstat.zfs.misc.arcstats.c_max"]
214 target_min_size
= Kstat
["kstat.zfs.misc.arcstats.c_min"]
215 target_size
= Kstat
["kstat.zfs.misc.arcstats.c"]
217 target_size_ratio
= (target_max_size
/ target_min_size
)
220 output
['arc_sizing'] = {}
221 output
['arc_sizing']['arc_size'] = {
222 'per': fPerc(arc_size
, target_max_size
),
223 'num': fBytes(arc_size
),
225 output
['arc_sizing']['target_max_size'] = {
226 'ratio': target_size_ratio
,
227 'num': fBytes(target_max_size
),
229 output
['arc_sizing']['target_min_size'] = {
230 'per': fPerc(target_min_size
, target_max_size
),
231 'num': fBytes(target_min_size
),
233 output
['arc_sizing']['target_size'] = {
234 'per': fPerc(target_size
, target_max_size
),
235 'num': fBytes(target_size
),
237 output
['arc_sizing']['meta_limit'] = {
238 'per': fPerc(meta_limit
, target_max_size
),
239 'num': fBytes(meta_limit
),
241 output
['arc_sizing']['meta_size'] = {
242 'per': fPerc(meta_size
, meta_limit
),
243 'num': fBytes(meta_size
),
245 output
['arc_sizing']['dnode_limit'] = {
246 'per': fPerc(dnode_limit
, meta_limit
),
247 'num': fBytes(dnode_limit
),
249 output
['arc_sizing']['dnode_size'] = {
250 'per': fPerc(dnode_size
, dnode_limit
),
251 'num': fBytes(dnode_size
),
255 output
['arc_hash_break'] = {}
256 output
['arc_hash_break']['hash_chain_max'] = Kstat
[
257 "kstat.zfs.misc.arcstats.hash_chain_max"
259 output
['arc_hash_break']['hash_chains'] = Kstat
[
260 "kstat.zfs.misc.arcstats.hash_chains"
262 output
['arc_hash_break']['hash_collisions'] = Kstat
[
263 "kstat.zfs.misc.arcstats.hash_collisions"
265 output
['arc_hash_break']['hash_elements'] = Kstat
[
266 "kstat.zfs.misc.arcstats.hash_elements"
268 output
['arc_hash_break']['hash_elements_max'] = Kstat
[
269 "kstat.zfs.misc.arcstats.hash_elements_max"
272 output
['arc_size_break'] = {}
273 output
['arc_size_break']['recently_used_cache_size'] = {
274 'per': fPerc(mru_size
, mru_size
+ mfu_size
),
275 'num': fBytes(mru_size
),
277 output
['arc_size_break']['frequently_used_cache_size'] = {
278 'per': fPerc(mfu_size
, mru_size
+ mfu_size
),
279 'num': fBytes(mfu_size
),
283 hash_chain_max
= Kstat
["kstat.zfs.misc.arcstats.hash_chain_max"]
284 hash_chains
= Kstat
["kstat.zfs.misc.arcstats.hash_chains"]
285 hash_collisions
= Kstat
["kstat.zfs.misc.arcstats.hash_collisions"]
286 hash_elements
= Kstat
["kstat.zfs.misc.arcstats.hash_elements"]
287 hash_elements_max
= Kstat
["kstat.zfs.misc.arcstats.hash_elements_max"]
289 output
['arc_hash_break'] = {}
290 output
['arc_hash_break']['elements_max'] = fHits(hash_elements_max
)
291 output
['arc_hash_break']['elements_current'] = {
292 'per': fPerc(hash_elements
, hash_elements_max
),
293 'num': fHits(hash_elements
),
295 output
['arc_hash_break']['collisions'] = fHits(hash_collisions
)
296 output
['arc_hash_break']['chain_max'] = fHits(hash_chain_max
)
297 output
['arc_hash_break']['chains'] = fHits(hash_chains
)
302 def _arc_summary(Kstat
):
303 """Print information on the ARC"""
306 arc
= get_arc_summary(Kstat
)
308 sys
.stdout
.write("ARC Summary: (%s)\n" % arc
['health'])
310 sys
.stdout
.write("\tMemory Throttle Count:\t\t\t%s\n" %
311 arc
['memory_throttle_count'])
312 sys
.stdout
.write("\n")
315 sys
.stdout
.write("ARC Misc:\n")
316 sys
.stdout
.write("\tDeleted:\t\t\t\t%s\n" % arc
['arc_misc']['deleted'])
317 sys
.stdout
.write("\tMutex Misses:\t\t\t\t%s\n" %
318 arc
['arc_misc']['mutex_miss'])
319 sys
.stdout
.write("\tEvict Skips:\t\t\t\t%s\n" %
320 arc
['arc_misc']['evict_skips'])
321 sys
.stdout
.write("\n")
324 sys
.stdout
.write("ARC Size:\t\t\t\t%s\t%s\n" % (
325 arc
['arc_sizing']['arc_size']['per'],
326 arc
['arc_sizing']['arc_size']['num']
329 sys
.stdout
.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % (
330 arc
['arc_sizing']['target_size']['per'],
331 arc
['arc_sizing']['target_size']['num'],
335 sys
.stdout
.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % (
336 arc
['arc_sizing']['target_min_size']['per'],
337 arc
['arc_sizing']['target_min_size']['num'],
341 sys
.stdout
.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % (
342 arc
['arc_sizing']['target_max_size']['ratio'],
343 arc
['arc_sizing']['target_max_size']['num'],
347 sys
.stdout
.write("\nARC Size Breakdown:\n")
348 sys
.stdout
.write("\tRecently Used Cache Size:\t%s\t%s\n" % (
349 arc
['arc_size_break']['recently_used_cache_size']['per'],
350 arc
['arc_size_break']['recently_used_cache_size']['num'],
353 sys
.stdout
.write("\tFrequently Used Cache Size:\t%s\t%s\n" % (
354 arc
['arc_size_break']['frequently_used_cache_size']['per'],
355 arc
['arc_size_break']['frequently_used_cache_size']['num'],
358 sys
.stdout
.write("\tMetadata Size (Hard Limit):\t%s\t%s\n" % (
359 arc
['arc_sizing']['meta_limit']['per'],
360 arc
['arc_sizing']['meta_limit']['num'],
363 sys
.stdout
.write("\tMetadata Size:\t\t\t%s\t%s\n" % (
364 arc
['arc_sizing']['meta_size']['per'],
365 arc
['arc_sizing']['meta_size']['num'],
368 sys
.stdout
.write("\tDnode Size (Hard Limit):\t%s\t%s\n" % (
369 arc
['arc_sizing']['dnode_limit']['per'],
370 arc
['arc_sizing']['dnode_limit']['num'],
373 sys
.stdout
.write("\tDnode Size:\t\t\t%s\t%s\n" % (
374 arc
['arc_sizing']['dnode_size']['per'],
375 arc
['arc_sizing']['dnode_size']['num'],
379 sys
.stdout
.write("\n")
382 sys
.stdout
.write("ARC Hash Breakdown:\n")
383 sys
.stdout
.write("\tElements Max:\t\t\t\t%s\n" %
384 arc
['arc_hash_break']['elements_max'])
385 sys
.stdout
.write("\tElements Current:\t\t%s\t%s\n" % (
386 arc
['arc_hash_break']['elements_current']['per'],
387 arc
['arc_hash_break']['elements_current']['num'],
390 sys
.stdout
.write("\tCollisions:\t\t\t\t%s\n" %
391 arc
['arc_hash_break']['collisions'])
392 sys
.stdout
.write("\tChain Max:\t\t\t\t%s\n" %
393 arc
['arc_hash_break']['chain_max'])
394 sys
.stdout
.write("\tChains:\t\t\t\t\t%s\n" %
395 arc
['arc_hash_break']['chains'])
398 def get_arc_efficiency(Kstat
):
399 """Collect information on the efficiency of the ARC"""
403 arc_hits
= Kstat
["kstat.zfs.misc.arcstats.hits"]
404 arc_misses
= Kstat
["kstat.zfs.misc.arcstats.misses"]
405 demand_data_hits
= Kstat
["kstat.zfs.misc.arcstats.demand_data_hits"]
406 demand_data_misses
= Kstat
["kstat.zfs.misc.arcstats.demand_data_misses"]
407 demand_metadata_hits
= Kstat
[
408 "kstat.zfs.misc.arcstats.demand_metadata_hits"
410 demand_metadata_misses
= Kstat
[
411 "kstat.zfs.misc.arcstats.demand_metadata_misses"
413 mfu_ghost_hits
= Kstat
["kstat.zfs.misc.arcstats.mfu_ghost_hits"]
414 mfu_hits
= Kstat
["kstat.zfs.misc.arcstats.mfu_hits"]
415 mru_ghost_hits
= Kstat
["kstat.zfs.misc.arcstats.mru_ghost_hits"]
416 mru_hits
= Kstat
["kstat.zfs.misc.arcstats.mru_hits"]
417 prefetch_data_hits
= Kstat
["kstat.zfs.misc.arcstats.prefetch_data_hits"]
418 prefetch_data_misses
= Kstat
[
419 "kstat.zfs.misc.arcstats.prefetch_data_misses"
421 prefetch_metadata_hits
= Kstat
[
422 "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
424 prefetch_metadata_misses
= Kstat
[
425 "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
428 anon_hits
= arc_hits
- (
429 mfu_hits
+ mru_hits
+ mfu_ghost_hits
+ mru_ghost_hits
431 arc_accesses_total
= (arc_hits
+ arc_misses
)
432 demand_data_total
= (demand_data_hits
+ demand_data_misses
)
433 prefetch_data_total
= (prefetch_data_hits
+ prefetch_data_misses
)
434 real_hits
= (mfu_hits
+ mru_hits
)
436 output
["total_accesses"] = fHits(arc_accesses_total
)
437 output
["cache_hit_ratio"] = {
438 'per': fPerc(arc_hits
, arc_accesses_total
),
439 'num': fHits(arc_hits
),
441 output
["cache_miss_ratio"] = {
442 'per': fPerc(arc_misses
, arc_accesses_total
),
443 'num': fHits(arc_misses
),
445 output
["actual_hit_ratio"] = {
446 'per': fPerc(real_hits
, arc_accesses_total
),
447 'num': fHits(real_hits
),
449 output
["data_demand_efficiency"] = {
450 'per': fPerc(demand_data_hits
, demand_data_total
),
451 'num': fHits(demand_data_total
),
454 if prefetch_data_total
> 0:
455 output
["data_prefetch_efficiency"] = {
456 'per': fPerc(prefetch_data_hits
, prefetch_data_total
),
457 'num': fHits(prefetch_data_total
),
461 output
["cache_hits_by_cache_list"] = {}
462 output
["cache_hits_by_cache_list"]["anonymously_used"] = {
463 'per': fPerc(anon_hits
, arc_hits
),
464 'num': fHits(anon_hits
),
467 output
["most_recently_used"] = {
468 'per': fPerc(mru_hits
, arc_hits
),
469 'num': fHits(mru_hits
),
471 output
["most_frequently_used"] = {
472 'per': fPerc(mfu_hits
, arc_hits
),
473 'num': fHits(mfu_hits
),
475 output
["most_recently_used_ghost"] = {
476 'per': fPerc(mru_ghost_hits
, arc_hits
),
477 'num': fHits(mru_ghost_hits
),
479 output
["most_frequently_used_ghost"] = {
480 'per': fPerc(mfu_ghost_hits
, arc_hits
),
481 'num': fHits(mfu_ghost_hits
),
484 output
["cache_hits_by_data_type"] = {}
485 output
["cache_hits_by_data_type"]["demand_data"] = {
486 'per': fPerc(demand_data_hits
, arc_hits
),
487 'num': fHits(demand_data_hits
),
489 output
["cache_hits_by_data_type"]["prefetch_data"] = {
490 'per': fPerc(prefetch_data_hits
, arc_hits
),
491 'num': fHits(prefetch_data_hits
),
493 output
["cache_hits_by_data_type"]["demand_metadata"] = {
494 'per': fPerc(demand_metadata_hits
, arc_hits
),
495 'num': fHits(demand_metadata_hits
),
497 output
["cache_hits_by_data_type"]["prefetch_metadata"] = {
498 'per': fPerc(prefetch_metadata_hits
, arc_hits
),
499 'num': fHits(prefetch_metadata_hits
),
502 output
["cache_misses_by_data_type"] = {}
503 output
["cache_misses_by_data_type"]["demand_data"] = {
504 'per': fPerc(demand_data_misses
, arc_misses
),
505 'num': fHits(demand_data_misses
),
507 output
["cache_misses_by_data_type"]["prefetch_data"] = {
508 'per': fPerc(prefetch_data_misses
, arc_misses
),
509 'num': fHits(prefetch_data_misses
),
511 output
["cache_misses_by_data_type"]["demand_metadata"] = {
512 'per': fPerc(demand_metadata_misses
, arc_misses
),
513 'num': fHits(demand_metadata_misses
),
515 output
["cache_misses_by_data_type"]["prefetch_metadata"] = {
516 'per': fPerc(prefetch_metadata_misses
, arc_misses
),
517 'num': fHits(prefetch_metadata_misses
),
523 def _arc_efficiency(Kstat
):
524 """Print information on the efficiency of the ARC"""
526 arc
= get_arc_efficiency(Kstat
)
528 sys
.stdout
.write("ARC Total accesses:\t\t\t\t\t%s\n" %
529 arc
['total_accesses'])
530 sys
.stdout
.write("\tCache Hit Ratio:\t\t%s\t%s\n" % (
531 arc
['cache_hit_ratio']['per'],
532 arc
['cache_hit_ratio']['num'],
535 sys
.stdout
.write("\tCache Miss Ratio:\t\t%s\t%s\n" % (
536 arc
['cache_miss_ratio']['per'],
537 arc
['cache_miss_ratio']['num'],
541 sys
.stdout
.write("\tActual Hit Ratio:\t\t%s\t%s\n" % (
542 arc
['actual_hit_ratio']['per'],
543 arc
['actual_hit_ratio']['num'],
547 sys
.stdout
.write("\n")
548 sys
.stdout
.write("\tData Demand Efficiency:\t\t%s\t%s\n" % (
549 arc
['data_demand_efficiency']['per'],
550 arc
['data_demand_efficiency']['num'],
554 if 'data_prefetch_efficiency' in arc
:
555 sys
.stdout
.write("\tData Prefetch Efficiency:\t%s\t%s\n" % (
556 arc
['data_prefetch_efficiency']['per'],
557 arc
['data_prefetch_efficiency']['num'],
560 sys
.stdout
.write("\n")
562 sys
.stdout
.write("\tCACHE HITS BY CACHE LIST:\n")
563 if 'cache_hits_by_cache_list' in arc
:
564 sys
.stdout
.write("\t Anonymously Used:\t\t%s\t%s\n" % (
565 arc
['cache_hits_by_cache_list']['anonymously_used']['per'],
566 arc
['cache_hits_by_cache_list']['anonymously_used']['num'],
569 sys
.stdout
.write("\t Most Recently Used:\t\t%s\t%s\n" % (
570 arc
['most_recently_used']['per'],
571 arc
['most_recently_used']['num'],
574 sys
.stdout
.write("\t Most Frequently Used:\t\t%s\t%s\n" % (
575 arc
['most_frequently_used']['per'],
576 arc
['most_frequently_used']['num'],
579 sys
.stdout
.write("\t Most Recently Used Ghost:\t%s\t%s\n" % (
580 arc
['most_recently_used_ghost']['per'],
581 arc
['most_recently_used_ghost']['num'],
584 sys
.stdout
.write("\t Most Frequently Used Ghost:\t%s\t%s\n" % (
585 arc
['most_frequently_used_ghost']['per'],
586 arc
['most_frequently_used_ghost']['num'],
590 sys
.stdout
.write("\n\tCACHE HITS BY DATA TYPE:\n")
591 sys
.stdout
.write("\t Demand Data:\t\t\t%s\t%s\n" % (
592 arc
["cache_hits_by_data_type"]['demand_data']['per'],
593 arc
["cache_hits_by_data_type"]['demand_data']['num'],
596 sys
.stdout
.write("\t Prefetch Data:\t\t%s\t%s\n" % (
597 arc
["cache_hits_by_data_type"]['prefetch_data']['per'],
598 arc
["cache_hits_by_data_type"]['prefetch_data']['num'],
601 sys
.stdout
.write("\t Demand Metadata:\t\t%s\t%s\n" % (
602 arc
["cache_hits_by_data_type"]['demand_metadata']['per'],
603 arc
["cache_hits_by_data_type"]['demand_metadata']['num'],
606 sys
.stdout
.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
607 arc
["cache_hits_by_data_type"]['prefetch_metadata']['per'],
608 arc
["cache_hits_by_data_type"]['prefetch_metadata']['num'],
612 sys
.stdout
.write("\n\tCACHE MISSES BY DATA TYPE:\n")
613 sys
.stdout
.write("\t Demand Data:\t\t\t%s\t%s\n" % (
614 arc
["cache_misses_by_data_type"]['demand_data']['per'],
615 arc
["cache_misses_by_data_type"]['demand_data']['num'],
618 sys
.stdout
.write("\t Prefetch Data:\t\t%s\t%s\n" % (
619 arc
["cache_misses_by_data_type"]['prefetch_data']['per'],
620 arc
["cache_misses_by_data_type"]['prefetch_data']['num'],
623 sys
.stdout
.write("\t Demand Metadata:\t\t%s\t%s\n" % (
624 arc
["cache_misses_by_data_type"]['demand_metadata']['per'],
625 arc
["cache_misses_by_data_type"]['demand_metadata']['num'],
628 sys
.stdout
.write("\t Prefetch Metadata:\t\t%s\t%s\n" % (
629 arc
["cache_misses_by_data_type"]['prefetch_metadata']['per'],
630 arc
["cache_misses_by_data_type"]['prefetch_metadata']['num'],
635 def get_l2arc_summary(Kstat
):
636 """Collection information on the L2ARC"""
640 l2_abort_lowmem
= Kstat
["kstat.zfs.misc.arcstats.l2_abort_lowmem"]
641 l2_cksum_bad
= Kstat
["kstat.zfs.misc.arcstats.l2_cksum_bad"]
642 l2_evict_lock_retry
= Kstat
["kstat.zfs.misc.arcstats.l2_evict_lock_retry"]
643 l2_evict_reading
= Kstat
["kstat.zfs.misc.arcstats.l2_evict_reading"]
644 l2_feeds
= Kstat
["kstat.zfs.misc.arcstats.l2_feeds"]
645 l2_free_on_write
= Kstat
["kstat.zfs.misc.arcstats.l2_free_on_write"]
646 l2_hdr_size
= Kstat
["kstat.zfs.misc.arcstats.l2_hdr_size"]
647 l2_hits
= Kstat
["kstat.zfs.misc.arcstats.l2_hits"]
648 l2_io_error
= Kstat
["kstat.zfs.misc.arcstats.l2_io_error"]
649 l2_misses
= Kstat
["kstat.zfs.misc.arcstats.l2_misses"]
650 l2_rw_clash
= Kstat
["kstat.zfs.misc.arcstats.l2_rw_clash"]
651 l2_size
= Kstat
["kstat.zfs.misc.arcstats.l2_size"]
652 l2_asize
= Kstat
["kstat.zfs.misc.arcstats.l2_asize"]
653 l2_writes_done
= Kstat
["kstat.zfs.misc.arcstats.l2_writes_done"]
654 l2_writes_error
= Kstat
["kstat.zfs.misc.arcstats.l2_writes_error"]
655 l2_writes_sent
= Kstat
["kstat.zfs.misc.arcstats.l2_writes_sent"]
657 l2_access_total
= (l2_hits
+ l2_misses
)
658 output
['l2_health_count'] = (l2_writes_error
+ l2_cksum_bad
+ l2_io_error
)
660 output
['l2_access_total'] = l2_access_total
661 output
['l2_size'] = l2_size
662 output
['l2_asize'] = l2_asize
664 if l2_size
> 0 and l2_access_total
> 0:
666 if output
['l2_health_count'] > 0:
667 output
["health"] = "DEGRADED"
669 output
["health"] = "HEALTHY"
671 output
["low_memory_aborts"] = fHits(l2_abort_lowmem
)
672 output
["free_on_write"] = fHits(l2_free_on_write
)
673 output
["rw_clashes"] = fHits(l2_rw_clash
)
674 output
["bad_checksums"] = fHits(l2_cksum_bad
)
675 output
["io_errors"] = fHits(l2_io_error
)
677 output
["l2_arc_size"] = {}
678 output
["l2_arc_size"]["adative"] = fBytes(l2_size
)
679 output
["l2_arc_size"]["actual"] = {
680 'per': fPerc(l2_asize
, l2_size
),
681 'num': fBytes(l2_asize
)
683 output
["l2_arc_size"]["head_size"] = {
684 'per': fPerc(l2_hdr_size
, l2_size
),
685 'num': fBytes(l2_hdr_size
),
688 output
["l2_arc_evicts"] = {}
689 output
["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry
)
690 output
["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading
)
692 output
['l2_arc_breakdown'] = {}
693 output
['l2_arc_breakdown']['value'] = fHits(l2_access_total
)
694 output
['l2_arc_breakdown']['hit_ratio'] = {
695 'per': fPerc(l2_hits
, l2_access_total
),
696 'num': fHits(l2_hits
),
698 output
['l2_arc_breakdown']['miss_ratio'] = {
699 'per': fPerc(l2_misses
, l2_access_total
),
700 'num': fHits(l2_misses
),
702 output
['l2_arc_breakdown']['feeds'] = fHits(l2_feeds
)
704 output
['l2_arc_buffer'] = {}
706 output
['l2_arc_writes'] = {}
707 output
['l2_writes_done'] = l2_writes_done
708 output
['l2_writes_sent'] = l2_writes_sent
709 if l2_writes_done
!= l2_writes_sent
:
710 output
['l2_arc_writes']['writes_sent'] = {
712 'num': fHits(l2_writes_sent
),
714 output
['l2_arc_writes']['done_ratio'] = {
715 'per': fPerc(l2_writes_done
, l2_writes_sent
),
716 'num': fHits(l2_writes_done
),
718 output
['l2_arc_writes']['error_ratio'] = {
719 'per': fPerc(l2_writes_error
, l2_writes_sent
),
720 'num': fHits(l2_writes_error
),
723 output
['l2_arc_writes']['writes_sent'] = {
725 'num': fHits(l2_writes_sent
),
731 def _l2arc_summary(Kstat
):
732 """Print information on the L2ARC"""
734 arc
= get_l2arc_summary(Kstat
)
736 if arc
['l2_size'] > 0 and arc
['l2_access_total'] > 0:
737 sys
.stdout
.write("L2 ARC Summary: ")
738 if arc
['l2_health_count'] > 0:
739 sys
.stdout
.write("(DEGRADED)\n")
741 sys
.stdout
.write("(HEALTHY)\n")
742 sys
.stdout
.write("\tLow Memory Aborts:\t\t\t%s\n" %
743 arc
['low_memory_aborts'])
744 sys
.stdout
.write("\tFree on Write:\t\t\t\t%s\n" % arc
['free_on_write'])
745 sys
.stdout
.write("\tR/W Clashes:\t\t\t\t%s\n" % arc
['rw_clashes'])
746 sys
.stdout
.write("\tBad Checksums:\t\t\t\t%s\n" % arc
['bad_checksums'])
747 sys
.stdout
.write("\tIO Errors:\t\t\t\t%s\n" % arc
['io_errors'])
748 sys
.stdout
.write("\n")
750 sys
.stdout
.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
751 arc
["l2_arc_size"]["adative"])
752 sys
.stdout
.write("\tCompressed:\t\t\t%s\t%s\n" % (
753 arc
["l2_arc_size"]["actual"]["per"],
754 arc
["l2_arc_size"]["actual"]["num"],
757 sys
.stdout
.write("\tHeader Size:\t\t\t%s\t%s\n" % (
758 arc
["l2_arc_size"]["head_size"]["per"],
759 arc
["l2_arc_size"]["head_size"]["num"],
762 sys
.stdout
.write("\n")
764 if arc
["l2_arc_evicts"]['lock_retries'] != '0' or \
765 arc
["l2_arc_evicts"]["reading"] != '0':
766 sys
.stdout
.write("L2 ARC Evicts:\n")
767 sys
.stdout
.write("\tLock Retries:\t\t\t\t%s\n" %
768 arc
["l2_arc_evicts"]['lock_retries'])
769 sys
.stdout
.write("\tUpon Reading:\t\t\t\t%s\n" %
770 arc
["l2_arc_evicts"]["reading"])
771 sys
.stdout
.write("\n")
773 sys
.stdout
.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
774 arc
['l2_arc_breakdown']['value'])
775 sys
.stdout
.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
776 arc
['l2_arc_breakdown']['hit_ratio']['per'],
777 arc
['l2_arc_breakdown']['hit_ratio']['num'],
781 sys
.stdout
.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
782 arc
['l2_arc_breakdown']['miss_ratio']['per'],
783 arc
['l2_arc_breakdown']['miss_ratio']['num'],
787 sys
.stdout
.write("\tFeeds:\t\t\t\t\t%s\n" %
788 arc
['l2_arc_breakdown']['feeds'])
789 sys
.stdout
.write("\n")
791 sys
.stdout
.write("L2 ARC Writes:\n")
792 if arc
['l2_writes_done'] != arc
['l2_writes_sent']:
793 sys
.stdout
.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % (
794 arc
['l2_arc_writes']['writes_sent']['value'],
795 arc
['l2_arc_writes']['writes_sent']['num'],
798 sys
.stdout
.write("\t Done Ratio:\t\t\t%s\t%s\n" % (
799 arc
['l2_arc_writes']['done_ratio']['per'],
800 arc
['l2_arc_writes']['done_ratio']['num'],
803 sys
.stdout
.write("\t Error Ratio:\t\t\t%s\t%s\n" % (
804 arc
['l2_arc_writes']['error_ratio']['per'],
805 arc
['l2_arc_writes']['error_ratio']['num'],
809 sys
.stdout
.write("\tWrites Sent:\t\t\t%s\t%s\n" % (
810 arc
['l2_arc_writes']['writes_sent']['per'],
811 arc
['l2_arc_writes']['writes_sent']['num'],
816 def get_dmu_summary(Kstat
):
817 """Collect information on the DMU"""
821 zfetch_hits
= Kstat
["kstat.zfs.misc.zfetchstats.hits"]
822 zfetch_misses
= Kstat
["kstat.zfs.misc.zfetchstats.misses"]
824 zfetch_access_total
= (zfetch_hits
+ zfetch_misses
)
825 output
['zfetch_access_total'] = zfetch_access_total
827 if zfetch_access_total
> 0:
829 output
['dmu']['efficiency'] = {}
830 output
['dmu']['efficiency']['value'] = fHits(zfetch_access_total
)
831 output
['dmu']['efficiency']['hit_ratio'] = {
832 'per': fPerc(zfetch_hits
, zfetch_access_total
),
833 'num': fHits(zfetch_hits
),
835 output
['dmu']['efficiency']['miss_ratio'] = {
836 'per': fPerc(zfetch_misses
, zfetch_access_total
),
837 'num': fHits(zfetch_misses
),
843 def _dmu_summary(Kstat
):
844 """Print information on the DMU"""
846 arc
= get_dmu_summary(Kstat
)
848 if arc
['zfetch_access_total'] > 0:
849 sys
.stdout
.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
850 arc
['dmu']['efficiency']['value'])
851 sys
.stdout
.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
852 arc
['dmu']['efficiency']['hit_ratio']['per'],
853 arc
['dmu']['efficiency']['hit_ratio']['num'],
856 sys
.stdout
.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
857 arc
['dmu']['efficiency']['miss_ratio']['per'],
858 arc
['dmu']['efficiency']['miss_ratio']['num'],
862 sys
.stdout
.write("\n")
865 def get_vdev_summary(Kstat
):
866 """Collect information on the VDEVs"""
870 vdev_cache_delegations
= \
871 Kstat
["kstat.zfs.misc.vdev_cache_stats.delegations"]
872 vdev_cache_misses
= Kstat
["kstat.zfs.misc.vdev_cache_stats.misses"]
873 vdev_cache_hits
= Kstat
["kstat.zfs.misc.vdev_cache_stats.hits"]
874 vdev_cache_total
= (vdev_cache_misses
+ vdev_cache_hits
+
875 vdev_cache_delegations
)
877 output
['vdev_cache_total'] = vdev_cache_total
879 if vdev_cache_total
> 0:
880 output
['summary'] = fHits(vdev_cache_total
)
881 output
['hit_ratio'] = {
882 'per': fPerc(vdev_cache_hits
, vdev_cache_total
),
883 'num': fHits(vdev_cache_hits
),
885 output
['miss_ratio'] = {
886 'per': fPerc(vdev_cache_misses
, vdev_cache_total
),
887 'num': fHits(vdev_cache_misses
),
889 output
['delegations'] = {
890 'per': fPerc(vdev_cache_delegations
, vdev_cache_total
),
891 'num': fHits(vdev_cache_delegations
),
897 def _vdev_summary(Kstat
):
898 """Print information on the VDEVs"""
900 arc
= get_vdev_summary(Kstat
)
902 if arc
['vdev_cache_total'] > 0:
903 sys
.stdout
.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc
['summary'])
904 sys
.stdout
.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
905 arc
['hit_ratio']['per'],
906 arc
['hit_ratio']['num'],
908 sys
.stdout
.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
909 arc
['miss_ratio']['per'],
910 arc
['miss_ratio']['num'],
912 sys
.stdout
.write("\tDelegations:\t\t\t%s\t%s\n" % (
913 arc
['delegations']['per'],
914 arc
['delegations']['num'],
918 def _tunable_summary(Kstat
):
919 """Print information on tunables, including descriptions if requested"""
921 global show_tunable_descriptions
922 global alternate_tunable_layout
924 names
= os
.listdir("/sys/module/zfs/parameters/")
928 with
open("/sys/module/zfs/parameters/" + name
) as f
:
930 values
[name
] = value
.strip()
934 if show_tunable_descriptions
:
936 command
= ["/sbin/modinfo", "zfs", "-0"]
939 p
= Popen(command
, stdin
=PIPE
, stdout
=PIPE
,
940 stderr
=PIPE
, shell
=False, close_fds
=True)
943 # By default, Python 2 returns a string as the first element of the
944 # tuple from p.communicate(), while Python 3 returns bytes which
945 # must be decoded first. The better way to do this would be with
946 # subprocess.run() or at least .check_output(), but this fails on
947 # CentOS 6 because of its old version of Python 2
948 desc
= bytes
.decode(p
.communicate()[0])
949 description_list
= desc
.strip().split('\0')
951 if p
.returncode
== 0:
952 for tunable
in description_list
:
953 if tunable
[0:5] == 'parm:':
954 tunable
= tunable
[5:].strip()
955 name
, description
= tunable
.split(':', 1)
957 description
= "Description unavailable"
958 descriptions
[name
] = description
960 sys
.stderr
.write("%s: '%s' exited with code %i\n" %
961 (sys
.argv
[0], command
[0], p
.returncode
))
962 sys
.stderr
.write("Tunable descriptions will be disabled.\n")
964 sys
.stderr
.write("%s: Cannot run '%s': %s\n" %
965 (sys
.argv
[0], command
[0], e
.strerror
))
966 sys
.stderr
.write("Tunable descriptions will be disabled.\n")
968 sys
.stdout
.write("ZFS Tunables:\n")
971 if alternate_tunable_layout
:
981 if show_tunable_descriptions
and name
in descriptions
:
982 sys
.stdout
.write("\t# %s\n" % descriptions
[name
])
984 sys
.stdout
.write(fmt
% (name
, values
[name
]))
998 """Print title string with date"""
1000 daydate
= time
.strftime('%a %b %d %H:%M:%S %Y')
1002 sys
.stdout
.write('\n'+'-'*72+'\n')
1003 sys
.stdout
.write('ZFS Subsystem Report\t\t\t\t%s' % daydate
)
1004 sys
.stdout
.write('\n')
1008 """Print usage information"""
1010 sys
.stdout
.write("Usage: arc_summary [-h] [-a] [-d] [-p PAGE]\n\n")
1011 sys
.stdout
.write("\t -h, --help : "
1012 "Print this help message and exit\n")
1013 sys
.stdout
.write("\t -a, --alternate : "
1014 "Show an alternate sysctl layout\n")
1015 sys
.stdout
.write("\t -d, --description : "
1016 "Show the sysctl descriptions\n")
1017 sys
.stdout
.write("\t -p PAGE, --page=PAGE : "
1018 "Select a single output page to display,\n")
1019 sys
.stdout
.write("\t "
1020 "should be an integer between 1 and " +
1021 str(len(unSub
)) + "\n\n")
1022 sys
.stdout
.write("Examples:\n")
1023 sys
.stdout
.write("\tarc_summary -a\n")
1024 sys
.stdout
.write("\tarc_summary -p 4\n")
1025 sys
.stdout
.write("\tarc_summary -ad\n")
1026 sys
.stdout
.write("\tarc_summary --page=2\n")
1032 global show_tunable_descriptions
1033 global alternate_tunable_layout
1036 opts
, args
= getopt
.getopt(
1038 "adp:h", ["alternate", "description", "page=", "help"]
1040 except getopt
.error
as e
:
1041 sys
.stderr
.write("Error: %s\n" % e
.msg
)
1046 for opt
, arg
in opts
:
1047 if opt
in ('-a', '--alternate'):
1049 if opt
in ('-d', '--description'):
1051 if opt
in ('-p', '--page'):
1053 if opt
in ('-h', '--help'):
1059 alternate_tunable_layout
= 'a' in args
1060 show_tunable_descriptions
= 'd' in args
1066 pages
.append(unSub
[int(args
['p']) - 1])
1068 sys
.stderr
.write('the argument to -p must be between 1 and ' +
1069 str(len(unSub
)) + '\n')
1077 sys
.stdout
.write("\n")
1080 if __name__
== '__main__':