]> git.proxmox.com Git - mirror_zfs.git/blame - cmd/arcstat.in
libspl/include: remove unused/empty headers
[mirror_zfs.git] / cmd / arcstat.in
CommitLineData
38e2e9ce 1#!/usr/bin/env @PYTHON_SHEBANG@
7634cd54
CS
2#
3# Print out ZFS ARC Statistics exported via kstat(1)
b29e31d8 4# For a definition of fields, or usage, use arcstat -v
7634cd54 5#
b29e31d8
AJ
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
7634cd54
CS
8# 09/18/2007
9# http://blogs.sun.com/realneel/entry/zfs_arc_statistics
10#
b29e31d8
AJ
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:
7634cd54
CS
14# http://github.com/mharsch/arcstat
15#
b29e31d8
AJ
16# but has since moved to the illumos-gate repository.
17#
18# This Python port was written by John Hixson for FreeNAS, introduced
19# in commit e2c29f:
20# https://github.com/freenas/freenas
21#
22# and has been improved by many people since.
7634cd54
CS
23#
24# CDDL HEADER START
25#
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
29# with the License.
30#
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.
35#
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]
41#
42# CDDL HEADER END
43#
44#
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.
49#
8a7c4efd 50# This script must remain compatible with Python 3.6+.
6e72a5b9 51#
7634cd54
CS
52
53import sys
54import time
55import getopt
56import re
57import copy
58
a82db4e1 59from signal import signal, SIGINT, SIGWINCH, SIG_DFL
7634cd54 60
2f1ca8a3 61
7634cd54
CS
62cols = {
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"],
2b21da4f 68 "hit%": [4, 100, "ARC hit percentage"],
7634cd54 69 "miss%": [5, 100, "ARC miss percentage"],
1c49ac57
IH
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"],
7634cd54
CS
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"],
63a77ae3 80 "mread": [5, 1000, "Metadata accesses per second"],
7634cd54
CS
81 "mh%": [3, 100, "Metadata hit percentage"],
82 "mm%": [3, 100, "Metadata miss percentage"],
2b21da4f 83 "arcsz": [5, 1024, "ARC size"],
7b232e93 84 "size": [4, 1024, "ARC size"],
2b21da4f
AJ
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"],
7634cd54 90 "eskip": [5, 1000, "evict_skip per second"],
08532162
GA
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"],
7634cd54 97 "mtxmis": [6, 1000, "mutex_miss per second"],
1c49ac57 98 "dread": [5, 1000, "Demand accesses per second"],
7634cd54
CS
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"],
08532162
GA
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"],
e78b6da3 115 "l2asize": [7, 1024, "Actual (compressed) size of the L2ARC"],
7634cd54 116 "l2size": [6, 1024, "Size of the L2ARC"],
2b21da4f
AJ
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"],
7b232e93 121 "avail": [5, 1024, "ARC available memory"],
85ec5cba 122 "waste": [5, 1024, "Wasted memory due to round up to pagesize"],
7634cd54
CS
123}
124
125v = {}
126hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis",
7b232e93 127 "mm%", "size", "c", "avail"]
f4092679 128xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "dread",
129 "pread", "read"]
7634cd54
CS
130sint = 1 # Default interval is 1 second
131count = 1 # Default count is 1
132hdr_intr = 20 # Print header every 20 lines of output
133opfile = None
134sep = " " # Default separator is 2 spaces
135version = "0.4"
136l2exist = False
2aaab887 137cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
23bc1f91 138 "[count]]\n")
7634cd54
CS
139cur = {}
140d = {}
141out = None
142kstat = None
2aaab887 143pretty_print = True
7634cd54
CS
144
145
101f9b17 146if sys.platform.startswith('freebsd'):
513c1962 147 # Requires py-sysctl on FreeBSD
101f9b17
RM
148 import sysctl
149
150 def kstat_update():
151 global kstat
152
513c1962
RM
153 k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
154 if ctl.type != sysctl.CTLTYPE_NODE]
101f9b17
RM
155
156 if not k:
157 sys.exit(1)
158
159 kstat = {}
160
161 for s in k:
162 if not s:
163 continue
164
165 name, value = s.name, s.value
166 # Trims 'kstat.zfs.misc.arcstats' from the name
4df8b2c3 167 kstat[name[24:]] = int(value)
101f9b17
RM
168
169elif sys.platform.startswith('linux'):
2f1ca8a3
RM
170 def kstat_update():
171 global kstat
172
173 k = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
174
175 if not k:
176 sys.exit(1)
177
178 del k[0:2]
179 kstat = {}
180
181 for s in k:
182 if not s:
183 continue
184
185 name, unused, value = s.split()
4df8b2c3 186 kstat[name] = int(value)
2f1ca8a3
RM
187
188
7634cd54
CS
189def detailed_usage():
190 sys.stderr.write("%s\n" % cmd)
191 sys.stderr.write("Field definitions are as follows:\n")
192 for key in cols:
193 sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
194 sys.stderr.write("\n")
195
e169749f 196 sys.exit(0)
7634cd54
CS
197
198
199def usage():
200 sys.stderr.write("%s\n" % cmd)
201 sys.stderr.write("\t -h : Print this help message\n")
2aaab887 202 sys.stderr.write("\t -a : Print all possible stats\n")
7634cd54 203 sys.stderr.write("\t -v : List all possible field headers and definitions"
23bc1f91 204 "\n")
7634cd54
CS
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 "
23bc1f91 209 "character or string\n")
2aaab887 210 sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
7634cd54 211 sys.stderr.write("\nExamples:\n")
6e72a5b9
BB
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")
7634cd54
CS
216 sys.stderr.write("\n")
217
218 sys.exit(1)
219
220
7634cd54
CS
221def snap_stats():
222 global cur
223 global kstat
224
225 prev = copy.deepcopy(cur)
226 kstat_update()
227
228 cur = kstat
229 for key in cur:
230 if re.match(key, "class"):
231 continue
232 if key in prev:
233 d[key] = cur[key] - prev[key]
234 else:
235 d[key] = cur[key]
236
237
238def prettynum(sz, scale, num=0):
239 suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
240 index = 0
241 save = 0
242
243 # Special case for date field
244 if scale == -1:
245 return "%s" % num
246
247 # Rounding error, return 0
09d672d3 248 elif 0 < num < 1:
7634cd54
CS
249 num = 0
250
7b232e93 251 while abs(num) > scale and index < 5:
7634cd54
CS
252 save = num
253 num = num / scale
254 index += 1
255
256 if index == 0:
257 return "%*d" % (sz, num)
258
7b232e93 259 if abs(save / scale) < 10:
7634cd54
CS
260 return "%*.1f%s" % (sz - 1, num, suffix[index])
261 else:
262 return "%*d%s" % (sz - 1, num, suffix[index])
263
264
265def print_values():
266 global hdr
267 global sep
268 global v
2aaab887 269 global pretty_print
7634cd54 270
2aaab887
RM
271 if pretty_print:
272 fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
273 else:
274 fmt = lambda col: v[col]
7b232e93 275
2aaab887 276 sys.stdout.write(sep.join(fmt(col) for col in hdr))
7634cd54 277 sys.stdout.write("\n")
3ad59c01 278 sys.stdout.flush()
7634cd54
CS
279
280
281def print_header():
282 global hdr
283 global sep
2aaab887 284 global pretty_print
7634cd54 285
2aaab887
RM
286 if pretty_print:
287 fmt = lambda col: "%*s" % (cols[col][0], col)
288 else:
289 fmt = lambda col: col
7b232e93 290
2aaab887 291 sys.stdout.write(sep.join(fmt(col) for col in hdr))
7634cd54
CS
292 sys.stdout.write("\n")
293
0bde1f7c 294
4e1c9f9c
IH
295def get_terminal_lines():
296 try:
0bde1f7c
GDN
297 import fcntl
298 import termios
299 import struct
4e1c9f9c
IH
300 data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
301 sz = struct.unpack('hh', data)
302 return sz[0]
63e5e960 303 except Exception:
4e1c9f9c 304 pass
7634cd54 305
0bde1f7c 306
a82db4e1
IH
307def update_hdr_intr():
308 global hdr_intr
309
310 lines = get_terminal_lines()
311 if lines and lines > 3:
312 hdr_intr = lines - 3
313
0bde1f7c 314
a82db4e1
IH
315def resize_handler(signum, frame):
316 update_hdr_intr()
317
318
7634cd54
CS
319def init():
320 global sint
321 global count
322 global hdr
323 global xhdr
324 global opfile
325 global sep
326 global out
327 global l2exist
2aaab887 328 global pretty_print
7634cd54
CS
329
330 desired_cols = None
2aaab887 331 aflag = False
7634cd54
CS
332 xflag = False
333 hflag = False
334 vflag = False
335 i = 1
336
337 try:
338 opts, args = getopt.getopt(
339 sys.argv[1:],
2aaab887 340 "axo:hvs:f:p",
7634cd54 341 [
2aaab887 342 "all",
7634cd54
CS
343 "extended",
344 "outfile",
345 "help",
346 "verbose",
d699aaef 347 "separator",
2aaab887
RM
348 "columns",
349 "parsable"
7634cd54
CS
350 ]
351 )
09d672d3 352 except getopt.error as msg:
1f87313a 353 sys.stderr.write("Error: %s\n" % str(msg))
7634cd54 354 usage()
09d672d3 355 opts = None
7634cd54
CS
356
357 for opt, arg in opts:
2aaab887
RM
358 if opt in ('-a', '--all'):
359 aflag = True
7634cd54
CS
360 if opt in ('-x', '--extended'):
361 xflag = True
362 if opt in ('-o', '--outfile'):
363 opfile = arg
364 i += 1
365 if opt in ('-h', '--help'):
366 hflag = True
367 if opt in ('-v', '--verbose'):
368 vflag = True
d699aaef 369 if opt in ('-s', '--separator'):
7634cd54
CS
370 sep = arg
371 i += 1
372 if opt in ('-f', '--columns'):
373 desired_cols = arg
374 i += 1
2aaab887
RM
375 if opt in ('-p', '--parsable'):
376 pretty_print = False
7634cd54
CS
377 i += 1
378
379 argv = sys.argv[i:]
4df8b2c3
A
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)
7634cd54
CS
382
383 if hflag or (xflag and desired_cols):
384 usage()
385
386 if vflag:
387 detailed_usage()
388
389 if xflag:
390 hdr = xhdr
391
a82db4e1 392 update_hdr_intr()
4e1c9f9c 393
7634cd54
CS
394 # check if L2ARC exists
395 snap_stats()
396 l2_size = cur.get("l2_size")
397 if l2_size:
398 l2exist = True
399
400 if desired_cols:
401 hdr = desired_cols.split(",")
402
403 invalid = []
404 incompat = []
405 for ele in hdr:
406 if ele not in cols:
407 invalid.append(ele)
408 elif not l2exist and ele.startswith("l2"):
409 sys.stdout.write("No L2ARC Here\n%s\n" % ele)
410 incompat.append(ele)
411
412 if len(invalid) > 0:
413 sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
414 usage()
415
416 if len(incompat) > 0:
23bc1f91
MT
417 sys.stderr.write("Incompatible field specified! -- %s\n" %
418 incompat)
7634cd54
CS
419 usage()
420
2aaab887
RM
421 if aflag:
422 if l2exist:
423 hdr = cols.keys()
424 else:
425 hdr = [col for col in cols.keys() if not col.startswith("l2")]
426
7634cd54
CS
427 if opfile:
428 try:
429 out = open(opfile, "w")
430 sys.stdout = out
431
09d672d3 432 except IOError:
7634cd54
CS
433 sys.stderr.write("Cannot open %s for writing\n" % opfile)
434 sys.exit(1)
435
436
437def calculate():
438 global d
439 global v
440 global l2exist
441
09d672d3 442 v = dict()
7634cd54 443 v["time"] = time.strftime("%H:%M:%S", time.localtime())
2d02bba2
VA
444 v["hits"] = d["hits"] // sint
445 v["miss"] = d["misses"] // sint
7634cd54 446 v["read"] = v["hits"] + v["miss"]
2d02bba2 447 v["hit%"] = 100 * v["hits"] // v["read"] if v["read"] > 0 else 0
7634cd54
CS
448 v["miss%"] = 100 - v["hit%"] if v["read"] > 0 else 0
449
2d02bba2
VA
450 v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) // sint
451 v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) // sint
7634cd54
CS
452
453 v["dread"] = v["dhit"] + v["dmis"]
2d02bba2 454 v["dh%"] = 100 * v["dhit"] // v["dread"] if v["dread"] > 0 else 0
7634cd54
CS
455 v["dm%"] = 100 - v["dh%"] if v["dread"] > 0 else 0
456
2d02bba2 457 v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) // sint
7634cd54 458 v["pmis"] = (d["prefetch_data_misses"] +
2d02bba2 459 d["prefetch_metadata_misses"]) // sint
7634cd54
CS
460
461 v["pread"] = v["phit"] + v["pmis"]
2d02bba2 462 v["ph%"] = 100 * v["phit"] // v["pread"] if v["pread"] > 0 else 0
7634cd54
CS
463 v["pm%"] = 100 - v["ph%"] if v["pread"] > 0 else 0
464
465 v["mhit"] = (d["prefetch_metadata_hits"] +
2d02bba2 466 d["demand_metadata_hits"]) // sint
7634cd54 467 v["mmis"] = (d["prefetch_metadata_misses"] +
2d02bba2 468 d["demand_metadata_misses"]) // sint
7634cd54
CS
469
470 v["mread"] = v["mhit"] + v["mmis"]
2d02bba2 471 v["mh%"] = 100 * v["mhit"] // v["mread"] if v["mread"] > 0 else 0
7634cd54
CS
472 v["mm%"] = 100 - v["mh%"] if v["mread"] > 0 else 0
473
474 v["arcsz"] = cur["size"]
7b232e93 475 v["size"] = cur["size"]
7634cd54 476 v["c"] = cur["c"]
2d02bba2
VA
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
7634cd54
CS
489
490 if l2exist:
2d02bba2
VA
491 v["l2hits"] = d["l2_hits"] // sint
492 v["l2miss"] = d["l2_misses"] // sint
7634cd54 493 v["l2read"] = v["l2hits"] + v["l2miss"]
2d02bba2 494 v["l2hit%"] = 100 * v["l2hits"] // v["l2read"] if v["l2read"] > 0 else 0
7634cd54
CS
495
496 v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
e78b6da3 497 v["l2asize"] = cur["l2_asize"]
7634cd54 498 v["l2size"] = cur["l2_size"]
2d02bba2 499 v["l2bytes"] = d["l2_read_bytes"] // sint
7634cd54 500
08532162
GA
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"]
2d02bba2
VA
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"]
08532162 511
63a77ae3
GK
512 v["grow"] = 0 if cur["arc_no_grow"] else 1
513 v["need"] = cur["arc_need_free"]
7b232e93
MA
514 v["free"] = cur["memory_free_bytes"]
515 v["avail"] = cur["memory_available_bytes"]
85ec5cba 516 v["waste"] = cur["abd_chunk_waste_size"]
63a77ae3 517
7634cd54 518
7634cd54
CS
519def main():
520 global sint
521 global count
522 global hdr_intr
523
524 i = 0
525 count_flag = 0
526
527 init()
528 if count > 0:
529 count_flag = 1
530
312f82ce 531 signal(SIGINT, SIG_DFL)
a82db4e1 532 signal(SIGWINCH, resize_handler)
7634cd54
CS
533 while True:
534 if i == 0:
535 print_header()
536
537 snap_stats()
538 calculate()
539 print_values()
540
541 if count_flag == 1:
542 if count <= 1:
543 break
544 count -= 1
545
a82db4e1 546 i = 0 if i >= hdr_intr else i + 1
7634cd54
CS
547 time.sleep(sint)
548
549 if out:
550 out.close()
551
552
553if __name__ == '__main__':
554 main()