]> granicus.if.org Git - zfs/blob - cmd/arc_summary/arc_summary.py
Fix TypeError: unorderable types: str() > int() in arc_summary.py
[zfs] / cmd / arc_summary / arc_summary.py
1 #!/usr/bin/python
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 # /usr/bin & /sbin
36 #
37 # Binaries used are:
38 #
39 # dc(1), kldstat(8), sed(1), sysctl(8) & vmstat(8)
40 #
41 # Binaries that I am working on phasing out are:
42 #
43 # dc(1) & sed(1)
44
45 import sys
46 import time
47 import getopt
48 import re
49 from os import listdir
50 from subprocess import Popen, PIPE
51 from decimal import Decimal as D
52
53
54 usetunable = True
55 show_tunable_descriptions = False
56 alternate_tunable_layout = False
57 kstat_pobj = re.compile("^([^:]+):\s+(.+)\s*$", flags=re.M)
58
59
60 def get_Kstat():
61     def load_proc_kstats(fn, namespace):
62         kstats = [line.strip() for line in open(fn)]
63         del kstats[0:2]
64         for kstat in kstats:
65             kstat = kstat.strip()
66             name, unused, value = kstat.split()
67             Kstat[namespace + name] = D(value)
68
69     Kstat = {}
70     load_proc_kstats('/proc/spl/kstat/zfs/arcstats',
71                      'kstat.zfs.misc.arcstats.')
72     load_proc_kstats('/proc/spl/kstat/zfs/zfetchstats',
73                      'kstat.zfs.misc.zfetchstats.')
74     load_proc_kstats('/proc/spl/kstat/zfs/vdev_cache_stats',
75                      'kstat.zfs.misc.vdev_cache_stats.')
76
77     return Kstat
78
79
80 def div1():
81     sys.stdout.write("\n")
82     for i in range(18):
83         sys.stdout.write("%s" % "----")
84     sys.stdout.write("\n")
85
86
87 def div2():
88     sys.stdout.write("\n")
89
90
91 def fBytes(Bytes=0, Decimal=2):
92     kbytes = (2 ** 10)
93     mbytes = (2 ** 20)
94     gbytes = (2 ** 30)
95     tbytes = (2 ** 40)
96     pbytes = (2 ** 50)
97     ebytes = (2 ** 60)
98     zbytes = (2 ** 70)
99     ybytes = (2 ** 80)
100
101     if Bytes >= ybytes:
102         return str("%0." + str(Decimal) + "f") % (Bytes / ybytes) + "\tYiB"
103     elif Bytes >= zbytes:
104         return str("%0." + str(Decimal) + "f") % (Bytes / zbytes) + "\tZiB"
105     elif Bytes >= ebytes:
106         return str("%0." + str(Decimal) + "f") % (Bytes / ebytes) + "\tEiB"
107     elif Bytes >= pbytes:
108         return str("%0." + str(Decimal) + "f") % (Bytes / pbytes) + "\tPiB"
109     elif Bytes >= tbytes:
110         return str("%0." + str(Decimal) + "f") % (Bytes / tbytes) + "\tTiB"
111     elif Bytes >= gbytes:
112         return str("%0." + str(Decimal) + "f") % (Bytes / gbytes) + "\tGiB"
113     elif Bytes >= mbytes:
114         return str("%0." + str(Decimal) + "f") % (Bytes / mbytes) + "\tMiB"
115     elif Bytes >= kbytes:
116         return str("%0." + str(Decimal) + "f") % (Bytes / kbytes) + "\tKiB"
117     elif Bytes == 0:
118         return str("%d" % 0) + "\tBytes"
119     else:
120         return str("%d" % Bytes) + "\tBytes"
121
122
123 def fHits(Hits=0, Decimal=2):
124     khits = (10 ** 3)
125     mhits = (10 ** 6)
126     bhits = (10 ** 9)
127     thits = (10 ** 12)
128     qhits = (10 ** 15)
129     Qhits = (10 ** 18)
130     shits = (10 ** 21)
131     Shits = (10 ** 24)
132
133     if Hits >= Shits:
134         return str("%0." + str(Decimal) + "f") % (Hits / Shits) + "S"
135     elif Hits >= shits:
136         return str("%0." + str(Decimal) + "f") % (Hits / shits) + "s"
137     elif Hits >= Qhits:
138         return str("%0." + str(Decimal) + "f") % (Hits / Qhits) + "Q"
139     elif Hits >= qhits:
140         return str("%0." + str(Decimal) + "f") % (Hits / qhits) + "q"
141     elif Hits >= thits:
142         return str("%0." + str(Decimal) + "f") % (Hits / thits) + "t"
143     elif Hits >= bhits:
144         return str("%0." + str(Decimal) + "f") % (Hits / bhits) + "b"
145     elif Hits >= mhits:
146         return str("%0." + str(Decimal) + "f") % (Hits / mhits) + "m"
147     elif Hits >= khits:
148         return str("%0." + str(Decimal) + "f") % (Hits / khits) + "k"
149     elif Hits == 0:
150         return str("%d" % 0)
151     else:
152         return str("%d" % Hits)
153
154
155 def fPerc(lVal=0, rVal=0, Decimal=2):
156     if rVal > 0:
157         return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%"
158     else:
159         return str("%0." + str(Decimal) + "f") % 100 + "%"
160
161
162 def get_arc_summary(Kstat):
163
164     output = {}
165     memory_throttle_count = Kstat[
166         "kstat.zfs.misc.arcstats.memory_throttle_count"
167         ]
168
169     if memory_throttle_count > 0:
170         output['health'] = 'THROTTLED'
171     else:
172         output['health'] = 'HEALTHY'
173
174     output['memory_throttle_count'] = fHits(memory_throttle_count)
175
176     # ARC Misc.
177     deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
178     mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
179
180     # ARC Misc.
181     output["arc_misc"] = {}
182     output["arc_misc"]["deleted"] = fHits(deleted)
183     output["arc_misc"]['mutex_miss'] = fHits(mutex_miss)
184     output["arc_misc"]['evict_skips'] = fHits(mutex_miss)
185
186     # ARC Sizing
187     arc_size = Kstat["kstat.zfs.misc.arcstats.size"]
188     mru_size = Kstat["kstat.zfs.misc.arcstats.p"]
189     target_max_size = Kstat["kstat.zfs.misc.arcstats.c_max"]
190     target_min_size = Kstat["kstat.zfs.misc.arcstats.c_min"]
191     target_size = Kstat["kstat.zfs.misc.arcstats.c"]
192
193     target_size_ratio = (target_max_size / target_min_size)
194
195     # ARC Sizing
196     output['arc_sizing'] = {}
197     output['arc_sizing']['arc_size'] = {
198         'per': fPerc(arc_size, target_max_size),
199         'num': fBytes(arc_size),
200     }
201     output['arc_sizing']['target_max_size'] = {
202         'ratio': target_size_ratio,
203         'num': fBytes(target_max_size),
204     }
205     output['arc_sizing']['target_min_size'] = {
206         'per': fPerc(target_min_size, target_max_size),
207         'num': fBytes(target_min_size),
208     }
209     output['arc_sizing']['target_size'] = {
210         'per': fPerc(target_size, target_max_size),
211         'num': fBytes(target_size),
212     }
213
214     # ARC Hash Breakdown
215     output['arc_hash_break'] = {}
216     output['arc_hash_break']['hash_chain_max'] = Kstat[
217         "kstat.zfs.misc.arcstats.hash_chain_max"
218         ]
219     output['arc_hash_break']['hash_chains'] = Kstat[
220         "kstat.zfs.misc.arcstats.hash_chains"
221         ]
222     output['arc_hash_break']['hash_collisions'] = Kstat[
223         "kstat.zfs.misc.arcstats.hash_collisions"
224         ]
225     output['arc_hash_break']['hash_elements'] = Kstat[
226         "kstat.zfs.misc.arcstats.hash_elements"
227         ]
228     output['arc_hash_break']['hash_elements_max'] = Kstat[
229         "kstat.zfs.misc.arcstats.hash_elements_max"
230         ]
231
232     output['arc_size_break'] = {}
233     if arc_size > target_size:
234         mfu_size = (arc_size - mru_size)
235         output['arc_size_break']['recently_used_cache_size'] = {
236             'per': fPerc(mru_size, arc_size),
237             'num': fBytes(mru_size),
238         }
239         output['arc_size_break']['frequently_used_cache_size'] = {
240             'per': fPerc(mfu_size, arc_size),
241             'num': fBytes(mfu_size),
242         }
243
244     elif arc_size < target_size:
245         mfu_size = (target_size - mru_size)
246         output['arc_size_break']['recently_used_cache_size'] = {
247             'per': fPerc(mru_size, target_size),
248             'num': fBytes(mru_size),
249         }
250         output['arc_size_break']['frequently_used_cache_size'] = {
251             'per': fPerc(mfu_size, target_size),
252             'num': fBytes(mfu_size),
253         }
254
255     # ARC Hash Breakdown
256     hash_chain_max = Kstat["kstat.zfs.misc.arcstats.hash_chain_max"]
257     hash_chains = Kstat["kstat.zfs.misc.arcstats.hash_chains"]
258     hash_collisions = Kstat["kstat.zfs.misc.arcstats.hash_collisions"]
259     hash_elements = Kstat["kstat.zfs.misc.arcstats.hash_elements"]
260     hash_elements_max = Kstat["kstat.zfs.misc.arcstats.hash_elements_max"]
261
262     output['arc_hash_break'] = {}
263     output['arc_hash_break']['elements_max'] = fHits(hash_elements_max)
264     output['arc_hash_break']['elements_current'] = {
265         'per': fPerc(hash_elements, hash_elements_max),
266         'num': fHits(hash_elements),
267         }
268     output['arc_hash_break']['collisions'] = fHits(hash_collisions)
269     output['arc_hash_break']['chain_max'] = fHits(hash_chain_max)
270     output['arc_hash_break']['chains'] = fHits(hash_chains)
271
272     return output
273
274
275 def _arc_summary(Kstat):
276     # ARC Sizing
277     arc = get_arc_summary(Kstat)
278
279     sys.stdout.write("ARC Summary: (%s)\n" % arc['health'])
280
281     sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" %
282                      arc['memory_throttle_count'])
283     sys.stdout.write("\n")
284
285     # ARC Misc.
286     sys.stdout.write("ARC Misc:\n")
287     sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
288     sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
289                      arc['arc_misc']['mutex_miss'])
290     sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" %
291                      arc['arc_misc']['mutex_miss'])
292     sys.stdout.write("\n")
293
294     # ARC Sizing
295     sys.stdout.write("ARC Size:\t\t\t\t%s\t%s\n" % (
296         arc['arc_sizing']['arc_size']['per'],
297         arc['arc_sizing']['arc_size']['num']
298         )
299     )
300     sys.stdout.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % (
301         arc['arc_sizing']['target_size']['per'],
302         arc['arc_sizing']['target_size']['num'],
303         )
304     )
305
306     sys.stdout.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % (
307         arc['arc_sizing']['target_min_size']['per'],
308         arc['arc_sizing']['target_min_size']['num'],
309         )
310     )
311
312     sys.stdout.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % (
313         arc['arc_sizing']['target_max_size']['ratio'],
314         arc['arc_sizing']['target_max_size']['num'],
315         )
316     )
317
318     sys.stdout.write("\nARC Size Breakdown:\n")
319     sys.stdout.write("\tRecently Used Cache Size:\t%s\t%s\n" % (
320         arc['arc_size_break']['recently_used_cache_size']['per'],
321         arc['arc_size_break']['recently_used_cache_size']['num'],
322         )
323     )
324     sys.stdout.write("\tFrequently Used Cache Size:\t%s\t%s\n" % (
325         arc['arc_size_break']['frequently_used_cache_size']['per'],
326         arc['arc_size_break']['frequently_used_cache_size']['num'],
327         )
328     )
329
330     sys.stdout.write("\n")
331
332     # ARC Hash Breakdown
333     sys.stdout.write("ARC Hash Breakdown:\n")
334     sys.stdout.write("\tElements Max:\t\t\t\t%s\n" %
335                      arc['arc_hash_break']['elements_max'])
336     sys.stdout.write("\tElements Current:\t\t%s\t%s\n" % (
337         arc['arc_hash_break']['elements_current']['per'],
338         arc['arc_hash_break']['elements_current']['num'],
339         )
340     )
341     sys.stdout.write("\tCollisions:\t\t\t\t%s\n" %
342                      arc['arc_hash_break']['collisions'])
343     sys.stdout.write("\tChain Max:\t\t\t\t%s\n" %
344                      arc['arc_hash_break']['chain_max'])
345     sys.stdout.write("\tChains:\t\t\t\t\t%s\n" %
346                      arc['arc_hash_break']['chains'])
347
348
349 def get_arc_efficiency(Kstat):
350     output = {}
351
352     arc_hits = Kstat["kstat.zfs.misc.arcstats.hits"]
353     arc_misses = Kstat["kstat.zfs.misc.arcstats.misses"]
354     demand_data_hits = Kstat["kstat.zfs.misc.arcstats.demand_data_hits"]
355     demand_data_misses = Kstat["kstat.zfs.misc.arcstats.demand_data_misses"]
356     demand_metadata_hits = Kstat[
357         "kstat.zfs.misc.arcstats.demand_metadata_hits"
358         ]
359     demand_metadata_misses = Kstat[
360         "kstat.zfs.misc.arcstats.demand_metadata_misses"
361         ]
362     mfu_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mfu_ghost_hits"]
363     mfu_hits = Kstat["kstat.zfs.misc.arcstats.mfu_hits"]
364     mru_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mru_ghost_hits"]
365     mru_hits = Kstat["kstat.zfs.misc.arcstats.mru_hits"]
366     prefetch_data_hits = Kstat["kstat.zfs.misc.arcstats.prefetch_data_hits"]
367     prefetch_data_misses = Kstat[
368         "kstat.zfs.misc.arcstats.prefetch_data_misses"
369         ]
370     prefetch_metadata_hits = Kstat[
371         "kstat.zfs.misc.arcstats.prefetch_metadata_hits"
372         ]
373     prefetch_metadata_misses = Kstat[
374         "kstat.zfs.misc.arcstats.prefetch_metadata_misses"
375         ]
376
377     anon_hits = arc_hits - (
378         mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits
379         )
380     arc_accesses_total = (arc_hits + arc_misses)
381     demand_data_total = (demand_data_hits + demand_data_misses)
382     prefetch_data_total = (prefetch_data_hits + prefetch_data_misses)
383     real_hits = (mfu_hits + mru_hits)
384
385     output["total_accesses"] = fHits(arc_accesses_total)
386     output["cache_hit_ratio"] = {
387         'per': fPerc(arc_hits, arc_accesses_total),
388         'num': fHits(arc_hits),
389     }
390     output["cache_miss_ratio"] = {
391         'per': fPerc(arc_misses, arc_accesses_total),
392         'num': fHits(arc_misses),
393     }
394     output["actual_hit_ratio"] = {
395         'per': fPerc(real_hits, arc_accesses_total),
396         'num': fHits(real_hits),
397     }
398     output["data_demand_efficiency"] = {
399         'per': fPerc(demand_data_hits, demand_data_total),
400         'num': fHits(demand_data_total),
401     }
402
403     if prefetch_data_total > 0:
404         output["data_prefetch_efficiency"] = {
405             'per': fPerc(prefetch_data_hits, prefetch_data_total),
406             'num': fHits(prefetch_data_total),
407         }
408
409     if anon_hits > 0:
410         output["cache_hits_by_cache_list"] = {}
411         output["cache_hits_by_cache_list"]["anonymously_used"] = {
412             'per': fPerc(anon_hits, arc_hits),
413             'num': fHits(anon_hits),
414         }
415
416     output["most_recently_used"] = {
417         'per': fPerc(mru_hits, arc_hits),
418         'num': fHits(mru_hits),
419     }
420     output["most_frequently_used"] = {
421         'per': fPerc(mfu_hits, arc_hits),
422         'num': fHits(mfu_hits),
423     }
424     output["most_recently_used_ghost"] = {
425         'per': fPerc(mru_ghost_hits, arc_hits),
426         'num': fHits(mru_ghost_hits),
427     }
428     output["most_frequently_used_ghost"] = {
429         'per': fPerc(mfu_ghost_hits, arc_hits),
430         'num': fHits(mfu_ghost_hits),
431     }
432
433     output["cache_hits_by_data_type"] = {}
434     output["cache_hits_by_data_type"]["demand_data"] = {
435         'per': fPerc(demand_data_hits, arc_hits),
436         'num': fHits(demand_data_hits),
437     }
438     output["cache_hits_by_data_type"]["prefetch_data"] = {
439         'per': fPerc(prefetch_data_hits, arc_hits),
440         'num': fHits(prefetch_data_hits),
441     }
442     output["cache_hits_by_data_type"]["demand_metadata"] = {
443         'per': fPerc(demand_metadata_hits, arc_hits),
444         'num': fHits(demand_metadata_hits),
445     }
446     output["cache_hits_by_data_type"]["prefetch_metadata"] = {
447         'per': fPerc(prefetch_metadata_hits, arc_hits),
448         'num': fHits(prefetch_metadata_hits),
449     }
450
451     output["cache_misses_by_data_type"] = {}
452     output["cache_misses_by_data_type"]["demand_data"] = {
453         'per': fPerc(demand_data_misses, arc_misses),
454         'num': fHits(demand_data_misses),
455     }
456     output["cache_misses_by_data_type"]["prefetch_data"] = {
457         'per': fPerc(prefetch_data_misses, arc_misses),
458         'num': fHits(prefetch_data_misses),
459     }
460     output["cache_misses_by_data_type"]["demand_metadata"] = {
461         'per': fPerc(demand_metadata_misses, arc_misses),
462         'num': fHits(demand_metadata_misses),
463     }
464     output["cache_misses_by_data_type"]["prefetch_metadata"] = {
465         'per': fPerc(prefetch_metadata_misses, arc_misses),
466         'num': fHits(prefetch_metadata_misses),
467     }
468
469     return output
470
471
472 def _arc_efficiency(Kstat):
473     arc = get_arc_efficiency(Kstat)
474
475     sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" %
476                      arc['total_accesses'])
477     sys.stdout.write("\tCache Hit Ratio:\t\t%s\t%s\n" % (
478         arc['cache_hit_ratio']['per'],
479         arc['cache_hit_ratio']['num'],
480         )
481     )
482     sys.stdout.write("\tCache Miss Ratio:\t\t%s\t%s\n" % (
483         arc['cache_miss_ratio']['per'],
484         arc['cache_miss_ratio']['num'],
485         )
486     )
487
488     sys.stdout.write("\tActual Hit Ratio:\t\t%s\t%s\n" % (
489         arc['actual_hit_ratio']['per'],
490         arc['actual_hit_ratio']['num'],
491         )
492     )
493
494     sys.stdout.write("\n")
495     sys.stdout.write("\tData Demand Efficiency:\t\t%s\t%s\n" % (
496         arc['data_demand_efficiency']['per'],
497         arc['data_demand_efficiency']['num'],
498         )
499     )
500
501     if 'data_prefetch_efficiency' in arc:
502         sys.stdout.write("\tData Prefetch Efficiency:\t%s\t%s\n" % (
503             arc['data_prefetch_efficiency']['per'],
504             arc['data_prefetch_efficiency']['num'],
505             )
506         )
507     sys.stdout.write("\n")
508
509     sys.stdout.write("\tCACHE HITS BY CACHE LIST:\n")
510     if 'cache_hits_by_cache_list' in arc:
511         sys.stdout.write("\t  Anonymously Used:\t\t%s\t%s\n" % (
512             arc['cache_hits_by_cache_list']['anonymously_used']['per'],
513             arc['cache_hits_by_cache_list']['anonymously_used']['num'],
514             )
515         )
516     sys.stdout.write("\t  Most Recently Used:\t\t%s\t%s\n" % (
517         arc['most_recently_used']['per'],
518         arc['most_recently_used']['num'],
519         )
520     )
521     sys.stdout.write("\t  Most Frequently Used:\t\t%s\t%s\n" % (
522         arc['most_frequently_used']['per'],
523         arc['most_frequently_used']['num'],
524         )
525     )
526     sys.stdout.write("\t  Most Recently Used Ghost:\t%s\t%s\n" % (
527         arc['most_recently_used_ghost']['per'],
528         arc['most_recently_used_ghost']['num'],
529         )
530     )
531     sys.stdout.write("\t  Most Frequently Used Ghost:\t%s\t%s\n" % (
532         arc['most_frequently_used_ghost']['per'],
533         arc['most_frequently_used_ghost']['num'],
534         )
535     )
536
537     sys.stdout.write("\n\tCACHE HITS BY DATA TYPE:\n")
538     sys.stdout.write("\t  Demand Data:\t\t\t%s\t%s\n" % (
539         arc["cache_hits_by_data_type"]['demand_data']['per'],
540         arc["cache_hits_by_data_type"]['demand_data']['num'],
541         )
542     )
543     sys.stdout.write("\t  Prefetch Data:\t\t%s\t%s\n" % (
544         arc["cache_hits_by_data_type"]['prefetch_data']['per'],
545         arc["cache_hits_by_data_type"]['prefetch_data']['num'],
546         )
547     )
548     sys.stdout.write("\t  Demand Metadata:\t\t%s\t%s\n" % (
549         arc["cache_hits_by_data_type"]['demand_metadata']['per'],
550         arc["cache_hits_by_data_type"]['demand_metadata']['num'],
551         )
552     )
553     sys.stdout.write("\t  Prefetch Metadata:\t\t%s\t%s\n" % (
554         arc["cache_hits_by_data_type"]['prefetch_metadata']['per'],
555         arc["cache_hits_by_data_type"]['prefetch_metadata']['num'],
556         )
557     )
558
559     sys.stdout.write("\n\tCACHE MISSES BY DATA TYPE:\n")
560     sys.stdout.write("\t  Demand Data:\t\t\t%s\t%s\n" % (
561         arc["cache_misses_by_data_type"]['demand_data']['per'],
562         arc["cache_misses_by_data_type"]['demand_data']['num'],
563         )
564     )
565     sys.stdout.write("\t  Prefetch Data:\t\t%s\t%s\n" % (
566         arc["cache_misses_by_data_type"]['prefetch_data']['per'],
567         arc["cache_misses_by_data_type"]['prefetch_data']['num'],
568         )
569     )
570     sys.stdout.write("\t  Demand Metadata:\t\t%s\t%s\n" % (
571         arc["cache_misses_by_data_type"]['demand_metadata']['per'],
572         arc["cache_misses_by_data_type"]['demand_metadata']['num'],
573         )
574     )
575     sys.stdout.write("\t  Prefetch Metadata:\t\t%s\t%s\n" % (
576         arc["cache_misses_by_data_type"]['prefetch_metadata']['per'],
577         arc["cache_misses_by_data_type"]['prefetch_metadata']['num'],
578         )
579     )
580
581
582 def get_l2arc_summary(Kstat):
583     output = {}
584
585     l2_abort_lowmem = Kstat["kstat.zfs.misc.arcstats.l2_abort_lowmem"]
586     l2_cksum_bad = Kstat["kstat.zfs.misc.arcstats.l2_cksum_bad"]
587     l2_evict_lock_retry = Kstat["kstat.zfs.misc.arcstats.l2_evict_lock_retry"]
588     l2_evict_reading = Kstat["kstat.zfs.misc.arcstats.l2_evict_reading"]
589     l2_feeds = Kstat["kstat.zfs.misc.arcstats.l2_feeds"]
590     l2_free_on_write = Kstat["kstat.zfs.misc.arcstats.l2_free_on_write"]
591     l2_hdr_size = Kstat["kstat.zfs.misc.arcstats.l2_hdr_size"]
592     l2_hits = Kstat["kstat.zfs.misc.arcstats.l2_hits"]
593     l2_io_error = Kstat["kstat.zfs.misc.arcstats.l2_io_error"]
594     l2_misses = Kstat["kstat.zfs.misc.arcstats.l2_misses"]
595     l2_rw_clash = Kstat["kstat.zfs.misc.arcstats.l2_rw_clash"]
596     l2_size = Kstat["kstat.zfs.misc.arcstats.l2_size"]
597     l2_asize = Kstat["kstat.zfs.misc.arcstats.l2_asize"]
598     l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"]
599     l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"]
600     l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"]
601
602     l2_access_total = (l2_hits + l2_misses)
603     output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error)
604
605     output['l2_access_total'] = l2_access_total
606     output['l2_size'] = l2_size
607     output['l2_asize'] = l2_asize
608
609     if l2_size > 0 and l2_access_total > 0:
610
611         if output['l2_health_count'] > 0:
612             output["health"] = "DEGRADED"
613         else:
614             output["health"] = "HEALTHY"
615
616         output["low_memory_aborts"] = fHits(l2_abort_lowmem)
617         output["free_on_write"] = fHits(l2_free_on_write)
618         output["rw_clashes"] = fHits(l2_rw_clash)
619         output["bad_checksums"] = fHits(l2_cksum_bad)
620         output["io_errors"] = fHits(l2_io_error)
621
622         output["l2_arc_size"] = {}
623         output["l2_arc_size"]["adative"] = fBytes(l2_size)
624         output["l2_arc_size"]["actual"] = {
625             'per': fPerc(l2_asize, l2_size),
626             'num': fBytes(l2_asize)
627             }
628         output["l2_arc_size"]["head_size"] = {
629             'per': fPerc(l2_hdr_size, l2_size),
630             'num': fBytes(l2_hdr_size),
631         }
632
633         output["l2_arc_evicts"] = {}
634         output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry)
635         output["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading)
636
637         output['l2_arc_breakdown'] = {}
638         output['l2_arc_breakdown']['value'] = fHits(l2_access_total)
639         output['l2_arc_breakdown']['hit_ratio'] = {
640             'per': fPerc(l2_hits, l2_access_total),
641             'num': fHits(l2_hits),
642         }
643         output['l2_arc_breakdown']['miss_ratio'] = {
644             'per': fPerc(l2_misses, l2_access_total),
645             'num': fHits(l2_misses),
646         }
647         output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds)
648
649         output['l2_arc_buffer'] = {}
650
651         output['l2_arc_writes'] = {}
652         output['l2_writes_done'] = l2_writes_done
653         output['l2_writes_sent'] = l2_writes_sent
654         if l2_writes_done != l2_writes_sent:
655             output['l2_arc_writes']['writes_sent'] = {
656                 'value': "FAULTED",
657                 'num': fHits(l2_writes_sent),
658             }
659             output['l2_arc_writes']['done_ratio'] = {
660                 'per': fPerc(l2_writes_done, l2_writes_sent),
661                 'num': fHits(l2_writes_done),
662             }
663             output['l2_arc_writes']['error_ratio'] = {
664                 'per': fPerc(l2_writes_error, l2_writes_sent),
665                 'num': fHits(l2_writes_error),
666             }
667         else:
668             output['l2_arc_writes']['writes_sent'] = {
669                 'per': fPerc(100),
670                 'num': fHits(l2_writes_sent),
671             }
672
673     return output
674
675
676 def _l2arc_summary(Kstat):
677
678     arc = get_l2arc_summary(Kstat)
679
680     if arc['l2_size'] > 0 and arc['l2_access_total'] > 0:
681         sys.stdout.write("L2 ARC Summary: ")
682         if arc['l2_health_count'] > 0:
683             sys.stdout.write("(DEGRADED)\n")
684         else:
685             sys.stdout.write("(HEALTHY)\n")
686         sys.stdout.write("\tLow Memory Aborts:\t\t\t%s\n" %
687                          arc['low_memory_aborts'])
688         sys.stdout.write("\tFree on Write:\t\t\t\t%s\n" % arc['free_on_write'])
689         sys.stdout.write("\tR/W Clashes:\t\t\t\t%s\n" % arc['rw_clashes'])
690         sys.stdout.write("\tBad Checksums:\t\t\t\t%s\n" % arc['bad_checksums'])
691         sys.stdout.write("\tIO Errors:\t\t\t\t%s\n" % arc['io_errors'])
692         sys.stdout.write("\n")
693
694         sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
695                          arc["l2_arc_size"]["adative"])
696         sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % (
697             arc["l2_arc_size"]["actual"]["per"],
698             arc["l2_arc_size"]["actual"]["num"],
699             )
700         )
701         sys.stdout.write("\tHeader Size:\t\t\t%s\t%s\n" % (
702             arc["l2_arc_size"]["head_size"]["per"],
703             arc["l2_arc_size"]["head_size"]["num"],
704             )
705         )
706         sys.stdout.write("\n")
707
708         if arc["l2_arc_evicts"]['lock_retries'] != '0' or \
709            arc["l2_arc_evicts"]["reading"] != '0':
710             sys.stdout.write("L2 ARC Evicts:\n")
711             sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" %
712                              arc["l2_arc_evicts"]['lock_retries'])
713             sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" %
714                              arc["l2_arc_evicts"]["reading"])
715             sys.stdout.write("\n")
716
717         sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
718                          arc['l2_arc_breakdown']['value'])
719         sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
720             arc['l2_arc_breakdown']['hit_ratio']['per'],
721             arc['l2_arc_breakdown']['hit_ratio']['num'],
722             )
723         )
724
725         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
726             arc['l2_arc_breakdown']['miss_ratio']['per'],
727             arc['l2_arc_breakdown']['miss_ratio']['num'],
728             )
729         )
730
731         sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" %
732                          arc['l2_arc_breakdown']['feeds'])
733         sys.stdout.write("\n")
734
735         sys.stdout.write("L2 ARC Writes:\n")
736         if arc['l2_writes_done'] != arc['l2_writes_sent']:
737             sys.stdout.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % (
738                 arc['l2_arc_writes']['writes_sent']['value'],
739                 arc['l2_arc_writes']['writes_sent']['num'],
740                 )
741             )
742             sys.stdout.write("\t  Done Ratio:\t\t\t%s\t%s\n" % (
743                 arc['l2_arc_writes']['done_ratio']['per'],
744                 arc['l2_arc_writes']['done_ratio']['num'],
745                 )
746             )
747             sys.stdout.write("\t  Error Ratio:\t\t\t%s\t%s\n" % (
748                 arc['l2_arc_writes']['error_ratio']['per'],
749                 arc['l2_arc_writes']['error_ratio']['num'],
750                 )
751             )
752         else:
753             sys.stdout.write("\tWrites Sent:\t\t\t%s\t%s\n" % (
754                 arc['l2_arc_writes']['writes_sent']['per'],
755                 arc['l2_arc_writes']['writes_sent']['num'],
756                 )
757             )
758
759
760 def get_dmu_summary(Kstat):
761     output = {}
762
763     zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"]
764     zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"]
765
766     zfetch_access_total = (zfetch_hits + zfetch_misses)
767     output['zfetch_access_total'] = zfetch_access_total
768
769     if zfetch_access_total > 0:
770         output['dmu'] = {}
771         output['dmu']['efficiency'] = {}
772         output['dmu']['efficiency']['value'] = fHits(zfetch_access_total)
773         output['dmu']['efficiency']['hit_ratio'] = {
774             'per': fPerc(zfetch_hits, zfetch_access_total),
775             'num': fHits(zfetch_hits),
776         }
777         output['dmu']['efficiency']['miss_ratio'] = {
778             'per': fPerc(zfetch_misses, zfetch_access_total),
779             'num': fHits(zfetch_misses),
780         }
781
782     return output
783
784
785 def _dmu_summary(Kstat):
786
787     arc = get_dmu_summary(Kstat)
788
789     if arc['zfetch_access_total'] > 0:
790         sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
791                          arc['dmu']['efficiency']['value'])
792         sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
793             arc['dmu']['efficiency']['hit_ratio']['per'],
794             arc['dmu']['efficiency']['hit_ratio']['num'],
795             )
796         )
797         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
798             arc['dmu']['efficiency']['miss_ratio']['per'],
799             arc['dmu']['efficiency']['miss_ratio']['num'],
800             )
801         )
802
803         sys.stdout.write("\n")
804
805
806 def get_vdev_summary(Kstat):
807     output = {}
808
809     vdev_cache_delegations = \
810         Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"]
811     vdev_cache_misses = Kstat["kstat.zfs.misc.vdev_cache_stats.misses"]
812     vdev_cache_hits = Kstat["kstat.zfs.misc.vdev_cache_stats.hits"]
813     vdev_cache_total = (vdev_cache_misses + vdev_cache_hits +
814                         vdev_cache_delegations)
815
816     output['vdev_cache_total'] = vdev_cache_total
817
818     if vdev_cache_total > 0:
819         output['summary'] = fHits(vdev_cache_total)
820         output['hit_ratio'] = {
821             'per': fPerc(vdev_cache_hits, vdev_cache_total),
822             'num': fHits(vdev_cache_hits),
823         }
824         output['miss_ratio'] = {
825             'per': fPerc(vdev_cache_misses, vdev_cache_total),
826             'num': fHits(vdev_cache_misses),
827         }
828         output['delegations'] = {
829             'per': fPerc(vdev_cache_delegations, vdev_cache_total),
830             'num': fHits(vdev_cache_delegations),
831         }
832
833     return output
834
835
836 def _vdev_summary(Kstat):
837     arc = get_vdev_summary(Kstat)
838
839     if arc['vdev_cache_total'] > 0:
840         sys.stdout.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc['summary'])
841         sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % (
842             arc['hit_ratio']['per'],
843             arc['hit_ratio']['num'],
844         ))
845         sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % (
846             arc['miss_ratio']['per'],
847             arc['miss_ratio']['num'],
848         ))
849         sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % (
850             arc['delegations']['per'],
851             arc['delegations']['num'],
852         ))
853
854
855 def _tunable_summary(Kstat):
856     global show_tunable_descriptions
857     global alternate_tunable_layout
858
859     names = listdir("/sys/module/zfs/parameters/")
860
861     values = {}
862     for name in names:
863         with open("/sys/module/zfs/parameters/" + name) as f:
864             value = f.read()
865         values[name] = value.strip()
866
867     descriptions = {}
868
869     if show_tunable_descriptions:
870         try:
871             command = ["/sbin/modinfo", "zfs", "-0"]
872             p = Popen(command, stdin=PIPE, stdout=PIPE,
873                       stderr=PIPE, shell=False, close_fds=True)
874             p.wait()
875
876             description_list = p.communicate()[0].strip().split('\0')
877
878             if p.returncode == 0:
879                 for tunable in description_list:
880                     if tunable[0:5] == 'parm:':
881                         tunable = tunable[5:].strip()
882                         name, description = tunable.split(':', 1)
883                         if not description:
884                             description = "Description unavailable"
885                         descriptions[name] = description
886             else:
887                 sys.stderr.write("%s: '%s' exited with code %i\n" %
888                                  (sys.argv[0], command[0], p.returncode))
889                 sys.stderr.write("Tunable descriptions will be disabled.\n")
890         except OSError as e:
891             sys.stderr.write("%s: Cannot run '%s': %s\n" %
892                              (sys.argv[0], command[0], e.strerror))
893             sys.stderr.write("Tunable descriptions will be disabled.\n")
894
895     sys.stdout.write("ZFS Tunable:\n")
896     for name in names:
897         if not name:
898             continue
899
900         format = "\t%-50s%s\n"
901         if alternate_tunable_layout:
902             format = "\t%s=%s\n"
903
904         if show_tunable_descriptions and name in descriptions:
905             sys.stdout.write("\t# %s\n" % descriptions[name])
906
907         sys.stdout.write(format % (name, values[name]))
908
909
910 unSub = [
911     _arc_summary,
912     _arc_efficiency,
913     _l2arc_summary,
914     _dmu_summary,
915     _vdev_summary,
916     _tunable_summary
917 ]
918
919
920 def zfs_header():
921     daydate = time.strftime("%a %b %d %H:%M:%S %Y")
922
923     div1()
924     sys.stdout.write("ZFS Subsystem Report\t\t\t\t%s" % daydate)
925     div2()
926
927
928 def usage():
929     sys.stdout.write("Usage: arc_summary.py [-h] [-a] [-d] [-p PAGE]\n\n")
930     sys.stdout.write("\t -h, --help           : "
931                      "Print this help message and exit\n")
932     sys.stdout.write("\t -a, --alternate      : "
933                      "Show an alternate sysctl layout\n")
934     sys.stdout.write("\t -d, --description    : "
935                      "Show the sysctl descriptions\n")
936     sys.stdout.write("\t -p PAGE, --page=PAGE : "
937                      "Select a single output page to display,\n")
938     sys.stdout.write("\t                        "
939                      "should be an integer between 1 and " +
940                      str(len(unSub)) + "\n\n")
941     sys.stdout.write("Examples:\n")
942     sys.stdout.write("\tarc_summary.py -a\n")
943     sys.stdout.write("\tarc_summary.py -p 4\n")
944     sys.stdout.write("\tarc_summary.py -ad\n")
945     sys.stdout.write("\tarc_summary.py --page=2\n")
946
947
948 def main():
949     global show_tunable_descriptions
950     global alternate_tunable_layout
951
952     opts, args = getopt.getopt(
953         sys.argv[1:], "adp:h", ["alternate", "description", "page=", "help"]
954     )
955
956     args = {}
957     for opt, arg in opts:
958         if opt in ('-a', '--alternate'):
959             args['a'] = True
960         if opt in ('-d', '--description'):
961             args['d'] = True
962         if opt in ('-p', '--page'):
963             args['p'] = arg
964         if opt in ('-h', '--help'):
965             usage()
966             sys.exit()
967
968     Kstat = get_Kstat()
969
970     alternate_tunable_layout = 'a' in args
971     show_tunable_descriptions = 'd' in args
972
973     pages = []
974
975     if 'p' in args:
976         try:
977             pages.append(unSub[int(args['p']) - 1])
978         except IndexError:
979             sys.stderr.write('the argument to -p must be between 1 and ' +
980                              str(len(unSub)) + '\n')
981             sys.exit()
982     else:
983         pages = unSub
984
985     zfs_header()
986     for page in pages:
987         page(Kstat)
988         div2()
989
990 if __name__ == '__main__':
991     main()