1 #!/usr/local/bin/python
3 # Print out ZFS ARC Statistics exported via kstat(1)
4 # For a definition of fields, or usage, use arctstat.pl -v
6 # This script is a fork of the original arcstat.pl (0.1) by
7 # Neelakanth Nadgir, originally published on his Sun blog on
9 # http://blogs.sun.com/realneel/entry/zfs_arc_statistics
11 # This version aims to improve upon the original by adding features
12 # and fixing bugs as needed. This version is maintained by
13 # Mike Harsch and is hosted in a public open source repository:
14 # http://github.com/mharsch/arcstat
16 # Comments, Questions, or Suggestions are always welcome.
17 # Contact the maintainer at ( mike at harschsystems dot com )
21 # The contents of this file are subject to the terms of the
22 # Common Development and Distribution License, Version 1.0 only
23 # (the "License"). You may not use this file except in compliance
26 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
27 # or http://www.opensolaris.org/os/licensing.
28 # See the License for the specific language governing permissions
29 # and limitations under the License.
31 # When distributing Covered Code, include this CDDL HEADER in each
32 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
33 # If applicable, add the following below this CDDL HEADER, with the
34 # fields enclosed by brackets "[]" replaced with your own identifying
35 # information: Portions Copyright [yyyy] [name of copyright owner]
40 # Fields have a fixed width. Every interval, we fill the "v"
41 # hash with its corresponding value (v[field]=value) using calculate().
42 # @hdr is the array of fields that needs to be printed, so we
43 # just iterate over this array and print the values using our pretty printer.
53 from decimal import Decimal
54 from subprocess import Popen, PIPE
55 from signal import signal, SIGINT
58 # HDR: [Size, Scale, Description]
59 "time": [8, -1, "Time"],
60 "hits": [4, 1000, "ARC reads per second"],
61 "miss": [4, 1000, "ARC misses per second"],
62 "read": [4, 1000, "Total ARC accesses per second"],
63 "hit%": [4, 100, "ARC Hit percentage"],
64 "miss%": [5, 100, "ARC miss percentage"],
65 "dhit": [4, 1000, "Demand Data hits per second"],
66 "dmis": [4, 1000, "Demand Data misses per second"],
67 "dh%": [3, 100, "Demand Data hit percentage"],
68 "dm%": [3, 100, "Demand Data miss percentage"],
69 "phit": [4, 1000, "Prefetch hits per second"],
70 "pmis": [4, 1000, "Prefetch misses per second"],
71 "ph%": [3, 100, "Prefetch hits percentage"],
72 "pm%": [3, 100, "Prefetch miss percentage"],
73 "mhit": [4, 1000, "Metadata hits per second"],
74 "mmis": [4, 1000, "Metadata misses per second"],
75 "mread": [4, 1000, "Metadata accesses per second"],
76 "mh%": [3, 100, "Metadata hit percentage"],
77 "mm%": [3, 100, "Metadata miss percentage"],
78 "arcsz": [5, 1024, "ARC Size"],
79 "c": [4, 1024, "ARC Target Size"],
80 "mfu": [4, 1000, "MFU List hits per second"],
81 "mru": [4, 1000, "MRU List hits per second"],
82 "mfug": [4, 1000, "MFU Ghost List hits per second"],
83 "mrug": [4, 1000, "MRU Ghost List hits per second"],
84 "eskip": [5, 1000, "evict_skip per second"],
85 "mtxmis": [6, 1000, "mutex_miss per second"],
86 "rmis": [4, 1000, "recycle_miss per second"],
87 "dread": [5, 1000, "Demand data accesses per second"],
88 "pread": [5, 1000, "Prefetch accesses per second"],
89 "l2hits": [6, 1000, "L2ARC hits per second"],
90 "l2miss": [6, 1000, "L2ARC misses per second"],
91 "l2read": [6, 1000, "Total L2ARC accesses per second"],
92 "l2hit%": [6, 100, "L2ARC access hit percentage"],
93 "l2miss%": [7, 100, "L2ARC access miss percentage"],
94 "l2size": [6, 1024, "Size of the L2ARC"],
95 "l2bytes": [7, 1024, "bytes read per second from the L2ARC"],
99 hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis",
101 xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "rmis",
102 "dread", "pread", "read"]
103 sint = 1 # Default interval is 1 second
104 count = 1 # Default count is 1
105 hdr_intr = 20 # Print header every 20 lines of output
107 sep = " " # Default separator is 2 spaces
110 cmd = ("Usage: arcstat [-hvx] [-f fields] [-o file] [-s string] [interval "
116 float_pobj = re.compile("^[0-9]+(\.[0-9]+)?$")
119 def detailed_usage():
120 sys.stderr.write("%s\n" % cmd)
121 sys.stderr.write("Field definitions are as follows:\n")
123 sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
124 sys.stderr.write("\n")
130 sys.stderr.write("%s\n" % cmd)
131 sys.stderr.write("\t -h : Print this help message\n")
132 sys.stderr.write("\t -v : List all possible field headers and definitions"
134 sys.stderr.write("\t -x : Print extended stats\n")
135 sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
136 sys.stderr.write("\t -o : Redirect output to the specified file\n")
137 sys.stderr.write("\t -s : Override default field separator with custom "
138 "character or string\n")
139 sys.stderr.write("\nExamples:\n")
140 sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
141 sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
142 sys.stderr.write("\tarcstat -v\n")
143 sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
144 sys.stderr.write("\n")
152 p = Popen("/sbin/sysctl -q 'kstat.zfs.misc.arcstats'", stdin=PIPE,
153 stdout=PIPE, stderr=PIPE, shell=True, close_fds=True)
156 k = p.communicate()[0].split('\n')
157 if p.returncode != 0:
171 name, value = s.split(':')
173 value = value.strip()
175 parts = name.split('.')
178 kstat[n] = Decimal(value)
185 prev = copy.deepcopy(cur)
190 if re.match(key, "class"):
193 d[key] = cur[key] - prev[key]
198 def prettynum(sz, scale, num=0):
199 suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
203 # Special case for date field
207 # Rounding error, return 0
208 elif num > 0 and num < 1:
211 while num > scale and index < 5:
217 return "%*d" % (sz, num)
219 if (save / scale) < 10:
220 return "%*.1f%s" % (sz - 1, num, suffix[index])
222 return "%*d%s" % (sz - 1, num, suffix[index])
231 sys.stdout.write("%s%s" % (
232 prettynum(cols[col][0], cols[col][1], v[col]),
235 sys.stdout.write("\n")
243 sys.stdout.write("%*s%s" % (cols[col][0], col, sep))
244 sys.stdout.write("\n")
264 opts, args = getopt.getopt(
277 except getopt.error, msg:
278 sys.stderr.write(msg)
281 for opt, arg in opts:
282 if opt in ('-x', '--extended'):
284 if opt in ('-o', '--outfile'):
287 if opt in ('-h', '--help'):
289 if opt in ('-v', '--verbose'):
291 if opt in ('-s', '--seperator'):
294 if opt in ('-f', '--columns'):
300 sint = Decimal(argv[0]) if argv else sint
301 count = int(argv[1]) if len(argv) > 1 else count
304 sint = Decimal(argv[0])
308 sint = Decimal(argv[0])
311 if hflag or (xflag and desired_cols):
320 # check if L2ARC exists
322 l2_size = cur.get("l2_size")
327 hdr = desired_cols.split(",")
334 elif not l2exist and ele.startswith("l2"):
335 sys.stdout.write("No L2ARC Here\n%s\n" % ele)
339 sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
342 if len(incompat) > 0:
343 sys.stderr.write("Incompatible field specified! -- %s\n" % (
350 out = open(opfile, "w")
354 sys.stderr.write("Cannot open %s for writing\n" % opfile)
364 v["time"] = time.strftime("%H:%M:%S", time.localtime())
365 v["hits"] = d["hits"] / sint
366 v["miss"] = d["misses"] / sint
367 v["read"] = v["hits"] + v["miss"]
368 v["hit%"] = 100 * v["hits"] / v["read"] if v["read"] > 0 else 0
369 v["miss%"] = 100 - v["hit%"] if v["read"] > 0 else 0
371 v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) / sint
372 v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) / sint
374 v["dread"] = v["dhit"] + v["dmis"]
375 v["dh%"] = 100 * v["dhit"] / v["dread"] if v["dread"] > 0 else 0
376 v["dm%"] = 100 - v["dh%"] if v["dread"] > 0 else 0
378 v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) / sint
379 v["pmis"] = (d["prefetch_data_misses"] +
380 d["prefetch_metadata_misses"]) / sint
382 v["pread"] = v["phit"] + v["pmis"]
383 v["ph%"] = 100 * v["phit"] / v["pread"] if v["pread"] > 0 else 0
384 v["pm%"] = 100 - v["ph%"] if v["pread"] > 0 else 0
386 v["mhit"] = (d["prefetch_metadata_hits"] +
387 d["demand_metadata_hits"]) / sint
388 v["mmis"] = (d["prefetch_metadata_misses"] +
389 d["demand_metadata_misses"]) / sint
391 v["mread"] = v["mhit"] + v["mmis"]
392 v["mh%"] = 100 * v["mhit"] / v["mread"] if v["mread"] > 0 else 0
393 v["mm%"] = 100 - v["mh%"] if v["mread"] > 0 else 0
395 v["arcsz"] = cur["size"]
397 v["mfu"] = d["mfu_hits"] / sint
398 v["mru"] = d["mru_hits"] / sint
399 v["mrug"] = d["mru_ghost_hits"] / sint
400 v["mfug"] = d["mfu_ghost_hits"] / sint
401 v["eskip"] = d["evict_skip"] / sint
402 v["rmis"] = d["recycle_miss"] / sint
403 v["mtxmis"] = d["mutex_miss"] / sint
406 v["l2hits"] = d["l2_hits"] / sint
407 v["l2miss"] = d["l2_misses"] / sint
408 v["l2read"] = v["l2hits"] + v["l2miss"]
409 v["l2hit%"] = 100 * v["l2hits"] / v["l2read"] if v["l2read"] > 0 else 0
411 v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
412 v["l2size"] = cur["l2_size"]
413 v["l2bytes"] = d["l2_read_bytes"] / sint
416 def sighandler(*args):
432 signal(SIGINT, sighandler)
446 i = 0 if i == hdr_intr else i + 1
453 if __name__ == '__main__':