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