]> git.proxmox.com Git - mirror_zfs-debian.git/blame - cmd/arc_summary/arc_summary.py
New upstream version 0.7.6
[mirror_zfs-debian.git] / cmd / arc_summary / arc_summary.py
CommitLineData
ea04106b
AX
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#
047218e2
AX
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)
ea04106b 39
047218e2
AX
40Provides basic information on the ARC, its efficiency, the L2ARC (if present),
41the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the
42in-source documentation and code at
43https://github.com/zfsonlinux/zfs/blob/master/module/zfs/arc.c for details.
44"""
45
46import getopt
47import os
ea04106b
AX
48import sys
49import time
047218e2
AX
50import errno
51
ea04106b
AX
52from subprocess import Popen, PIPE
53from decimal import Decimal as D
54
ea04106b
AX
55show_tunable_descriptions = False
56alternate_tunable_layout = False
047218e2
AX
57
58
59def 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
68sys.excepthook = handle_Exception
ea04106b
AX
69
70
71def get_Kstat():
047218e2
AX
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
ea04106b 77 def load_proc_kstats(fn, namespace):
047218e2
AX
78 """Collect information on a specific subsystem of the ARC"""
79
ea04106b
AX
80 kstats = [line.strip() for line in open(fn)]
81 del kstats[0:2]
82 for kstat in kstats:
83 kstat = kstat.strip()
047218e2 84 name, _, value = kstat.split()
ea04106b
AX
85 Kstat[namespace + name] = D(value)
86
ea04106b
AX
87 Kstat = {}
88 load_proc_kstats('/proc/spl/kstat/zfs/arcstats',
cae5b340 89 'kstat.zfs.misc.arcstats.')
ea04106b 90 load_proc_kstats('/proc/spl/kstat/zfs/zfetchstats',
cae5b340 91 'kstat.zfs.misc.zfetchstats.')
ea04106b 92 load_proc_kstats('/proc/spl/kstat/zfs/vdev_cache_stats',
cae5b340 93 'kstat.zfs.misc.vdev_cache_stats.')
ea04106b
AX
94
95 return Kstat
96
cae5b340 97
047218e2
AX
98def 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 """
ea04106b 104
047218e2
AX
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)
ea04106b 114
047218e2
AX
115 if b >= 2**10:
116
117 for limit, unit in prefixes:
ea04106b 118
047218e2
AX
119 if b >= limit:
120 value = b / limit
121 break
122
123 result = "%0.2f\t%s" % (value, unit)
ea04106b 124
ea04106b 125 else:
047218e2
AX
126
127 result = "%d\tBytes" % b
128
129 return result
130
131
132def 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
ea04106b 161 else:
047218e2
AX
162
163 result = "%d" % hits
164
165 return result
ea04106b
AX
166
167
168def fPerc(lVal=0, rVal=0, Decimal=2):
047218e2
AX
169 """Calculate percentage value and return in human-readable format"""
170
ea04106b
AX
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
177def get_arc_summary(Kstat):
047218e2 178 """Collect general data on the ARC"""
ea04106b
AX
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
cae5b340 192 # ARC Misc.
ea04106b
AX
193 deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
194 mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
047218e2 195 evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"]
ea04106b 196
cae5b340 197 # ARC Misc.
ea04106b
AX
198 output["arc_misc"] = {}
199 output["arc_misc"]["deleted"] = fHits(deleted)
ea04106b 200 output["arc_misc"]['mutex_miss'] = fHits(mutex_miss)
047218e2 201 output["arc_misc"]['evict_skips'] = fHits(evict_skip)
ea04106b 202
cae5b340 203 # ARC Sizing
ea04106b 204 arc_size = Kstat["kstat.zfs.misc.arcstats.size"]
41d74433
AX
205 mru_size = Kstat["kstat.zfs.misc.arcstats.mru_size"]
206 mfu_size = Kstat["kstat.zfs.misc.arcstats.mfu_size"]
ea04106b
AX
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
cae5b340 213 # ARC Sizing
ea04106b
AX
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
cae5b340 232 # ARC Hash Breakdown
ea04106b
AX
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'] = {}
41d74433
AX
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 }
ea04106b 259
cae5b340 260 # ARC Hash Breakdown
ea04106b
AX
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
280def _arc_summary(Kstat):
047218e2
AX
281 """Print information on the ARC"""
282
cae5b340 283 # ARC Sizing
ea04106b
AX
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" %
cae5b340 289 arc['memory_throttle_count'])
ea04106b
AX
290 sys.stdout.write("\n")
291
cae5b340 292 # ARC Misc.
ea04106b
AX
293 sys.stdout.write("ARC Misc:\n")
294 sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
ea04106b 295 sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
cae5b340 296 arc['arc_misc']['mutex_miss'])
ea04106b 297 sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" %
047218e2 298 arc['arc_misc']['evict_skips'])
ea04106b
AX
299 sys.stdout.write("\n")
300
cae5b340 301 # ARC Sizing
ea04106b
AX
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
cae5b340 339 # ARC Hash Breakdown
ea04106b
AX
340 sys.stdout.write("ARC Hash Breakdown:\n")
341 sys.stdout.write("\tElements Max:\t\t\t\t%s\n" %
cae5b340 342 arc['arc_hash_break']['elements_max'])
ea04106b
AX
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" %
cae5b340 349 arc['arc_hash_break']['collisions'])
ea04106b 350 sys.stdout.write("\tChain Max:\t\t\t\t%s\n" %
cae5b340 351 arc['arc_hash_break']['chain_max'])
ea04106b 352 sys.stdout.write("\tChains:\t\t\t\t\t%s\n" %
cae5b340 353 arc['arc_hash_break']['chains'])
ea04106b
AX
354
355
356def get_arc_efficiency(Kstat):
047218e2
AX
357 """Collect information on the efficiency of the ARC"""
358
ea04106b
AX
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
481def _arc_efficiency(Kstat):
047218e2
AX
482 """Print information on the efficiency of the ARC"""
483
ea04106b
AX
484 arc = get_arc_efficiency(Kstat)
485
486 sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" %
cae5b340 487 arc['total_accesses'])
ea04106b
AX
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
593def get_l2arc_summary(Kstat):
047218e2
AX
594 """Collection information on the L2ARC"""
595
ea04106b
AX
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
689def _l2arc_summary(Kstat):
047218e2 690 """Print information on the L2ARC"""
ea04106b
AX
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" %
cae5b340 701 arc['low_memory_aborts'])
ea04106b
AX
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" %
cae5b340 709 arc["l2_arc_size"]["adative"])
ea04106b
AX
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
cae5b340
AX
722 if arc["l2_arc_evicts"]['lock_retries'] != '0' or \
723 arc["l2_arc_evicts"]["reading"] != '0':
ea04106b
AX
724 sys.stdout.write("L2 ARC Evicts:\n")
725 sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" %
cae5b340 726 arc["l2_arc_evicts"]['lock_retries'])
ea04106b 727 sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" %
cae5b340 728 arc["l2_arc_evicts"]["reading"])
ea04106b
AX
729 sys.stdout.write("\n")
730
731 sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" %
cae5b340 732 arc['l2_arc_breakdown']['value'])
ea04106b
AX
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" %
cae5b340 746 arc['l2_arc_breakdown']['feeds'])
ea04106b
AX
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
774def get_dmu_summary(Kstat):
047218e2
AX
775 """Collect information on the DMU"""
776
ea04106b
AX
777 output = {}
778
ea04106b
AX
779 zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"]
780 zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"]
ea04106b
AX
781
782 zfetch_access_total = (zfetch_hits + zfetch_misses)
ea04106b
AX
783 output['zfetch_access_total'] = zfetch_access_total
784
785 if zfetch_access_total > 0:
ea04106b
AX
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
ea04106b
AX
798 return output
799
800
801def _dmu_summary(Kstat):
047218e2 802 """Print information on the DMU"""
ea04106b
AX
803
804 arc = get_dmu_summary(Kstat)
805
806 if arc['zfetch_access_total'] > 0:
cae5b340
AX
807 sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" %
808 arc['dmu']['efficiency']['value'])
ea04106b
AX
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
ea04106b
AX
822
823def get_vdev_summary(Kstat):
047218e2
AX
824 """Collect information on the VDEVs"""
825
ea04106b
AX
826 output = {}
827
828 vdev_cache_delegations = \
cae5b340 829 Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"]
ea04106b
AX
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 +
cae5b340 833 vdev_cache_delegations)
ea04106b
AX
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
855def _vdev_summary(Kstat):
047218e2
AX
856 """Print information on the VDEVs"""
857
ea04106b
AX
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
876def _tunable_summary(Kstat):
047218e2
AX
877 """Print information on tunables, including descriptions if requested"""
878
ea04106b
AX
879 global show_tunable_descriptions
880 global alternate_tunable_layout
881
047218e2 882 names = os.listdir("/sys/module/zfs/parameters/")
ea04106b
AX
883
884 values = {}
885 for name in names:
cae5b340
AX
886 with open("/sys/module/zfs/parameters/" + name) as f:
887 value = f.read()
ea04106b
AX
888 values[name] = value.strip()
889
890 descriptions = {}
891
892 if show_tunable_descriptions:
047218e2
AX
893
894 command = ["/sbin/modinfo", "zfs", "-0"]
895
ea04106b 896 try:
ea04106b 897 p = Popen(command, stdin=PIPE, stdout=PIPE,
cae5b340 898 stderr=PIPE, shell=False, close_fds=True)
ea04106b
AX
899 p.wait()
900
047218e2
AX
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')
ea04106b
AX
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" %
cae5b340 919 (sys.argv[0], command[0], p.returncode))
ea04106b
AX
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" %
cae5b340 923 (sys.argv[0], command[0], e.strerror))
ea04106b
AX
924 sys.stderr.write("Tunable descriptions will be disabled.\n")
925
047218e2
AX
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
ea04106b 934 for name in names:
047218e2 935
ea04106b
AX
936 if not name:
937 continue
938
4d815aed 939 if show_tunable_descriptions and name in descriptions:
ea04106b
AX
940 sys.stdout.write("\t# %s\n" % descriptions[name])
941
047218e2 942 sys.stdout.write(fmt % (name, values[name]))
ea04106b
AX
943
944
945unSub = [
946 _arc_summary,
947 _arc_efficiency,
948 _l2arc_summary,
949 _dmu_summary,
950 _vdev_summary,
951 _tunable_summary
952]
953
954
955def zfs_header():
047218e2
AX
956 """Print title string with date"""
957
958 daydate = time.strftime('%a %b %d %H:%M:%S %Y')
ea04106b 959
047218e2
AX
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')
ea04106b
AX
963
964
965def usage():
047218e2
AX
966 """Print usage information"""
967
ea04106b
AX
968 sys.stdout.write("Usage: arc_summary.py [-h] [-a] [-d] [-p PAGE]\n\n")
969 sys.stdout.write("\t -h, --help : "
cae5b340 970 "Print this help message and exit\n")
ea04106b 971 sys.stdout.write("\t -a, --alternate : "
cae5b340 972 "Show an alternate sysctl layout\n")
ea04106b 973 sys.stdout.write("\t -d, --description : "
cae5b340 974 "Show the sysctl descriptions\n")
ea04106b 975 sys.stdout.write("\t -p PAGE, --page=PAGE : "
cae5b340 976 "Select a single output page to display,\n")
ea04106b 977 sys.stdout.write("\t "
cae5b340
AX
978 "should be an integer between 1 and " +
979 str(len(unSub)) + "\n\n")
ea04106b
AX
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
cae5b340 986
ea04106b 987def main():
047218e2
AX
988 """Main function"""
989
ea04106b
AX
990 global show_tunable_descriptions
991 global alternate_tunable_layout
992
047218e2
AX
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)
ea04106b
AX
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()
047218e2 1013 sys.exit(0)
ea04106b
AX
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])
cae5b340 1025 except IndexError:
ea04106b 1026 sys.stderr.write('the argument to -p must be between 1 and ' +
cae5b340 1027 str(len(unSub)) + '\n')
047218e2 1028 sys.exit(1)
ea04106b
AX
1029 else:
1030 pages = unSub
1031
1032 zfs_header()
1033 for page in pages:
1034 page(Kstat)
047218e2 1035 sys.stdout.write("\n")
ea04106b 1036
cae5b340 1037
ea04106b
AX
1038if __name__ == '__main__':
1039 main()