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