1 #!/usr/bin/env @PYTHON_SHEBANG@
3 # Print out ZFS ARC Statistics exported via kstat(1)
4 # For a definition of fields, or usage, use arcstat -v
6 # This script was originally a fork of the original arcstat.pl (0.1)
7 # by Neelakanth Nadgir, originally published on his Sun blog on
9 # http://blogs.sun.com/realneel/entry/zfs_arc_statistics
11 # A new version aimed to improve upon the original by adding features
12 # and fixing bugs as needed. This version was maintained by Mike
13 # Harsch and was hosted in a public open source repository:
14 # http://github.com/mharsch/arcstat
16 # but has since moved to the illumos-gate repository.
18 # This Python port was written by John Hixson for FreeNAS, introduced
20 # https://github.com/freenas/freenas
22 # and has been improved by many people since.
26 # The contents of this file are subject to the terms of the
27 # Common Development and Distribution License, Version 1.0 only
28 # (the "License"). You may not use this file except in compliance
31 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
32 # or http://www.opensolaris.org/os/licensing.
33 # See the License for the specific language governing permissions
34 # and limitations under the License.
36 # When distributing Covered Code, include this CDDL HEADER in each
37 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
38 # If applicable, add the following below this CDDL HEADER, with the
39 # fields enclosed by brackets "[]" replaced with your own identifying
40 # information: Portions Copyright [yyyy] [name of copyright owner]
45 # Fields have a fixed width. Every interval, we fill the "v"
46 # hash with its corresponding value (v[field]=value) using calculate().
47 # @hdr is the array of fields that needs to be printed, so we
48 # just iterate over this array and print the values using our pretty printer.
50 # This script must remain compatible with Python 3.6+.
59 from signal import signal, SIGINT, SIGWINCH, SIG_DFL
63 # HDR: [Size, Scale, Description]
64 "time": [8, -1, "Time"],
65 "hits": [4, 1000, "ARC reads per second"],
66 "miss": [4, 1000, "ARC misses per second"],
67 "read": [4, 1000, "Total ARC accesses per second"],
68 "hit%": [4, 100, "ARC hit percentage"],
69 "miss%": [5, 100, "ARC miss percentage"],
70 "dhit": [4, 1000, "Demand hits per second"],
71 "dmis": [4, 1000, "Demand misses per second"],
72 "dh%": [3, 100, "Demand hit percentage"],
73 "dm%": [3, 100, "Demand miss percentage"],
74 "phit": [4, 1000, "Prefetch hits per second"],
75 "pmis": [4, 1000, "Prefetch misses per second"],
76 "ph%": [3, 100, "Prefetch hits percentage"],
77 "pm%": [3, 100, "Prefetch miss percentage"],
78 "mhit": [4, 1000, "Metadata hits per second"],
79 "mmis": [4, 1000, "Metadata misses per second"],
80 "mread": [5, 1000, "Metadata accesses per second"],
81 "mh%": [3, 100, "Metadata hit percentage"],
82 "mm%": [3, 100, "Metadata miss percentage"],
83 "arcsz": [5, 1024, "ARC size"],
84 "size": [4, 1024, "ARC size"],
85 "c": [4, 1024, "ARC target size"],
86 "mfu": [4, 1000, "MFU list hits per second"],
87 "mru": [4, 1000, "MRU list hits per second"],
88 "mfug": [4, 1000, "MFU ghost list hits per second"],
89 "mrug": [4, 1000, "MRU ghost list hits per second"],
90 "eskip": [5, 1000, "evict_skip per second"],
91 "el2skip": [7, 1000, "evict skip, due to l2 writes, per second"],
92 "el2cach": [7, 1024, "Size of L2 cached evictions per second"],
93 "el2el": [5, 1024, "Size of L2 eligible evictions per second"],
94 "el2mfu": [6, 1024, "Size of L2 eligible MFU evictions per second"],
95 "el2mru": [6, 1024, "Size of L2 eligible MRU evictions per second"],
96 "el2inel": [7, 1024, "Size of L2 ineligible evictions per second"],
97 "mtxmis": [6, 1000, "mutex_miss per second"],
98 "dread": [5, 1000, "Demand accesses per second"],
99 "pread": [5, 1000, "Prefetch accesses per second"],
100 "l2hits": [6, 1000, "L2ARC hits per second"],
101 "l2miss": [6, 1000, "L2ARC misses per second"],
102 "l2read": [6, 1000, "Total L2ARC accesses per second"],
103 "l2hit%": [6, 100, "L2ARC access hit percentage"],
104 "l2miss%": [7, 100, "L2ARC access miss percentage"],
105 "l2pref": [6, 1024, "L2ARC prefetch allocated size"],
106 "l2mfu": [5, 1024, "L2ARC MFU allocated size"],
107 "l2mru": [5, 1024, "L2ARC MRU allocated size"],
108 "l2data": [6, 1024, "L2ARC data allocated size"],
109 "l2meta": [6, 1024, "L2ARC metadata allocated size"],
110 "l2pref%": [7, 100, "L2ARC prefetch percentage"],
111 "l2mfu%": [6, 100, "L2ARC MFU percentage"],
112 "l2mru%": [6, 100, "L2ARC MRU percentage"],
113 "l2data%": [7, 100, "L2ARC data percentage"],
114 "l2meta%": [7, 100, "L2ARC metadata percentage"],
115 "l2asize": [7, 1024, "Actual (compressed) size of the L2ARC"],
116 "l2size": [6, 1024, "Size of the L2ARC"],
117 "l2bytes": [7, 1024, "Bytes read per second from the L2ARC"],
118 "grow": [4, 1000, "ARC grow disabled"],
119 "need": [4, 1024, "ARC reclaim need"],
120 "free": [4, 1024, "ARC free memory"],
121 "avail": [5, 1024, "ARC available memory"],
122 "waste": [5, 1024, "Wasted memory due to round up to pagesize"],
126 hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis",
127 "mm%", "size", "c", "avail"]
128 xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "dread",
130 sint = 1 # Default interval is 1 second
131 count = 1 # Default count is 1
132 hdr_intr = 20 # Print header every 20 lines of output
134 sep = " " # Default separator is 2 spaces
137 cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
146 if sys.platform.startswith('freebsd'):
147 # Requires py-sysctl on FreeBSD
153 k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
154 if ctl.type != sysctl.CTLTYPE_NODE]
165 name, value = s.name, s.value
166 # Trims 'kstat.zfs.misc.arcstats' from the name
167 kstat[name[24:]] = int(value)
169 elif sys.platform.startswith('linux'):
173 k = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
185 name, unused, value = s.split()
186 kstat[name] = int(value)
189 def detailed_usage():
190 sys.stderr.write("%s\n" % cmd)
191 sys.stderr.write("Field definitions are as follows:\n")
193 sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
194 sys.stderr.write("\n")
200 sys.stderr.write("%s\n" % cmd)
201 sys.stderr.write("\t -h : Print this help message\n")
202 sys.stderr.write("\t -a : Print all possible stats\n")
203 sys.stderr.write("\t -v : List all possible field headers and definitions"
205 sys.stderr.write("\t -x : Print extended stats\n")
206 sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
207 sys.stderr.write("\t -o : Redirect output to the specified file\n")
208 sys.stderr.write("\t -s : Override default field separator with custom "
209 "character or string\n")
210 sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
211 sys.stderr.write("\nExamples:\n")
212 sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
213 sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
214 sys.stderr.write("\tarcstat -v\n")
215 sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
216 sys.stderr.write("\n")
225 prev = copy.deepcopy(cur)
230 if re.match(key, "class"):
233 d[key] = cur[key] - prev[key]
238 def prettynum(sz, scale, num=0):
239 suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
243 # Special case for date field
247 # Rounding error, return 0
251 while abs(num) > scale and index < 5:
257 return "%*d" % (sz, num)
259 if abs(save / scale) < 10:
260 return "%*.1f%s" % (sz - 1, num, suffix[index])
262 return "%*d%s" % (sz - 1, num, suffix[index])
272 fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
274 fmt = lambda col: v[col]
276 sys.stdout.write(sep.join(fmt(col) for col in hdr))
277 sys.stdout.write("\n")
287 fmt = lambda col: "%*s" % (cols[col][0], col)
289 fmt = lambda col: col
291 sys.stdout.write(sep.join(fmt(col) for col in hdr))
292 sys.stdout.write("\n")
295 def get_terminal_lines():
300 data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
301 sz = struct.unpack('hh', data)
307 def update_hdr_intr():
310 lines = get_terminal_lines()
311 if lines and lines > 3:
315 def resize_handler(signum, frame):
338 opts, args = getopt.getopt(
352 except getopt.error as msg:
353 sys.stderr.write("Error: %s\n" % str(msg))
357 for opt, arg in opts:
358 if opt in ('-a', '--all'):
360 if opt in ('-x', '--extended'):
362 if opt in ('-o', '--outfile'):
365 if opt in ('-h', '--help'):
367 if opt in ('-v', '--verbose'):
369 if opt in ('-s', '--separator'):
372 if opt in ('-f', '--columns'):
375 if opt in ('-p', '--parsable'):
380 sint = int(argv[0]) if argv else sint
381 count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)
383 if hflag or (xflag and desired_cols):
394 # check if L2ARC exists
396 l2_size = cur.get("l2_size")
401 hdr = desired_cols.split(",")
408 elif not l2exist and ele.startswith("l2"):
409 sys.stdout.write("No L2ARC Here\n%s\n" % ele)
413 sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
416 if len(incompat) > 0:
417 sys.stderr.write("Incompatible field specified! -- %s\n" %
425 hdr = [col for col in cols.keys() if not col.startswith("l2")]
429 out = open(opfile, "w")
433 sys.stderr.write("Cannot open %s for writing\n" % opfile)
443 v["time"] = time.strftime("%H:%M:%S", time.localtime())
444 v["hits"] = d["hits"] // sint
445 v["miss"] = d["misses"] // sint
446 v["read"] = v["hits"] + v["miss"]
447 v["hit%"] = 100 * v["hits"] // v["read"] if v["read"] > 0 else 0
448 v["miss%"] = 100 - v["hit%"] if v["read"] > 0 else 0
450 v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) // sint
451 v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) // sint
453 v["dread"] = v["dhit"] + v["dmis"]
454 v["dh%"] = 100 * v["dhit"] // v["dread"] if v["dread"] > 0 else 0
455 v["dm%"] = 100 - v["dh%"] if v["dread"] > 0 else 0
457 v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) // sint
458 v["pmis"] = (d["prefetch_data_misses"] +
459 d["prefetch_metadata_misses"]) // sint
461 v["pread"] = v["phit"] + v["pmis"]
462 v["ph%"] = 100 * v["phit"] // v["pread"] if v["pread"] > 0 else 0
463 v["pm%"] = 100 - v["ph%"] if v["pread"] > 0 else 0
465 v["mhit"] = (d["prefetch_metadata_hits"] +
466 d["demand_metadata_hits"]) // sint
467 v["mmis"] = (d["prefetch_metadata_misses"] +
468 d["demand_metadata_misses"]) // sint
470 v["mread"] = v["mhit"] + v["mmis"]
471 v["mh%"] = 100 * v["mhit"] // v["mread"] if v["mread"] > 0 else 0
472 v["mm%"] = 100 - v["mh%"] if v["mread"] > 0 else 0
474 v["arcsz"] = cur["size"]
475 v["size"] = cur["size"]
477 v["mfu"] = d["mfu_hits"] // sint
478 v["mru"] = d["mru_hits"] // sint
479 v["mrug"] = d["mru_ghost_hits"] // sint
480 v["mfug"] = d["mfu_ghost_hits"] // sint
481 v["eskip"] = d["evict_skip"] // sint
482 v["el2skip"] = d["evict_l2_skip"] // sint
483 v["el2cach"] = d["evict_l2_cached"] // sint
484 v["el2el"] = d["evict_l2_eligible"] // sint
485 v["el2mfu"] = d["evict_l2_eligible_mfu"] // sint
486 v["el2mru"] = d["evict_l2_eligible_mru"] // sint
487 v["el2inel"] = d["evict_l2_ineligible"] // sint
488 v["mtxmis"] = d["mutex_miss"] // sint
491 v["l2hits"] = d["l2_hits"] // sint
492 v["l2miss"] = d["l2_misses"] // sint
493 v["l2read"] = v["l2hits"] + v["l2miss"]
494 v["l2hit%"] = 100 * v["l2hits"] // v["l2read"] if v["l2read"] > 0 else 0
496 v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
497 v["l2asize"] = cur["l2_asize"]
498 v["l2size"] = cur["l2_size"]
499 v["l2bytes"] = d["l2_read_bytes"] // sint
501 v["l2pref"] = cur["l2_prefetch_asize"]
502 v["l2mfu"] = cur["l2_mfu_asize"]
503 v["l2mru"] = cur["l2_mru_asize"]
504 v["l2data"] = cur["l2_bufc_data_asize"]
505 v["l2meta"] = cur["l2_bufc_metadata_asize"]
506 v["l2pref%"] = 100 * v["l2pref"] // v["l2asize"]
507 v["l2mfu%"] = 100 * v["l2mfu"] // v["l2asize"]
508 v["l2mru%"] = 100 * v["l2mru"] // v["l2asize"]
509 v["l2data%"] = 100 * v["l2data"] // v["l2asize"]
510 v["l2meta%"] = 100 * v["l2meta"] // v["l2asize"]
512 v["grow"] = 0 if cur["arc_no_grow"] else 1
513 v["need"] = cur["arc_need_free"]
514 v["free"] = cur["memory_free_bytes"]
515 v["avail"] = cur["memory_available_bytes"]
516 v["waste"] = cur["abd_chunk_waste_size"]
531 signal(SIGINT, SIG_DFL)
532 signal(SIGWINCH, resize_handler)
546 i = 0 if i >= hdr_intr else i + 1
553 if __name__ == '__main__':