]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - cmd/arc_summary/arc_summary2
Vendor import of openzfs master @ 184df27eef0abdc7ab2105b21257f753834b936b
[FreeBSD/FreeBSD.git] / 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 if sys.platform.startswith('freebsd'):
59     # Requires py27-sysctl on FreeBSD
60     import sysctl
61
62     def load_kstats(namespace):
63         """Collect information on a specific subsystem of the ARC"""
64
65         base = 'kstat.zfs.misc.%s.' % namespace
66         return [(kstat.name, D(kstat.value)) for kstat in sysctl.filter(base)]
67
68     def load_tunables():
69         return dict((ctl.name, ctl.value) for ctl in sysctl.filter('vfs.zfs'))
70
71 elif sys.platform.startswith('linux'):
72
73     def load_kstats(namespace):
74         """Collect information on a specific subsystem of the ARC"""
75
76         kstat = 'kstat.zfs.misc.%s.%%s' % namespace
77         path = '/proc/spl/kstat/zfs/%s' % namespace
78         with open(path) as f:
79             entries = [line.strip().split() for line in f][2:] # Skip header
80         return [(kstat % name, D(value)) for name, _, value in entries]
81
82     def load_tunables():
83         basepath = '/sys/module/zfs/parameters'
84         tunables = {}
85         for name in os.listdir(basepath):
86             if not name:
87                 continue
88             path = '%s/%s' % (basepath, name)
89             with open(path) as f:
90                 value = f.read()
91             tunables[name] = value.strip()
92         return tunables
93
94
95 show_tunable_descriptions = False
96 alternate_tunable_layout = False
97
98
99 def handle_Exception(ex_cls, ex, tb):
100     if ex is IOError:
101         if ex.errno == errno.EPIPE:
102             sys.exit()
103
104     if ex is KeyboardInterrupt:
105         sys.exit()
106
107
108 sys.excepthook = handle_Exception
109
110
111 def get_Kstat():
112     """Collect information on the ZFS subsystem from the /proc virtual
113     file system. The name "kstat" is a holdover from the Solaris utility
114     of the same name.
115     """
116
117     Kstat = {}
118     Kstat.update(load_kstats('arcstats'))
119     Kstat.update(load_kstats('zfetchstats'))
120     Kstat.update(load_kstats('vdev_cache_stats'))
121     return Kstat
122
123
124 def fBytes(b=0):
125     """Return human-readable representation of a byte value in
126     powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
127     points. Values smaller than one KiB are returned without
128     decimal points.
129     """
130
131     prefixes = [
132         [2**80, "YiB"],   # yobibytes (yotta)
133         [2**70, "ZiB"],   # zebibytes (zetta)
134         [2**60, "EiB"],   # exbibytes (exa)
135         [2**50, "PiB"],   # pebibytes (peta)
136         [2**40, "TiB"],   # tebibytes (tera)
137         [2**30, "GiB"],   # gibibytes (giga)
138         [2**20, "MiB"],   # mebibytes (mega)
139         [2**10, "KiB"]]   # kibibytes (kilo)
140
141     if b >= 2**10:
142
143         for limit, unit in prefixes:
144
145             if b >= limit:
146                 value = b / limit
147                 break
148
149         result = "%0.2f\t%s" % (value, unit)
150
151     else:
152
153         result = "%d\tBytes" % b
154
155     return result
156
157
158 def fHits(hits=0):
159     """Create a human-readable representation of the number of hits.
160     The single-letter symbols used are SI to avoid the confusion caused
161     by the different "short scale" and "long scale" representations in
162     English, which use the same words for different values. See
163     https://en.wikipedia.org/wiki/Names_of_large_numbers and
164     https://physics.nist.gov/cuu/Units/prefixes.html
165     """
166
167     numbers = [
168             [10**24, 'Y'],  # yotta (septillion)
169             [10**21, 'Z'],  # zetta (sextillion)
170             [10**18, 'E'],  # exa   (quintrillion)
171             [10**15, 'P'],  # peta  (quadrillion)
172             [10**12, 'T'],  # tera  (trillion)
173             [10**9, 'G'],   # giga  (billion)
174             [10**6, 'M'],   # mega  (million)
175             [10**3, 'k']]   # kilo  (thousand)
176
177     if hits >= 1000:
178
179         for limit, symbol in numbers:
180
181             if hits >= limit:
182                 value = hits/limit
183                 break
184
185         result = "%0.2f%s" % (value, symbol)
186
187     else:
188
189         result = "%d" % hits
190
191     return result
192
193
194 def fPerc(lVal=0, rVal=0, Decimal=2):
195     """Calculate percentage value and return in human-readable format"""
196
197     if rVal > 0:
198         return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%"
199     else:
200         return str("%0." + str(Decimal) + "f") % 100 + "%"
201
202
203 def get_arc_summary(Kstat):
204     """Collect general data on the ARC"""
205
206     output = {}
207     memory_throttle_count = Kstat[
208         "kstat.zfs.misc.arcstats.memory_throttle_count"
209         ]
210
211     if memory_throttle_count > 0:
212         output['health'] = 'THROTTLED'
213     else:
214         output['health'] = 'HEALTHY'
215
216     output['memory_throttle_count'] = fHits(memory_throttle_count)
217
218     # ARC Misc.
219     deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
220     mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
221     evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"]
222
223     # ARC Misc.
224     output["arc_misc"] = {}
225     output["arc_misc"]["deleted"] = fHits(deleted)
226     output["arc_misc"]['mutex_miss'] = fHits(mutex_miss)
227     output["arc_misc"]['evict_skips'] = fHits(evict_skip)
228
229     # ARC Sizing
230     arc_size = Kstat["kstat.zfs.misc.arcstats.size"]
231     mru_size = Kstat["kstat.zfs.misc.arcstats.mru_size"]
232     mfu_size = Kstat["kstat.zfs.misc.arcstats.mfu_size"]
233     meta_limit = Kstat["kstat.zfs.misc.arcstats.arc_meta_limit"]
234     meta_size = Kstat["kstat.zfs.misc.arcstats.arc_meta_used"]
235     dnode_limit = Kstat["kstat.zfs.misc.arcstats.arc_dnode_limit"]
236     dnode_size = Kstat["kstat.zfs.misc.arcstats.dnode_size"]
237     target_max_size = Kstat["kstat.zfs.misc.arcstats.c_max"]
238     target_min_size = Kstat["kstat.zfs.misc.arcstats.c_min"]
239     target_size = Kstat["kstat.zfs.misc.arcstats.c"]
240
241     target_size_ratio = (target_max_size / target_min_size)
242
243     # ARC Sizing
244     output['arc_sizing'] = {}
245     output['arc_sizing']['arc_size'] = {
246         'per': fPerc(arc_size, target_max_size),
247         'num': fBytes(arc_size),
248     }
249     output['arc_sizing']['target_max_size'] = {
250         'ratio': target_size_ratio,
251         'num': fBytes(target_max_size),
252     }
253     output['arc_sizing']['target_min_size'] = {
254         'per': fPerc(target_min_size, target_max_size),
255         'num': fBytes(target_min_size),
256     }
257     output['arc_sizing']['target_size'] = {
258         'per': fPerc(target_size, target_max_size),
259         'num': fBytes(target_size),
260     }
261     output['arc_sizing']['meta_limit'] = {
262         'per': fPerc(meta_limit, target_max_size),
263         'num': fBytes(meta_limit),
264     }
265     output['arc_sizing']['meta_size'] = {
266         'per': fPerc(meta_size, meta_limit),
267         'num': fBytes(meta_size),
268     }
269     output['arc_sizing']['dnode_limit'] = {
270         'per': fPerc(dnode_limit, meta_limit),
271         'num': fBytes(dnode_limit),
272     }
273     output['arc_sizing']['dnode_size'] = {
274         'per': fPerc(dnode_size, dnode_limit),
275         'num': fBytes(dnode_size),
276     }
277
278     # ARC Hash Breakdown
279     output['arc_hash_break'] = {}
280     output['arc_hash_break']['hash_chain_max'] = Kstat[
281         "kstat.zfs.misc.arcstats.hash_chain_max"
282         ]
283     output['arc_hash_break']['hash_chains'] = Kstat[
284         "kstat.zfs.misc.arcstats.hash_chains"
285         ]
286     output['arc_hash_break']['hash_collisions'] = Kstat[
287         "kstat.zfs.misc.arcstats.hash_collisions"
288         ]
289     output['arc_hash_break']['hash_elements'] = Kstat[
290         "kstat.zfs.misc.arcstats.hash_elements"
291         ]
292     output['arc_hash_break']['hash_elements_max'] = Kstat[
293         "kstat.zfs.misc.arcstats.hash_elements_max"
294         ]
295
296     output['arc_size_break'] = {}
297     output['arc_size_break']['recently_used_cache_size'] = {
298         'per': fPerc(mru_size, mru_size + mfu_size),
299         'num': fBytes(mru_size),
300     }
301     output['arc_size_break']['frequently_used_cache_size'] = {
302         'per': fPerc(mfu_size, mru_size + mfu_size),
303         'num': fBytes(mfu_size),
304     }
305
306     # ARC Hash Breakdown
307     hash_chain_max = Kstat["kstat.zfs.misc.arcstats.hash_chain_max"]
308     hash_chains = Kstat["kstat.zfs.misc.arcstats.hash_chains"]
309     hash_collisions = Kstat["kstat.zfs.misc.arcstats.hash_collisions"]
310     hash_elements = Kstat["kstat.zfs.misc.arcstats.hash_elements"]
311     hash_elements_max = Kstat["kstat.zfs.misc.arcstats.hash_elements_max"]
312
313     output['arc_hash_break'] = {}
314     output['arc_hash_break']['elements_max'] = fHits(hash_elements_max)
315     output['arc_hash_break']['elements_current'] = {
316         'per': fPerc(hash_elements, hash_elements_max),
317         'num': fHits(hash_elements),
318         }
319     output['arc_hash_break']['collisions'] = fHits(hash_collisions)
320     output['arc_hash_break']['chain_max'] = fHits(hash_chain_max)
321     output['arc_hash_break']['chains'] = fHits(hash_chains)
322
323     return output
324
325
326 def _arc_summary(Kstat):
327     """Print information on the ARC"""
328
329     # ARC Sizing
330     arc = get_arc_summary(Kstat)
331
332     sys.stdout.write("ARC Summary: (%s)\n" % arc['health'])
333
334     sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" %
335                      arc['memory_throttle_count'])
336     sys.stdout.write("\n")
337
338     # ARC Misc.
339     sys.stdout.write("ARC Misc:\n")
340     sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
341     sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
342                      arc['arc_misc']['mutex_miss'])
343     sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" %
344                      arc['arc_misc']['evict_skips'])
345     sys.stdout.write("\n")
346
347     # ARC Sizing
348     sys.stdout.write("ARC Size:\t\t\t\t%s\t%s\n" % (
349         arc['arc_sizing']['arc_size']['per'],
350         arc['arc_sizing']['arc_size']['num']
351         )
352     )
353     sys.stdout.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % (
354         arc['arc_sizing']['target_size']['per'],
355         arc['arc_sizing']['target_size']['num'],
356         )
357     )
358
359     sys.stdout.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % (
360         arc['arc_sizing']['target_min_size']['per'],
361         arc['arc_sizing']['target_min_size']['num'],
362         )
363     )
364
365     sys.stdout.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % (
366         arc['arc_sizing']['target_max_size']['ratio'],
367         arc['arc_sizing']['target_max_size']['num'],
368         )
369     )
370
371     sys.stdout.write("\nARC Size Breakdown:\n")
372     sys.stdout.write("\tRecently Used Cache Size:\t%s\t%s\n" % (
373         arc['arc_size_break']['recently_used_cache_size']['per'],
374         arc['arc_size_break']['recently_used_cache_size']['num'],
375         )
376     )
377     sys.stdout.write("\tFrequently Used Cache Size:\t%s\t%s\n" % (
378         arc['arc_size_break']['frequently_used_cache_size']['per'],
379         arc['arc_size_break']['frequently_used_cache_size']['num'],
380         )
381     )
382     sys.stdout.write("\tMetadata Size (Hard Limit):\t%s\t%s\n" % (
383         arc['arc_sizing']['meta_limit']['per'],
384         arc['arc_sizing']['meta_limit']['num'],
385         )
386     )
387     sys.stdout.write("\tMetadata Size:\t\t\t%s\t%s\n" % (
388         arc['arc_sizing']['meta_size']['per'],
389         arc['arc_sizing']['meta_size']['num'],
390         )
391     )
392     sys.stdout.write("\tDnode Size (Hard Limit):\t%s\t%s\n" % (
393         arc['arc_sizing']['dnode_limit']['per'],
394         arc['arc_sizing']['dnode_limit']['num'],
395         )
396     )
397     sys.stdout.write("\tDnode Size:\t\t\t%s\t%s\n" % (
398         arc['arc_sizing']['dnode_size']['per'],
399         arc['arc_sizing']['dnode_size']['num'],
400         )
401     )
402
403     sys.stdout.write("\n")
404
405     # ARC Hash Breakdown
406     sys.stdout.write("ARC Hash Breakdown:\n")
407     sys.stdout.write("\tElements Max:\t\t\t\t%s\n" %
408                      arc['arc_hash_break']['elements_max'])
409     sys.stdout.write("\tElements Current:\t\t%s\t%s\n" % (
410         arc['arc_hash_break']['elements_current']['per'],
411         arc['arc_hash_break']['elements_current']['num'],
412         )
413     )
414     sys.stdout.write("\tCollisions:\t\t\t\t%s\n" %
415                      arc['arc_hash_break']['collisions'])
416     sys.stdout.write("\tChain Max:\t\t\t\t%s\n" %
417                      arc['arc_hash_break']['chain_max'])
418     sys.stdout.write("\tChains:\t\t\t\t\t%s\n" %
419                      arc['arc_hash_break']['chains'])
420
421
422 def get_arc_efficiency(Kstat):
423     """Collect information on the efficiency of the ARC"""
424
425     output = {}
426
427     arc_hits = Kstat["kstat.zfs.misc.arcstats.hits"]
428     arc_misses = Kstat["kstat.zfs.misc.arcstats.misses"]
429     demand_data_hits = Kstat["kstat.zfs.misc.arcstats.demand_data_hits"]
430     demand_data_misses = Kstat["kstat.zfs.misc.arcstats.demand_data_misses"]
431     demand_metadata_hits = Kstat[
432         "kstat.zfs.misc.arcstats.demand_metadata_hits"
433         ]
434     demand_metadata_misses = Kstat[
435         "kstat.zfs.misc.arcstats.demand_metadata_misses"
436         ]
437     mfu_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mfu_ghost_hits"]
438     mfu_hits = Kstat["kstat.zfs.misc.arcstats.mfu_hits"]
439     mru_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mru_ghost_hits"]
440     mru_hits = Kstat["kstat.zfs.misc.arcstats.mru_hits"]
441     prefetch_data_hits = Kstat["kstat.zfs.misc.arcstats.prefetch_data_hits"]
442     prefetch_data_misses = Kstat[
443         "kstat.zfs.misc.arcstats.prefetch_data_misses"
444         ]
445     prefetch_metadata_hits = Kstat[
446         "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
447         ]
448     prefetch_metadata_misses = Kstat[
449         "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
450         ]
451
452     anon_hits = arc_hits - (
453         mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits
454         )
455     arc_accesses_total = (arc_hits + arc_misses)
456     demand_data_total = (demand_data_hits + demand_data_misses)
457     prefetch_data_total = (prefetch_data_hits + prefetch_data_misses)
458     real_hits = (mfu_hits + mru_hits)
459
460     output["total_accesses"] = fHits(arc_accesses_total)
461     output["cache_hit_ratio"] = {
462         'per': fPerc(arc_hits, arc_accesses_total),
463         'num': fHits(arc_hits),
464     }
465     output["cache_miss_ratio"] = {
466         'per': fPerc(arc_misses, arc_accesses_total),
467         'num': fHits(arc_misses),
468     }
469     output["actual_hit_ratio"] = {
470         'per': fPerc(real_hits, arc_accesses_total),
471         'num': fHits(real_hits),
472     }
473     output["data_demand_efficiency"] = {
474         'per': fPerc(demand_data_hits, demand_data_total),
475         'num': fHits(demand_data_total),
476     }
477
478     if prefetch_data_total > 0:
479         output["data_prefetch_efficiency"] = {
480             'per': fPerc(prefetch_data_hits, prefetch_data_total),
481             'num': fHits(prefetch_data_total),
482         }
483
484     if anon_hits > 0:
485         output["cache_hits_by_cache_list"] = {}
486         output["cache_hits_by_cache_list"]["anonymously_used"] = {
487             'per': fPerc(anon_hits, arc_hits),
488             'num': fHits(anon_hits),
489         }
490
491     output["most_recently_used"] = {
492         'per': fPerc(mru_hits, arc_hits),
493         'num': fHits(mru_hits),
494     }
495     output["most_frequently_used"] = {
496         'per': fPerc(mfu_hits, arc_hits),
497         'num': fHits(mfu_hits),
498     }
499     output["most_recently_used_ghost"] = {
500         'per': fPerc(mru_ghost_hits, arc_hits),
501         'num': fHits(mru_ghost_hits),
502     }
503     output["most_frequently_used_ghost"] = {
504         'per': fPerc(mfu_ghost_hits, arc_hits),
505         'num': fHits(mfu_ghost_hits),
506     }
507
508     output["cache_hits_by_data_type"] = {}
509     output["cache_hits_by_data_type"]["demand_data"] = {
510         'per': fPerc(demand_data_hits, arc_hits),
511         'num': fHits(demand_data_hits),
512     }
513     output["cache_hits_by_data_type"]["prefetch_data"] = {
514         'per': fPerc(prefetch_data_hits, arc_hits),
515         'num': fHits(prefetch_data_hits),
516     }
517     output["cache_hits_by_data_type"]["demand_metadata"] = {
518         'per': fPerc(demand_metadata_hits, arc_hits),
519         'num': fHits(demand_metadata_hits),
520     }
521     output["cache_hits_by_data_type"]["prefetch_metadata"] = {
522         'per': fPerc(prefetch_metadata_hits, arc_hits),
523         'num': fHits(prefetch_metadata_hits),
524     }
525
526     output["cache_misses_by_data_type"] = {}
527     output["cache_misses_by_data_type"]["demand_data"] = {
528         'per': fPerc(demand_data_misses, arc_misses),
529         'num': fHits(demand_data_misses),
530     }
531     output["cache_misses_by_data_type"]["prefetch_data"] = {
532         'per': fPerc(prefetch_data_misses, arc_misses),
533         'num': fHits(prefetch_data_misses),
534     }
535     output["cache_misses_by_data_type"]["demand_metadata"] = {
536         'per': fPerc(demand_metadata_misses, arc_misses),
537         'num': fHits(demand_metadata_misses),
538     }
539     output["cache_misses_by_data_type"]["prefetch_metadata"] = {
540         'per': fPerc(prefetch_metadata_misses, arc_misses),
541         'num': fHits(prefetch_metadata_misses),
542     }
543
544     return output
545
546
547 def _arc_efficiency(Kstat):
548     """Print information on the efficiency of the ARC"""
549
550     arc = get_arc_efficiency(Kstat)
551
552     sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" %
553                      arc['total_accesses'])
554     sys.stdout.write("\tCache Hit Ratio:\t\t%s\t%s\n" % (
555         arc['cache_hit_ratio']['per'],
556         arc['cache_hit_ratio']['num'],
557         )
558     )
559     sys.stdout.write("\tCache Miss Ratio:\t\t%s\t%s\n" % (
560         arc['cache_miss_ratio']['per'],
561         arc['cache_miss_ratio']['num'],
562         )
563     )
564
565     sys.stdout.write("\tActual Hit Ratio:\t\t%s\t%s\n" % (
566         arc['actual_hit_ratio']['per'],
567         arc['actual_hit_ratio']['num'],
568         )
569     )
570
571     sys.stdout.write("\n")
572     sys.stdout.write("\tData Demand Efficiency:\t\t%s\t%s\n" % (
573         arc['data_demand_efficiency']['per'],
574         arc['data_demand_efficiency']['num'],
575         )
576     )
577
578     if 'data_prefetch_efficiency' in arc:
579         sys.stdout.write("\tData Prefetch Efficiency:\t%s\t%s\n" % (
580             arc['data_prefetch_efficiency']['per'],
581             arc['data_prefetch_efficiency']['num'],
582             )
583         )
584     sys.stdout.write("\n")
585
586     sys.stdout.write("\tCACHE HITS BY CACHE LIST:\n")
587     if 'cache_hits_by_cache_list' in arc:
588         sys.stdout.write("\t  Anonymously Used:\t\t%s\t%s\n" % (
589             arc['cache_hits_by_cache_list']['anonymously_used']['per'],
590             arc['cache_hits_by_cache_list']['anonymously_used']['num'],
591             )
592         )
593     sys.stdout.write("\t  Most Recently Used:\t\t%s\t%s\n" % (
594         arc['most_recently_used']['per'],
595         arc['most_recently_used']['num'],
596         )
597     )
598     sys.stdout.write("\t  Most Frequently Used:\t\t%s\t%s\n" % (
599         arc['most_frequently_used']['per'],
600         arc['most_frequently_used']['num'],
601         )
602     )
603     sys.stdout.write("\t  Most Recently Used Ghost:\t%s\t%s\n" % (
604         arc['most_recently_used_ghost']['per'],
605         arc['most_recently_used_ghost']['num'],
606         )
607     )
608     sys.stdout.write("\t  Most Frequently Used Ghost:\t%s\t%s\n" % (
609         arc['most_frequently_used_ghost']['per'],
610         arc['most_frequently_used_ghost']['num'],
611         )
612     )
613
614     sys.stdout.write("\n\tCACHE HITS BY DATA TYPE:\n")
615     sys.stdout.write("\t  Demand Data:\t\t\t%s\t%s\n" % (
616         arc["cache_hits_by_data_type"]['demand_data']['per'],
617         arc["cache_hits_by_data_type"]['demand_data']['num'],
618         )
619     )
620     sys.stdout.write("\t  Prefetch Data:\t\t%s\t%s\n" % (
621         arc["cache_hits_by_data_type"]['prefetch_data']['per'],
622         arc["cache_hits_by_data_type"]['prefetch_data']['num'],
623         )
624     )
625     sys.stdout.write("\t  Demand Metadata:\t\t%s\t%s\n" % (
626         arc["cache_hits_by_data_type"]['demand_metadata']['per'],
627         arc["cache_hits_by_data_type"]['demand_metadata']['num'],
628         )
629     )
630     sys.stdout.write("\t  Prefetch Metadata:\t\t%s\t%s\n" % (
631         arc["cache_hits_by_data_type"]['prefetch_metadata']['per'],
632         arc["cache_hits_by_data_type"]['prefetch_metadata']['num'],
633         )
634     )
635
636     sys.stdout.write("\n\tCACHE MISSES BY DATA TYPE:\n")
637     sys.stdout.write("\t  Demand Data:\t\t\t%s\t%s\n" % (
638         arc["cache_misses_by_data_type"]['demand_data']['per'],
639         arc["cache_misses_by_data_type"]['demand_data']['num'],
640         )
641     )
642     sys.stdout.write("\t  Prefetch Data:\t\t%s\t%s\n" % (
643         arc["cache_misses_by_data_type"]['prefetch_data']['per'],
644         arc["cache_misses_by_data_type"]['prefetch_data']['num'],
645         )
646     )
647     sys.stdout.write("\t  Demand Metadata:\t\t%s\t%s\n" % (
648         arc["cache_misses_by_data_type"]['demand_metadata']['per'],
649         arc["cache_misses_by_data_type"]['demand_metadata']['num'],
650         )
651     )
652     sys.stdout.write("\t  Prefetch Metadata:\t\t%s\t%s\n" % (
653         arc["cache_misses_by_data_type"]['prefetch_metadata']['per'],
654         arc["cache_misses_by_data_type"]['prefetch_metadata']['num'],
655         )
656     )
657
658
659 def get_l2arc_summary(Kstat):
660     """Collection information on the L2ARC"""
661
662     output = {}
663
664     l2_abort_lowmem = Kstat["kstat.zfs.misc.arcstats.l2_abort_lowmem"]
665     l2_cksum_bad = Kstat["kstat.zfs.misc.arcstats.l2_cksum_bad"]
666     l2_evict_lock_retry = Kstat["kstat.zfs.misc.arcstats.l2_evict_lock_retry"]
667     l2_evict_reading = Kstat["kstat.zfs.misc.arcstats.l2_evict_reading"]
668     l2_feeds = Kstat["kstat.zfs.misc.arcstats.l2_feeds"]
669     l2_free_on_write = Kstat["kstat.zfs.misc.arcstats.l2_free_on_write"]
670     l2_hdr_size = Kstat["kstat.zfs.misc.arcstats.l2_hdr_size"]
671     l2_hits = Kstat["kstat.zfs.misc.arcstats.l2_hits"]
672     l2_io_error = Kstat["kstat.zfs.misc.arcstats.l2_io_error"]
673     l2_misses = Kstat["kstat.zfs.misc.arcstats.l2_misses"]
674     l2_rw_clash = Kstat["kstat.zfs.misc.arcstats.l2_rw_clash"]
675     l2_size = Kstat["kstat.zfs.misc.arcstats.l2_size"]
676     l2_asize = Kstat["kstat.zfs.misc.arcstats.l2_asize"]
677     l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"]
678     l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"]
679     l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"]
680
681     l2_access_total = (l2_hits + l2_misses)
682     output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error)
683
684     output['l2_access_total'] = l2_access_total
685     output['l2_size'] = l2_size
686     output['l2_asize'] = l2_asize
687
688     if l2_size > 0 and l2_access_total > 0:
689
690         if output['l2_health_count'] > 0:
691             output["health"] = "DEGRADED"
692         else:
693             output["health"] = "HEALTHY"
694
695         output["low_memory_aborts"] = fHits(l2_abort_lowmem)
696         output["free_on_write"] = fHits(l2_free_on_write)
697         output["rw_clashes"] = fHits(l2_rw_clash)
698         output["bad_checksums"] = fHits(l2_cksum_bad)
699         output["io_errors"] = fHits(l2_io_error)
700
701         output["l2_arc_size"] = {}
702         output["l2_arc_size"]["adative"] = fBytes(l2_size)
703         output["l2_arc_size"]["actual"] = {
704             'per': fPerc(l2_asize, l2_size),
705             'num': fBytes(l2_asize)
706             }
707         output["l2_arc_size"]["head_size"] = {
708             'per': fPerc(l2_hdr_size, l2_size),
709             'num': fBytes(l2_hdr_size),
710         }
711
712         output["l2_arc_evicts"] = {}
713         output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry)
714         output["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading)
715
716         output['l2_arc_breakdown'] = {}
717         output['l2_arc_breakdown']['value'] = fHits(l2_access_total)
718         output['l2_arc_breakdown']['hit_ratio'] = {
719             'per': fPerc(l2_hits, l2_access_total),
720             'num': fHits(l2_hits),
721         }
722         output['l2_arc_breakdown']['miss_ratio'] = {
723             'per': fPerc(l2_misses, l2_access_total),
724             'num': fHits(l2_misses),
725         }
726         output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds)
727
728         output['l2_arc_buffer'] = {}
729
730         output['l2_arc_writes'] = {}
731         output['l2_writes_done'] = l2_writes_done
732         output['l2_writes_sent'] = l2_writes_sent
733         if l2_writes_done != l2_writes_sent:
734             output['l2_arc_writes']['writes_sent'] = {
735                 'value': "FAULTED",
736                 'num': fHits(l2_writes_sent),
737             }
738             output['l2_arc_writes']['done_ratio'] = {
739                 'per': fPerc(l2_writes_done, l2_writes_sent),
740                 'num': fHits(l2_writes_done),
741             }
742             output['l2_arc_writes']['error_ratio'] = {
743                 'per': fPerc(l2_writes_error, l2_writes_sent),
744                 'num': fHits(l2_writes_error),
745             }
746         else:
747             output['l2_arc_writes']['writes_sent'] = {
748                 'per': fPerc(100),
749                 'num': fHits(l2_writes_sent),
750             }
751
752     return output
753
754
755 def _l2arc_summary(Kstat):
756     """Print information on the L2ARC"""
757
758     arc = get_l2arc_summary(Kstat)
759
760     if arc['l2_size'] > 0 and arc['l2_access_total'] > 0:
761         sys.stdout.write("L2 ARC Summary: ")
762         if arc['l2_health_count'] > 0:
763             sys.stdout.write("(DEGRADED)\n")
764         else:
765             sys.stdout.write("(HEALTHY)\n")
766         sys.stdout.write("\tLow Memory Aborts:\t\t\t%s\n" %
767                          arc['low_memory_aborts'])
768         sys.stdout.write("\tFree on Write:\t\t\t\t%s\n" % arc['free_on_write'])
769         sys.stdout.write("\tR/W Clashes:\t\t\t\t%s\n" % arc['rw_clashes'])
770         sys.stdout.write("\tBad Checksums:\t\t\t\t%s\n" % arc['bad_checksums'])
771         sys.stdout.write("\tIO Errors:\t\t\t\t%s\n" % arc['io_errors'])
772         sys.stdout.write("\n")
773
774         sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
775                          arc["l2_arc_size"]["adative"])
776         sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % (
777             arc["l2_arc_size"]["actual"]["per"],
778             arc["l2_arc_size"]["actual"]["num"],
779             )
780         )
781         sys.stdout.write("\tHeader Size:\t\t\t%s\t%s\n" % (
782             arc["l2_arc_size"]["head_size"]["per"],
783             arc["l2_arc_size"]["head_size"]["num"],
784             )
785         )
786         sys.stdout.write("\n")
787
788         if arc["l2_arc_evicts"]['lock_retries'] != '0' or \
789            arc["l2_arc_evicts"]["reading"] != '0':
790             sys.stdout.write("L2 ARC Evicts:\n")
791             sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" %
792                              arc["l2_arc_evicts"]['lock_retries'])
793             sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" %
794                              arc["l2_arc_evicts"]["reading"])
795             sys.stdout.write("\n")
796
797         sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
798                          arc['l2_arc_breakdown']['value'])
799         sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
800             arc['l2_arc_breakdown']['hit_ratio']['per'],
801             arc['l2_arc_breakdown']['hit_ratio']['num'],
802             )
803         )
804
805         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
806             arc['l2_arc_breakdown']['miss_ratio']['per'],
807             arc['l2_arc_breakdown']['miss_ratio']['num'],
808             )
809         )
810
811         sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" %
812                          arc['l2_arc_breakdown']['feeds'])
813         sys.stdout.write("\n")
814
815         sys.stdout.write("L2 ARC Writes:\n")
816         if arc['l2_writes_done'] != arc['l2_writes_sent']:
817             sys.stdout.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % (
818                 arc['l2_arc_writes']['writes_sent']['value'],
819                 arc['l2_arc_writes']['writes_sent']['num'],
820                 )
821             )
822             sys.stdout.write("\t  Done Ratio:\t\t\t%s\t%s\n" % (
823                 arc['l2_arc_writes']['done_ratio']['per'],
824                 arc['l2_arc_writes']['done_ratio']['num'],
825                 )
826             )
827             sys.stdout.write("\t  Error Ratio:\t\t\t%s\t%s\n" % (
828                 arc['l2_arc_writes']['error_ratio']['per'],
829                 arc['l2_arc_writes']['error_ratio']['num'],
830                 )
831             )
832         else:
833             sys.stdout.write("\tWrites Sent:\t\t\t%s\t%s\n" % (
834                 arc['l2_arc_writes']['writes_sent']['per'],
835                 arc['l2_arc_writes']['writes_sent']['num'],
836                 )
837             )
838
839
840 def get_dmu_summary(Kstat):
841     """Collect information on the DMU"""
842
843     output = {}
844
845     zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"]
846     zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"]
847
848     zfetch_access_total = (zfetch_hits + zfetch_misses)
849     output['zfetch_access_total'] = zfetch_access_total
850
851     if zfetch_access_total > 0:
852         output['dmu'] = {}
853         output['dmu']['efficiency'] = {}
854         output['dmu']['efficiency']['value'] = fHits(zfetch_access_total)
855         output['dmu']['efficiency']['hit_ratio'] = {
856             'per': fPerc(zfetch_hits, zfetch_access_total),
857             'num': fHits(zfetch_hits),
858         }
859         output['dmu']['efficiency']['miss_ratio'] = {
860             'per': fPerc(zfetch_misses, zfetch_access_total),
861             'num': fHits(zfetch_misses),
862         }
863
864     return output
865
866
867 def _dmu_summary(Kstat):
868     """Print information on the DMU"""
869
870     arc = get_dmu_summary(Kstat)
871
872     if arc['zfetch_access_total'] > 0:
873         sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
874                          arc['dmu']['efficiency']['value'])
875         sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
876             arc['dmu']['efficiency']['hit_ratio']['per'],
877             arc['dmu']['efficiency']['hit_ratio']['num'],
878             )
879         )
880         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
881             arc['dmu']['efficiency']['miss_ratio']['per'],
882             arc['dmu']['efficiency']['miss_ratio']['num'],
883             )
884         )
885
886         sys.stdout.write("\n")
887
888
889 def get_vdev_summary(Kstat):
890     """Collect information on the VDEVs"""
891
892     output = {}
893
894     vdev_cache_delegations = \
895         Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"]
896     vdev_cache_misses = Kstat["kstat.zfs.misc.vdev_cache_stats.misses"]
897     vdev_cache_hits = Kstat["kstat.zfs.misc.vdev_cache_stats.hits"]
898     vdev_cache_total = (vdev_cache_misses + vdev_cache_hits +
899                         vdev_cache_delegations)
900
901     output['vdev_cache_total'] = vdev_cache_total
902
903     if vdev_cache_total > 0:
904         output['summary'] = fHits(vdev_cache_total)
905         output['hit_ratio'] = {
906             'per': fPerc(vdev_cache_hits, vdev_cache_total),
907             'num': fHits(vdev_cache_hits),
908         }
909         output['miss_ratio'] = {
910             'per': fPerc(vdev_cache_misses, vdev_cache_total),
911             'num': fHits(vdev_cache_misses),
912         }
913         output['delegations'] = {
914             'per': fPerc(vdev_cache_delegations, vdev_cache_total),
915             'num': fHits(vdev_cache_delegations),
916         }
917
918     return output
919
920
921 def _vdev_summary(Kstat):
922     """Print information on the VDEVs"""
923
924     arc = get_vdev_summary(Kstat)
925
926     if arc['vdev_cache_total'] > 0:
927         sys.stdout.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc['summary'])
928         sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
929             arc['hit_ratio']['per'],
930             arc['hit_ratio']['num'],
931         ))
932         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
933             arc['miss_ratio']['per'],
934             arc['miss_ratio']['num'],
935         ))
936         sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % (
937             arc['delegations']['per'],
938             arc['delegations']['num'],
939         ))
940
941
942 def _tunable_summary(Kstat):
943     """Print information on tunables, including descriptions if requested"""
944
945     global show_tunable_descriptions
946     global alternate_tunable_layout
947
948     tunables = load_tunables()
949     descriptions = {}
950
951     if show_tunable_descriptions:
952
953         command = ["/sbin/modinfo", "zfs", "-0"]
954
955         try:
956             p = Popen(command, stdin=PIPE, stdout=PIPE,
957                       stderr=PIPE, shell=False, close_fds=True)
958             p.wait()
959
960             # By default, Python 2 returns a string as the first element of the
961             # tuple from p.communicate(), while Python 3 returns bytes which
962             # must be decoded first. The better way to do this would be with
963             # subprocess.run() or at least .check_output(), but this fails on
964             # CentOS 6 because of its old version of Python 2
965             desc = bytes.decode(p.communicate()[0])
966             description_list = desc.strip().split('\0')
967
968             if p.returncode == 0:
969                 for tunable in description_list:
970                     if tunable[0:5] == 'parm:':
971                         tunable = tunable[5:].strip()
972                         name, description = tunable.split(':', 1)
973                         if not description:
974                             description = "Description unavailable"
975                         descriptions[name] = description
976             else:
977                 sys.stderr.write("%s: '%s' exited with code %i\n" %
978                                  (sys.argv[0], command[0], p.returncode))
979                 sys.stderr.write("Tunable descriptions will be disabled.\n")
980         except OSError as e:
981             sys.stderr.write("%s: Cannot run '%s': %s\n" %
982                              (sys.argv[0], command[0], e.strerror))
983             sys.stderr.write("Tunable descriptions will be disabled.\n")
984
985     sys.stdout.write("ZFS Tunables:\n")
986
987     if alternate_tunable_layout:
988         fmt = "\t%s=%s\n"
989     else:
990         fmt = "\t%-50s%s\n"
991
992     for name in sorted(tunables.keys()):
993         if show_tunable_descriptions and name in descriptions:
994             sys.stdout.write("\t# %s\n" % descriptions[name])
995
996         sys.stdout.write(fmt % (name, tunables[name]))
997
998
999 unSub = [
1000     _arc_summary,
1001     _arc_efficiency,
1002     _l2arc_summary,
1003     _dmu_summary,
1004     _vdev_summary,
1005     _tunable_summary
1006 ]
1007
1008
1009 def zfs_header():
1010     """Print title string with date"""
1011
1012     daydate = time.strftime('%a %b %d %H:%M:%S %Y')
1013
1014     sys.stdout.write('\n'+'-'*72+'\n')
1015     sys.stdout.write('ZFS Subsystem Report\t\t\t\t%s' % daydate)
1016     sys.stdout.write('\n')
1017
1018
1019 def usage():
1020     """Print usage information"""
1021
1022     sys.stdout.write("Usage: arc_summary [-h] [-a] [-d] [-p PAGE]\n\n")
1023     sys.stdout.write("\t -h, --help           : "
1024                      "Print this help message and exit\n")
1025     sys.stdout.write("\t -a, --alternate      : "
1026                      "Show an alternate sysctl layout\n")
1027     sys.stdout.write("\t -d, --description    : "
1028                      "Show the sysctl descriptions\n")
1029     sys.stdout.write("\t -p PAGE, --page=PAGE : "
1030                      "Select a single output page to display,\n")
1031     sys.stdout.write("\t                        "
1032                      "should be an integer between 1 and " +
1033                      str(len(unSub)) + "\n\n")
1034     sys.stdout.write("Examples:\n")
1035     sys.stdout.write("\tarc_summary -a\n")
1036     sys.stdout.write("\tarc_summary -p 4\n")
1037     sys.stdout.write("\tarc_summary -ad\n")
1038     sys.stdout.write("\tarc_summary --page=2\n")
1039
1040
1041 def main():
1042     """Main function"""
1043
1044     global show_tunable_descriptions
1045     global alternate_tunable_layout
1046
1047     try:
1048         opts, args = getopt.getopt(
1049             sys.argv[1:],
1050             "adp:h", ["alternate", "description", "page=", "help"]
1051         )
1052     except getopt.error as e:
1053         sys.stderr.write("Error: %s\n" % e.msg)
1054         usage()
1055         sys.exit(1)
1056
1057     args = {}
1058     for opt, arg in opts:
1059         if opt in ('-a', '--alternate'):
1060             args['a'] = True
1061         if opt in ('-d', '--description'):
1062             args['d'] = True
1063         if opt in ('-p', '--page'):
1064             args['p'] = arg
1065         if opt in ('-h', '--help'):
1066             usage()
1067             sys.exit(0)
1068
1069     Kstat = get_Kstat()
1070
1071     alternate_tunable_layout = 'a' in args
1072     show_tunable_descriptions = 'd' in args
1073
1074     pages = []
1075
1076     if 'p' in args:
1077         try:
1078             pages.append(unSub[int(args['p']) - 1])
1079         except IndexError:
1080             sys.stderr.write('the argument to -p must be between 1 and ' +
1081                              str(len(unSub)) + '\n')
1082             sys.exit(1)
1083     else:
1084         pages = unSub
1085
1086     zfs_header()
1087     for page in pages:
1088         page(Kstat)
1089         sys.stdout.write("\n")
1090
1091
1092 if __name__ == '__main__':
1093     main()