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