]> granicus.if.org Git - zfs/blob - cmd/arc_summary/arc_summary2
Fix for ARC sysctls ignored at runtime
[zfs] / cmd / arc_summary / arc_summary2
1 #!/usr/bin/env python2
2 #
3 # $Id: arc_summary.pl,v 388:e27800740aa2 2011-07-08 02:53:29Z jhell $
4 #
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>,
8 # All rights reserved.
9 #
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions
12 # are met:
13 #
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.
19 #
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
30 # SUCH DAMAGE.
31 #
32 # If you are having troubles when using this script from cron(8) please try
33 # adjusting your PATH before reporting problems.
34 #
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 #
39
40 """Print statistics on the ZFS Adjustable Replacement Cache (ARC)
41
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.
46 """
47
48 import getopt
49 import os
50 import sys
51 import time
52 import errno
53
54 from subprocess import Popen, PIPE
55 from decimal import Decimal as D
56
57
58 show_tunable_descriptions = False
59 alternate_tunable_layout = False
60
61
62 def handle_Exception(ex_cls, ex, tb):
63     if ex is IOError:
64         if ex.errno == errno.EPIPE:
65             sys.exit()
66
67     if ex is KeyboardInterrupt:
68         sys.exit()
69
70
71 sys.excepthook = handle_Exception
72
73
74 def get_Kstat():
75     """Collect information on the ZFS subsystem from the /proc virtual
76     file system. The name "kstat" is a holdover from the Solaris utility
77     of the same name.
78     """
79
80     def load_kstats(namespace):
81         """Collect information on a specific subsystem of the ARC"""
82
83         kstat = 'kstat.zfs.misc.%s.%%s' % namespace
84         path = '/proc/spl/kstat/zfs/%s' % namespace
85         with open(path) as f:
86             entries = [line.strip().split() for line in f][2:] # Skip header
87         return [(kstat % name, D(value)) for name, _, value in entries]
88
89     Kstat = {}
90     Kstat.update(load_kstats('arcstats'))
91     Kstat.update(load_kstats('zfetchstats'))
92     Kstat.update(load_kstats('vdev_cache_stats'))
93     return Kstat
94
95
96 def fBytes(b=0):
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
100     decimal points.
101     """
102
103     prefixes = [
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)
112
113     if b >= 2**10:
114
115         for limit, unit in prefixes:
116
117             if b >= limit:
118                 value = b / limit
119                 break
120
121         result = "%0.2f\t%s" % (value, unit)
122
123     else:
124
125         result = "%d\tBytes" % b
126
127     return result
128
129
130 def fHits(hits=0):
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
137     """
138
139     numbers = [
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)
148
149     if hits >= 1000:
150
151         for limit, symbol in numbers:
152
153             if hits >= limit:
154                 value = hits/limit
155                 break
156
157         result = "%0.2f%s" % (value, symbol)
158
159     else:
160
161         result = "%d" % hits
162
163     return result
164
165
166 def fPerc(lVal=0, rVal=0, Decimal=2):
167     """Calculate percentage value and return in human-readable format"""
168
169     if rVal > 0:
170         return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%"
171     else:
172         return str("%0." + str(Decimal) + "f") % 100 + "%"
173
174
175 def get_arc_summary(Kstat):
176     """Collect general data on the ARC"""
177
178     output = {}
179     memory_throttle_count = Kstat[
180         "kstat.zfs.misc.arcstats.memory_throttle_count"
181         ]
182
183     if memory_throttle_count > 0:
184         output['health'] = 'THROTTLED'
185     else:
186         output['health'] = 'HEALTHY'
187
188     output['memory_throttle_count'] = fHits(memory_throttle_count)
189
190     # ARC Misc.
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"]
194
195     # ARC Misc.
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)
200
201     # ARC Sizing
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"]
212
213     target_size_ratio = (target_max_size / target_min_size)
214
215     # ARC Sizing
216     output['arc_sizing'] = {}
217     output['arc_sizing']['arc_size'] = {
218         'per': fPerc(arc_size, target_max_size),
219         'num': fBytes(arc_size),
220     }
221     output['arc_sizing']['target_max_size'] = {
222         'ratio': target_size_ratio,
223         'num': fBytes(target_max_size),
224     }
225     output['arc_sizing']['target_min_size'] = {
226         'per': fPerc(target_min_size, target_max_size),
227         'num': fBytes(target_min_size),
228     }
229     output['arc_sizing']['target_size'] = {
230         'per': fPerc(target_size, target_max_size),
231         'num': fBytes(target_size),
232     }
233     output['arc_sizing']['meta_limit'] = {
234         'per': fPerc(meta_limit, target_max_size),
235         'num': fBytes(meta_limit),
236     }
237     output['arc_sizing']['meta_size'] = {
238         'per': fPerc(meta_size, meta_limit),
239         'num': fBytes(meta_size),
240     }
241     output['arc_sizing']['dnode_limit'] = {
242         'per': fPerc(dnode_limit, meta_limit),
243         'num': fBytes(dnode_limit),
244     }
245     output['arc_sizing']['dnode_size'] = {
246         'per': fPerc(dnode_size, dnode_limit),
247         'num': fBytes(dnode_size),
248     }
249
250     # ARC Hash Breakdown
251     output['arc_hash_break'] = {}
252     output['arc_hash_break']['hash_chain_max'] = Kstat[
253         "kstat.zfs.misc.arcstats.hash_chain_max"
254         ]
255     output['arc_hash_break']['hash_chains'] = Kstat[
256         "kstat.zfs.misc.arcstats.hash_chains"
257         ]
258     output['arc_hash_break']['hash_collisions'] = Kstat[
259         "kstat.zfs.misc.arcstats.hash_collisions"
260         ]
261     output['arc_hash_break']['hash_elements'] = Kstat[
262         "kstat.zfs.misc.arcstats.hash_elements"
263         ]
264     output['arc_hash_break']['hash_elements_max'] = Kstat[
265         "kstat.zfs.misc.arcstats.hash_elements_max"
266         ]
267
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),
272     }
273     output['arc_size_break']['frequently_used_cache_size'] = {
274         'per': fPerc(mfu_size, mru_size + mfu_size),
275         'num': fBytes(mfu_size),
276     }
277
278     # ARC Hash Breakdown
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"]
284
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),
290         }
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)
294
295     return output
296
297
298 def _arc_summary(Kstat):
299     """Print information on the ARC"""
300
301     # ARC Sizing
302     arc = get_arc_summary(Kstat)
303
304     sys.stdout.write("ARC Summary: (%s)\n" % arc['health'])
305
306     sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" %
307                      arc['memory_throttle_count'])
308     sys.stdout.write("\n")
309
310     # ARC Misc.
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")
318
319     # ARC Sizing
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']
323         )
324     )
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'],
328         )
329     )
330
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'],
334         )
335     )
336
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'],
340         )
341     )
342
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'],
347         )
348     )
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'],
352         )
353     )
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'],
357         )
358     )
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'],
362         )
363     )
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'],
367         )
368     )
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'],
372         )
373     )
374
375     sys.stdout.write("\n")
376
377     # ARC Hash Breakdown
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'],
384         )
385     )
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'])
392
393
394 def get_arc_efficiency(Kstat):
395     """Collect information on the efficiency of the ARC"""
396
397     output = {}
398
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"
405         ]
406     demand_metadata_misses = Kstat[
407         "kstat.zfs.misc.arcstats.demand_metadata_misses"
408         ]
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"
416         ]
417     prefetch_metadata_hits = Kstat[
418         "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
419         ]
420     prefetch_metadata_misses = Kstat[
421         "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
422         ]
423
424     anon_hits = arc_hits - (
425         mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits
426         )
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)
431
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),
436     }
437     output["cache_miss_ratio"] = {
438         'per': fPerc(arc_misses, arc_accesses_total),
439         'num': fHits(arc_misses),
440     }
441     output["actual_hit_ratio"] = {
442         'per': fPerc(real_hits, arc_accesses_total),
443         'num': fHits(real_hits),
444     }
445     output["data_demand_efficiency"] = {
446         'per': fPerc(demand_data_hits, demand_data_total),
447         'num': fHits(demand_data_total),
448     }
449
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),
454         }
455
456     if anon_hits > 0:
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),
461         }
462
463     output["most_recently_used"] = {
464         'per': fPerc(mru_hits, arc_hits),
465         'num': fHits(mru_hits),
466     }
467     output["most_frequently_used"] = {
468         'per': fPerc(mfu_hits, arc_hits),
469         'num': fHits(mfu_hits),
470     }
471     output["most_recently_used_ghost"] = {
472         'per': fPerc(mru_ghost_hits, arc_hits),
473         'num': fHits(mru_ghost_hits),
474     }
475     output["most_frequently_used_ghost"] = {
476         'per': fPerc(mfu_ghost_hits, arc_hits),
477         'num': fHits(mfu_ghost_hits),
478     }
479
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),
484     }
485     output["cache_hits_by_data_type"]["prefetch_data"] = {
486         'per': fPerc(prefetch_data_hits, arc_hits),
487         'num': fHits(prefetch_data_hits),
488     }
489     output["cache_hits_by_data_type"]["demand_metadata"] = {
490         'per': fPerc(demand_metadata_hits, arc_hits),
491         'num': fHits(demand_metadata_hits),
492     }
493     output["cache_hits_by_data_type"]["prefetch_metadata"] = {
494         'per': fPerc(prefetch_metadata_hits, arc_hits),
495         'num': fHits(prefetch_metadata_hits),
496     }
497
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),
502     }
503     output["cache_misses_by_data_type"]["prefetch_data"] = {
504         'per': fPerc(prefetch_data_misses, arc_misses),
505         'num': fHits(prefetch_data_misses),
506     }
507     output["cache_misses_by_data_type"]["demand_metadata"] = {
508         'per': fPerc(demand_metadata_misses, arc_misses),
509         'num': fHits(demand_metadata_misses),
510     }
511     output["cache_misses_by_data_type"]["prefetch_metadata"] = {
512         'per': fPerc(prefetch_metadata_misses, arc_misses),
513         'num': fHits(prefetch_metadata_misses),
514     }
515
516     return output
517
518
519 def _arc_efficiency(Kstat):
520     """Print information on the efficiency of the ARC"""
521
522     arc = get_arc_efficiency(Kstat)
523
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'],
529         )
530     )
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'],
534         )
535     )
536
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'],
540         )
541     )
542
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'],
547         )
548     )
549
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'],
554             )
555         )
556     sys.stdout.write("\n")
557
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'],
563             )
564         )
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'],
568         )
569     )
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'],
573         )
574     )
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'],
578         )
579     )
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'],
583         )
584     )
585
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'],
590         )
591     )
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'],
595         )
596     )
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'],
600         )
601     )
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'],
605         )
606     )
607
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'],
612         )
613     )
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'],
617         )
618     )
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'],
622         )
623     )
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'],
627         )
628     )
629
630
631 def get_l2arc_summary(Kstat):
632     """Collection information on the L2ARC"""
633
634     output = {}
635
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"]
652
653     l2_access_total = (l2_hits + l2_misses)
654     output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error)
655
656     output['l2_access_total'] = l2_access_total
657     output['l2_size'] = l2_size
658     output['l2_asize'] = l2_asize
659
660     if l2_size > 0 and l2_access_total > 0:
661
662         if output['l2_health_count'] > 0:
663             output["health"] = "DEGRADED"
664         else:
665             output["health"] = "HEALTHY"
666
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)
672
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)
678             }
679         output["l2_arc_size"]["head_size"] = {
680             'per': fPerc(l2_hdr_size, l2_size),
681             'num': fBytes(l2_hdr_size),
682         }
683
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)
687
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),
693         }
694         output['l2_arc_breakdown']['miss_ratio'] = {
695             'per': fPerc(l2_misses, l2_access_total),
696             'num': fHits(l2_misses),
697         }
698         output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds)
699
700         output['l2_arc_buffer'] = {}
701
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'] = {
707                 'value': "FAULTED",
708                 'num': fHits(l2_writes_sent),
709             }
710             output['l2_arc_writes']['done_ratio'] = {
711                 'per': fPerc(l2_writes_done, l2_writes_sent),
712                 'num': fHits(l2_writes_done),
713             }
714             output['l2_arc_writes']['error_ratio'] = {
715                 'per': fPerc(l2_writes_error, l2_writes_sent),
716                 'num': fHits(l2_writes_error),
717             }
718         else:
719             output['l2_arc_writes']['writes_sent'] = {
720                 'per': fPerc(100),
721                 'num': fHits(l2_writes_sent),
722             }
723
724     return output
725
726
727 def _l2arc_summary(Kstat):
728     """Print information on the L2ARC"""
729
730     arc = get_l2arc_summary(Kstat)
731
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")
736         else:
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")
745
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"],
751             )
752         )
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"],
756             )
757         )
758         sys.stdout.write("\n")
759
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")
768
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'],
774             )
775         )
776
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'],
780             )
781         )
782
783         sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" %
784                          arc['l2_arc_breakdown']['feeds'])
785         sys.stdout.write("\n")
786
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'],
792                 )
793             )
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'],
797                 )
798             )
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'],
802                 )
803             )
804         else:
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'],
808                 )
809             )
810
811
812 def get_dmu_summary(Kstat):
813     """Collect information on the DMU"""
814
815     output = {}
816
817     zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"]
818     zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"]
819
820     zfetch_access_total = (zfetch_hits + zfetch_misses)
821     output['zfetch_access_total'] = zfetch_access_total
822
823     if zfetch_access_total > 0:
824         output['dmu'] = {}
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),
830         }
831         output['dmu']['efficiency']['miss_ratio'] = {
832             'per': fPerc(zfetch_misses, zfetch_access_total),
833             'num': fHits(zfetch_misses),
834         }
835
836     return output
837
838
839 def _dmu_summary(Kstat):
840     """Print information on the DMU"""
841
842     arc = get_dmu_summary(Kstat)
843
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'],
850             )
851         )
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'],
855             )
856         )
857
858         sys.stdout.write("\n")
859
860
861 def get_vdev_summary(Kstat):
862     """Collect information on the VDEVs"""
863
864     output = {}
865
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)
872
873     output['vdev_cache_total'] = vdev_cache_total
874
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),
880         }
881         output['miss_ratio'] = {
882             'per': fPerc(vdev_cache_misses, vdev_cache_total),
883             'num': fHits(vdev_cache_misses),
884         }
885         output['delegations'] = {
886             'per': fPerc(vdev_cache_delegations, vdev_cache_total),
887             'num': fHits(vdev_cache_delegations),
888         }
889
890     return output
891
892
893 def _vdev_summary(Kstat):
894     """Print information on the VDEVs"""
895
896     arc = get_vdev_summary(Kstat)
897
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'],
903         ))
904         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
905             arc['miss_ratio']['per'],
906             arc['miss_ratio']['num'],
907         ))
908         sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % (
909             arc['delegations']['per'],
910             arc['delegations']['num'],
911         ))
912
913
914 def _tunable_summary(Kstat):
915     """Print information on tunables, including descriptions if requested"""
916
917     global show_tunable_descriptions
918     global alternate_tunable_layout
919
920     def load_tunables():
921         basepath = '/sys/module/zfs/parameters'
922         tunables = {}
923         for name in os.listdir(basepath):
924             if not name:
925                 continue
926             path = '%s/%s' % (basepath, name)
927             with open(path) as f:
928                 value = f.read()
929             tunables[name] = value.strip()
930         return tunables
931
932     tunables = load_tunables()
933     descriptions = {}
934
935     if show_tunable_descriptions:
936
937         command = ["/sbin/modinfo", "zfs", "-0"]
938
939         try:
940             p = Popen(command, stdin=PIPE, stdout=PIPE,
941                       stderr=PIPE, shell=False, close_fds=True)
942             p.wait()
943
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')
951
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)
957                         if not description:
958                             description = "Description unavailable"
959                         descriptions[name] = description
960             else:
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")
964         except OSError as e:
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")
968
969     sys.stdout.write("ZFS Tunables:\n")
970
971     if alternate_tunable_layout:
972         fmt = "\t%s=%s\n"
973     else:
974         fmt = "\t%-50s%s\n"
975
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])
979
980         sys.stdout.write(fmt % (name, tunables[name]))
981
982
983 unSub = [
984     _arc_summary,
985     _arc_efficiency,
986     _l2arc_summary,
987     _dmu_summary,
988     _vdev_summary,
989     _tunable_summary
990 ]
991
992
993 def zfs_header():
994     """Print title string with date"""
995
996     daydate = time.strftime('%a %b %d %H:%M:%S %Y')
997
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')
1001
1002
1003 def usage():
1004     """Print usage information"""
1005
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")
1023
1024
1025 def main():
1026     """Main function"""
1027
1028     global show_tunable_descriptions
1029     global alternate_tunable_layout
1030
1031     try:
1032         opts, args = getopt.getopt(
1033             sys.argv[1:],
1034             "adp:h", ["alternate", "description", "page=", "help"]
1035         )
1036     except getopt.error as e:
1037         sys.stderr.write("Error: %s\n" % e.msg)
1038         usage()
1039         sys.exit(1)
1040
1041     args = {}
1042     for opt, arg in opts:
1043         if opt in ('-a', '--alternate'):
1044             args['a'] = True
1045         if opt in ('-d', '--description'):
1046             args['d'] = True
1047         if opt in ('-p', '--page'):
1048             args['p'] = arg
1049         if opt in ('-h', '--help'):
1050             usage()
1051             sys.exit(0)
1052
1053     Kstat = get_Kstat()
1054
1055     alternate_tunable_layout = 'a' in args
1056     show_tunable_descriptions = 'd' in args
1057
1058     pages = []
1059
1060     if 'p' in args:
1061         try:
1062             pages.append(unSub[int(args['p']) - 1])
1063         except IndexError:
1064             sys.stderr.write('the argument to -p must be between 1 and ' +
1065                              str(len(unSub)) + '\n')
1066             sys.exit(1)
1067     else:
1068         pages = unSub
1069
1070     zfs_header()
1071     for page in pages:
1072         page(Kstat)
1073         sys.stdout.write("\n")
1074
1075
1076 if __name__ == '__main__':
1077     main()