mirror of
				https://git.proxmox.com/git/mirror_zfs
				synced 2025-10-31 09:29:10 +00:00 
			
		
		
		
	 4d469acd17
			
		
	
	
		4d469acd17
		
			
		
	
	
	
	
		
			
			Add ARC structural breakdown, ARC types breakdown, ARC states breakdown similar to arc_summary. Additional cleanups included. Reviewed-by: Alexander Motin <mav@FreeBSD.org> Signed-off-by: Theera K. <tkittich@hotmail.com> Closes #16509
		
			
				
	
	
		
			802 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			802 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Plaintext
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env @PYTHON_SHEBANG@
 | |
| #
 | |
| # Print out ZFS ARC Statistics exported via kstat(1)
 | |
| # For a definition of fields, or usage, use arcstat -v
 | |
| #
 | |
| # This script was originally a fork of the original arcstat.pl (0.1)
 | |
| # by Neelakanth Nadgir, originally published on his Sun blog on
 | |
| # 09/18/2007
 | |
| #     http://blogs.sun.com/realneel/entry/zfs_arc_statistics
 | |
| #
 | |
| # A new version aimed to improve upon the original by adding features
 | |
| # and fixing bugs as needed.  This version was maintained by Mike
 | |
| # Harsch and was hosted in a public open source repository:
 | |
| #    http://github.com/mharsch/arcstat
 | |
| #
 | |
| # but has since moved to the illumos-gate repository.
 | |
| #
 | |
| # This Python port was written by John Hixson for FreeNAS, introduced
 | |
| # in commit e2c29f:
 | |
| #    https://github.com/freenas/freenas
 | |
| #
 | |
| # and has been improved by many people since.
 | |
| #
 | |
| # CDDL HEADER START
 | |
| #
 | |
| # The contents of this file are subject to the terms of the
 | |
| # Common Development and Distribution License, Version 1.0 only
 | |
| # (the "License").  You may not use this file except in compliance
 | |
| # with the License.
 | |
| #
 | |
| # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 | |
| # or https://opensource.org/licenses/CDDL-1.0.
 | |
| # See the License for the specific language governing permissions
 | |
| # and limitations under the License.
 | |
| #
 | |
| # When distributing Covered Code, include this CDDL HEADER in each
 | |
| # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 | |
| # If applicable, add the following below this CDDL HEADER, with the
 | |
| # fields enclosed by brackets "[]" replaced with your own identifying
 | |
| # information: Portions Copyright [yyyy] [name of copyright owner]
 | |
| #
 | |
| # CDDL HEADER END
 | |
| #
 | |
| #
 | |
| # Fields have a fixed width. Every interval, we fill the "v"
 | |
| # hash with its corresponding value (v[field]=value) using calculate().
 | |
| # @hdr is the array of fields that needs to be printed, so we
 | |
| # just iterate over this array and print the values using our pretty printer.
 | |
| #
 | |
| # This script must remain compatible with Python 3.6+.
 | |
| #
 | |
| 
 | |
| import sys
 | |
| import time
 | |
| import getopt
 | |
| import re
 | |
| import copy
 | |
| 
 | |
| from signal import signal, SIGINT, SIGWINCH, SIG_DFL
 | |
| 
 | |
| 
 | |
| cols = {
 | |
|     # HDR:        [Size, Scale, Description]
 | |
|     "time":       [8, -1, "Time"],
 | |
|     "hits":       [4, 1000, "ARC hits per second"],
 | |
|     "iohs":       [4, 1000, "ARC I/O hits per second"],
 | |
|     "miss":       [4, 1000, "ARC misses per second"],
 | |
|     "read":       [4, 1000, "Total ARC accesses per second"],
 | |
|     "hit%":       [4, 100, "ARC hit percentage"],
 | |
|     "ioh%":       [4, 100, "ARC I/O hit percentage"],
 | |
|     "miss%":      [5, 100, "ARC miss percentage"],
 | |
|     "dhit":       [4, 1000, "Demand hits per second"],
 | |
|     "dioh":       [4, 1000, "Demand I/O hits per second"],
 | |
|     "dmis":       [4, 1000, "Demand misses per second"],
 | |
|     "dh%":        [3, 100, "Demand hit percentage"],
 | |
|     "di%":        [3, 100, "Demand I/O hit percentage"],
 | |
|     "dm%":        [3, 100, "Demand miss percentage"],
 | |
|     "ddhit":      [5, 1000, "Demand data hits per second"],
 | |
|     "ddioh":      [5, 1000, "Demand data I/O hits per second"],
 | |
|     "ddmis":      [5, 1000, "Demand data misses per second"],
 | |
|     "ddh%":       [4, 100, "Demand data hit percentage"],
 | |
|     "ddi%":       [4, 100, "Demand data I/O hit percentage"],
 | |
|     "ddm%":       [4, 100, "Demand data miss percentage"],
 | |
|     "dmhit":      [5, 1000, "Demand metadata hits per second"],
 | |
|     "dmioh":      [5, 1000, "Demand metadata I/O hits per second"],
 | |
|     "dmmis":      [5, 1000, "Demand metadata misses per second"],
 | |
|     "dmh%":       [4, 100, "Demand metadata hit percentage"],
 | |
|     "dmi%":       [4, 100, "Demand metadata I/O hit percentage"],
 | |
|     "dmm%":       [4, 100, "Demand metadata miss percentage"],
 | |
|     "phit":       [4, 1000, "Prefetch hits per second"],
 | |
|     "pioh":       [4, 1000, "Prefetch I/O hits per second"],
 | |
|     "pmis":       [4, 1000, "Prefetch misses per second"],
 | |
|     "ph%":        [3, 100, "Prefetch hits percentage"],
 | |
|     "pi%":        [3, 100, "Prefetch I/O hits percentage"],
 | |
|     "pm%":        [3, 100, "Prefetch miss percentage"],
 | |
|     "pdhit":      [5, 1000, "Prefetch data hits per second"],
 | |
|     "pdioh":      [5, 1000, "Prefetch data I/O hits per second"],
 | |
|     "pdmis":      [5, 1000, "Prefetch data misses per second"],
 | |
|     "pdh%":       [4, 100, "Prefetch data hits percentage"],
 | |
|     "pdi%":       [4, 100, "Prefetch data I/O hits percentage"],
 | |
|     "pdm%":       [4, 100, "Prefetch data miss percentage"],
 | |
|     "pmhit":      [5, 1000, "Prefetch metadata hits per second"],
 | |
|     "pmioh":      [5, 1000, "Prefetch metadata I/O hits per second"],
 | |
|     "pmmis":      [5, 1000, "Prefetch metadata misses per second"],
 | |
|     "pmh%":       [4, 100, "Prefetch metadata hits percentage"],
 | |
|     "pmi%":       [4, 100, "Prefetch metadata I/O hits percentage"],
 | |
|     "pmm%":       [4, 100, "Prefetch metadata miss percentage"],
 | |
|     "mhit":       [4, 1000, "Metadata hits per second"],
 | |
|     "mioh":       [4, 1000, "Metadata I/O hits per second"],
 | |
|     "mmis":       [4, 1000, "Metadata misses per second"],
 | |
|     "mread":      [5, 1000, "Metadata accesses per second"],
 | |
|     "mh%":        [3, 100, "Metadata hit percentage"],
 | |
|     "mi%":        [3, 100, "Metadata I/O hit percentage"],
 | |
|     "mm%":        [3, 100, "Metadata miss percentage"],
 | |
|     "arcsz":      [5, 1024, "ARC size"],
 | |
|     "size":       [5, 1024, "ARC size"],
 | |
|     "c":          [5, 1024, "ARC target size"],
 | |
|     "mfu":        [4, 1000, "MFU list hits per second"],
 | |
|     "mru":        [4, 1000, "MRU list hits per second"],
 | |
|     "mfug":       [4, 1000, "MFU ghost list hits per second"],
 | |
|     "mrug":       [4, 1000, "MRU ghost list hits per second"],
 | |
|     "unc":        [4, 1000, "Uncached list hits per second"],
 | |
|     "eskip":      [5, 1000, "evict_skip per second"],
 | |
|     "el2skip":    [7, 1000, "evict skip, due to l2 writes, per second"],
 | |
|     "el2cach":    [7, 1024, "Size of L2 cached evictions per second"],
 | |
|     "el2el":      [5, 1024, "Size of L2 eligible evictions per second"],
 | |
|     "el2mfu":     [6, 1024, "Size of L2 eligible MFU evictions per second"],
 | |
|     "el2mru":     [6, 1024, "Size of L2 eligible MRU evictions per second"],
 | |
|     "el2inel":    [7, 1024, "Size of L2 ineligible evictions per second"],
 | |
|     "mtxmis":     [6, 1000, "mutex_miss per second"],
 | |
|     "dread":      [5, 1000, "Demand accesses per second"],
 | |
|     "ddread":     [6, 1000, "Demand data accesses per second"],
 | |
|     "dmread":     [6, 1000, "Demand metadata accesses per second"],
 | |
|     "pread":      [5, 1000, "Prefetch accesses per second"],
 | |
|     "pdread":     [6, 1000, "Prefetch data accesses per second"],
 | |
|     "pmread":     [6, 1000, "Prefetch metadata accesses per second"],
 | |
|     "l2hits":     [6, 1000, "L2ARC hits per second"],
 | |
|     "l2miss":     [6, 1000, "L2ARC misses per second"],
 | |
|     "l2read":     [6, 1000, "Total L2ARC accesses per second"],
 | |
|     "l2hit%":     [6, 100, "L2ARC access hit percentage"],
 | |
|     "l2miss%":    [7, 100, "L2ARC access miss percentage"],
 | |
|     "l2pref":     [6, 1024, "L2ARC prefetch allocated size"],
 | |
|     "l2mfu":      [5, 1024, "L2ARC MFU allocated size"],
 | |
|     "l2mru":      [5, 1024, "L2ARC MRU allocated size"],
 | |
|     "l2data":     [6, 1024, "L2ARC data allocated size"],
 | |
|     "l2meta":     [6, 1024, "L2ARC metadata allocated size"],
 | |
|     "l2pref%":    [7, 100, "L2ARC prefetch percentage"],
 | |
|     "l2mfu%":     [6, 100, "L2ARC MFU percentage"],
 | |
|     "l2mru%":     [6, 100, "L2ARC MRU percentage"],
 | |
|     "l2data%":    [7, 100, "L2ARC data percentage"],
 | |
|     "l2meta%":    [7, 100, "L2ARC metadata percentage"],
 | |
|     "l2asize":    [7, 1024, "Actual (compressed) size of the L2ARC"],
 | |
|     "l2size":     [6, 1024, "Size of the L2ARC"],
 | |
|     "l2bytes":    [7, 1024, "Bytes read per second from the L2ARC"],
 | |
|     "l2wbytes":   [8, 1024, "Bytes written per second to the L2ARC"],
 | |
|     "grow":       [4, 1000, "ARC grow disabled"],
 | |
|     "need":       [5, 1024, "ARC reclaim need"],
 | |
|     "free":       [5, 1024, "ARC free memory"],
 | |
|     "avail":      [5, 1024, "ARC available memory"],
 | |
|     "waste":      [5, 1024, "Wasted memory due to round up to pagesize"],
 | |
|     "ztotal":     [6, 1000, "zfetch total prefetcher calls per second"],
 | |
|     "zhits":      [5, 1000, "zfetch stream hits per second"],
 | |
|     "zahead":     [6, 1000, "zfetch hits ahead of streams per second"],
 | |
|     "zpast":      [5, 1000, "zfetch hits behind streams per second"],
 | |
|     "zmisses":    [7, 1000, "zfetch stream misses per second"],
 | |
|     "zmax":       [4, 1000, "zfetch limit reached per second"],
 | |
|     "zfuture":    [7, 1000, "zfetch stream future per second"],
 | |
|     "zstride":    [7, 1000, "zfetch stream strides per second"],
 | |
|     "zissued":    [7, 1000, "zfetch prefetches issued per second"],
 | |
|     "zactive":    [7, 1000, "zfetch prefetches active per second"],
 | |
| }
 | |
| 
 | |
| # ARC structural breakdown from arc_summary
 | |
| structfields = {
 | |
|     "cmp":      ["compressed", "Compressed"],
 | |
|     "ovh":      ["overhead", "Overhead"],
 | |
|     "bon":      ["bonus", "Bonus"],
 | |
|     "dno":      ["dnode", "Dnode"],
 | |
|     "dbu":      ["dbuf", "Dbuf"],
 | |
|     "hdr":      ["hdr", "Header"],
 | |
|     "l2h":      ["l2_hdr", "L2 header"],
 | |
|     "abd":      ["abd_chunk_waste", "ABD chunk waste"],
 | |
| }
 | |
| structstats = {                             # size stats
 | |
|     "percent":  "size",                     # percentage of this value
 | |
|     "sz":       ["_size", "size"],
 | |
| }
 | |
| 
 | |
| # ARC types breakdown from arc_summary
 | |
| typefields = {
 | |
|     "data":     ["data", "ARC data"],
 | |
|     "meta":     ["metadata", "ARC metadata"],
 | |
| }
 | |
| typestats = {                               # size stats
 | |
|     "percent":  "cachessz",                 # percentage of this value
 | |
|     "tg":       ["_target", "target"],
 | |
|     "sz":       ["_size", "size"],
 | |
| }
 | |
| 
 | |
| # ARC states breakdown from arc_summary
 | |
| statefields = {
 | |
|     "ano":      ["anon", "Anonymous"],
 | |
|     "mfu":      ["mfu", "MFU"],
 | |
|     "mru":      ["mru", "MRU"],
 | |
|     "unc":      ["uncached", "Uncached"],
 | |
| }
 | |
| targetstats = {
 | |
|     "percent":  "cachessz",                 # percentage of this value
 | |
|     "fields":   ["mfu", "mru"],             # only applicable to these fields
 | |
|     "tg":       ["_target", "target"],
 | |
|     "dt":       ["_data_target", "data target"],
 | |
|     "mt":       ["_metadata_target", "metadata target"],
 | |
| }
 | |
| statestats = {                              # size stats
 | |
|     "percent":  "cachessz",                 # percentage of this value
 | |
|     "sz":       ["_size", "size"],
 | |
|     "da":       ["_data", "data size"],
 | |
|     "me":       ["_metadata", "metadata size"],
 | |
|     "ed":       ["_evictable_data", "evictable data size"],
 | |
|     "em":       ["_evictable_metadata", "evictable metadata size"],
 | |
| }
 | |
| ghoststats = {
 | |
|     "fields":   ["mfu", "mru"],             # only applicable to these fields
 | |
|     "gsz":      ["_ghost_size", "ghost size"],
 | |
|     "gd":       ["_ghost_data", "ghost data size"],
 | |
|     "gm":       ["_ghost_metadata", "ghost metadata size"],
 | |
| }
 | |
| 
 | |
| # fields and stats
 | |
| fieldstats = [
 | |
|     [structfields, structstats],
 | |
|     [typefields, typestats],
 | |
|     [statefields, targetstats, statestats, ghoststats],
 | |
| ]
 | |
| for fs in fieldstats:
 | |
|     fields, stats = fs[0], fs[1:]
 | |
|     for field, fieldval in fields.items():
 | |
|         for group in stats:
 | |
|             for stat, statval in group.items():
 | |
|                 if stat in ["fields", "percent"] or \
 | |
|                     ("fields" in group and field not in group["fields"]):
 | |
|                     continue
 | |
|                 colname = field + stat
 | |
|                 coldesc = fieldval[1] + " " + statval[1]
 | |
|                 cols[colname] = [len(colname), 1024, coldesc]
 | |
|                 if "percent" in group:
 | |
|                     cols[colname + "%"] = [len(colname) + 1, 100, \
 | |
|                         coldesc + " percentage"]
 | |
| 
 | |
| v = {}
 | |
| hdr = ["time", "read", "ddread", "ddh%", "dmread", "dmh%", "pread", "ph%",
 | |
|        "size", "c", "avail"]
 | |
| xhdr = ["time", "mfu", "mru", "mfug", "mrug", "unc", "eskip", "mtxmis",
 | |
|         "dread", "pread", "read"]
 | |
| zhdr = ["time", "ztotal", "zhits", "zahead", "zpast", "zmisses", "zmax",
 | |
|         "zfuture", "zstride", "zissued", "zactive"]
 | |
| sint = 1               # Default interval is 1 second
 | |
| count = 1              # Default count is 1
 | |
| hdr_intr = 20          # Print header every 20 lines of output
 | |
| opfile = None
 | |
| sep = "  "              # Default separator is 2 spaces
 | |
| l2exist = False
 | |
| cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
 | |
|        "[count]]\n")
 | |
| cur = {}
 | |
| d = {}
 | |
| out = None
 | |
| kstat = None
 | |
| pretty_print = True
 | |
| 
 | |
| 
 | |
| if sys.platform.startswith('freebsd'):
 | |
|     # Requires py-sysctl on FreeBSD
 | |
|     import sysctl
 | |
| 
 | |
|     def kstat_update():
 | |
|         global kstat
 | |
| 
 | |
|         k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
 | |
|              if ctl.type != sysctl.CTLTYPE_NODE]
 | |
|         k += [ctl for ctl in sysctl.filter('kstat.zfs.misc.zfetchstats')
 | |
|              if ctl.type != sysctl.CTLTYPE_NODE]
 | |
| 
 | |
|         if not k:
 | |
|             sys.exit(1)
 | |
| 
 | |
|         kstat = {}
 | |
| 
 | |
|         for s in k:
 | |
|             if not s:
 | |
|                 continue
 | |
| 
 | |
|             name, value = s.name, s.value
 | |
| 
 | |
|             if "arcstats" in name:
 | |
|                 # Trims 'kstat.zfs.misc.arcstats' from the name
 | |
|                 kstat[name[24:]] = int(value)
 | |
|             else:
 | |
|                 kstat["zfetch_" + name[27:]] = int(value)
 | |
| 
 | |
| elif sys.platform.startswith('linux'):
 | |
|     def kstat_update():
 | |
|         global kstat
 | |
| 
 | |
|         k1 = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
 | |
| 
 | |
|         k2 = ["zfetch_" + line.strip() for line in
 | |
|              open('/proc/spl/kstat/zfs/zfetchstats')]
 | |
| 
 | |
|         if k1 is None or k2 is None:
 | |
|             sys.exit(1)
 | |
| 
 | |
|         del k1[0:2]
 | |
|         del k2[0:2]
 | |
|         k = k1 + k2
 | |
|         kstat = {}
 | |
| 
 | |
|         for s in k:
 | |
|             if not s:
 | |
|                 continue
 | |
| 
 | |
|             name, unused, value = s.split()
 | |
|             kstat[name] = int(value)
 | |
| 
 | |
| 
 | |
| def detailed_usage():
 | |
|     sys.stderr.write("%s\n" % cmd)
 | |
|     sys.stderr.write("Field definitions are as follows:\n")
 | |
|     for key in cols:
 | |
|         sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
 | |
|     sys.stderr.write("\n")
 | |
| 
 | |
|     sys.exit(0)
 | |
| 
 | |
| 
 | |
| def usage():
 | |
|     sys.stderr.write("%s\n" % cmd)
 | |
|     sys.stderr.write("\t -h : Print this help message\n")
 | |
|     sys.stderr.write("\t -a : Print all possible stats\n")
 | |
|     sys.stderr.write("\t -v : List all possible field headers and definitions"
 | |
|                      "\n")
 | |
|     sys.stderr.write("\t -x : Print extended stats\n")
 | |
|     sys.stderr.write("\t -z : Print zfetch stats\n")
 | |
|     sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
 | |
|     sys.stderr.write("\t -o : Redirect output to the specified file\n")
 | |
|     sys.stderr.write("\t -s : Override default field separator with custom "
 | |
|                      "character or string\n")
 | |
|     sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
 | |
|     sys.stderr.write("\nExamples:\n")
 | |
|     sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
 | |
|     sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
 | |
|     sys.stderr.write("\tarcstat -v\n")
 | |
|     sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
 | |
|     sys.stderr.write("\n")
 | |
| 
 | |
|     sys.exit(1)
 | |
| 
 | |
| 
 | |
| def snap_stats():
 | |
|     global cur
 | |
|     global kstat
 | |
| 
 | |
|     prev = copy.deepcopy(cur)
 | |
|     kstat_update()
 | |
| 
 | |
|     cur = kstat
 | |
| 
 | |
|     # fill in additional values from arc_summary
 | |
|     cur["caches_size"] = caches_size = cur["anon_data"]+cur["anon_metadata"]+\
 | |
|         cur["mfu_data"]+cur["mfu_metadata"]+cur["mru_data"]+cur["mru_metadata"]+\
 | |
|         cur["uncached_data"]+cur["uncached_metadata"]
 | |
|     s = 4294967296
 | |
|     pd = cur["pd"]
 | |
|     pm = cur["pm"]
 | |
|     meta = cur["meta"]
 | |
|     v = (s-int(pd))*(s-int(meta))/s
 | |
|     cur["mfu_data_target"] = v / 65536 * caches_size / 65536
 | |
|     v = (s-int(pm))*int(meta)/s
 | |
|     cur["mfu_metadata_target"] = v / 65536 * caches_size / 65536
 | |
|     v = int(pd)*(s-int(meta))/s
 | |
|     cur["mru_data_target"] = v / 65536 * caches_size / 65536
 | |
|     v = int(pm)*int(meta)/s
 | |
|     cur["mru_metadata_target"] = v / 65536 * caches_size / 65536
 | |
| 
 | |
|     cur["data_target"] = cur["mfu_data_target"] + cur["mru_data_target"]
 | |
|     cur["metadata_target"] = cur["mfu_metadata_target"] + cur["mru_metadata_target"]
 | |
|     cur["mfu_target"] = cur["mfu_data_target"] + cur["mfu_metadata_target"]
 | |
|     cur["mru_target"] = cur["mru_data_target"] + cur["mru_metadata_target"]
 | |
| 
 | |
|     for key in cur:
 | |
|         if re.match(key, "class"):
 | |
|             continue
 | |
|         if key in prev:
 | |
|             d[key] = cur[key] - prev[key]
 | |
|         else:
 | |
|             d[key] = cur[key]
 | |
| 
 | |
| 
 | |
| def isint(num):
 | |
|     if isinstance(num, float):
 | |
|         return num.is_integer()
 | |
|     if isinstance(num, int):
 | |
|         return True
 | |
|     return False
 | |
| 
 | |
| 
 | |
| def prettynum(sz, scale, num=0):
 | |
|     suffix = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
 | |
|     index = 0
 | |
| 
 | |
|     # Special case for date field
 | |
|     if scale == -1:
 | |
|         return "%s" % num
 | |
| 
 | |
|     if scale != 100:
 | |
|         while abs(num) > scale and index < 5:
 | |
|             num = num / scale
 | |
|             index += 1
 | |
| 
 | |
|     width = sz - (0 if index == 0 else 1)
 | |
|     intlen = len("%.0f" % num)              # %.0f rounds to nearest int
 | |
|     if sint == 1 and isint(num) or width < intlen + 2:
 | |
|         decimal = 0
 | |
|     else:
 | |
|         decimal = 1
 | |
|     return "%*.*f%s" % (width, decimal, num, suffix[index])
 | |
| 
 | |
| 
 | |
| def print_values():
 | |
|     global hdr
 | |
|     global sep
 | |
|     global v
 | |
|     global pretty_print
 | |
| 
 | |
|     if pretty_print:
 | |
|         fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
 | |
|     else:
 | |
|         fmt = lambda col: str(v[col])
 | |
| 
 | |
|     sys.stdout.write(sep.join(fmt(col) for col in hdr))
 | |
|     sys.stdout.write("\n")
 | |
|     sys.stdout.flush()
 | |
| 
 | |
| 
 | |
| def print_header():
 | |
|     global hdr
 | |
|     global sep
 | |
|     global pretty_print
 | |
| 
 | |
|     if pretty_print:
 | |
|         fmt = lambda col: "%*s" % (cols[col][0], col)
 | |
|     else:
 | |
|         fmt = lambda col: col
 | |
| 
 | |
|     sys.stdout.write(sep.join(fmt(col) for col in hdr))
 | |
|     sys.stdout.write("\n")
 | |
| 
 | |
| 
 | |
| def get_terminal_lines():
 | |
|     try:
 | |
|         import fcntl
 | |
|         import termios
 | |
|         import struct
 | |
|         data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
 | |
|         sz = struct.unpack('hh', data)
 | |
|         return sz[0]
 | |
|     except Exception:
 | |
|         pass
 | |
| 
 | |
| 
 | |
| def update_hdr_intr():
 | |
|     global hdr_intr
 | |
| 
 | |
|     lines = get_terminal_lines()
 | |
|     if lines and lines > 3:
 | |
|         hdr_intr = lines - 3
 | |
| 
 | |
| 
 | |
| def resize_handler(signum, frame):
 | |
|     update_hdr_intr()
 | |
| 
 | |
| 
 | |
| def init():
 | |
|     global sint
 | |
|     global count
 | |
|     global hdr
 | |
|     global xhdr
 | |
|     global zhdr
 | |
|     global opfile
 | |
|     global sep
 | |
|     global out
 | |
|     global l2exist
 | |
|     global pretty_print
 | |
| 
 | |
|     desired_cols = None
 | |
|     aflag = False
 | |
|     xflag = False
 | |
|     hflag = False
 | |
|     vflag = False
 | |
|     zflag = False
 | |
|     i = 1
 | |
| 
 | |
|     try:
 | |
|         opts, args = getopt.getopt(
 | |
|             sys.argv[1:],
 | |
|             "axzo:hvs:f:p",
 | |
|             [
 | |
|                 "all",
 | |
|                 "extended",
 | |
|                 "zfetch",
 | |
|                 "outfile",
 | |
|                 "help",
 | |
|                 "verbose",
 | |
|                 "separator",
 | |
|                 "columns",
 | |
|                 "parsable"
 | |
|             ]
 | |
|         )
 | |
|     except getopt.error as msg:
 | |
|         sys.stderr.write("Error: %s\n" % str(msg))
 | |
|         usage()
 | |
|         opts = None
 | |
| 
 | |
|     for opt, arg in opts:
 | |
|         if opt in ('-a', '--all'):
 | |
|             aflag = True
 | |
|         if opt in ('-x', '--extended'):
 | |
|             xflag = True
 | |
|         if opt in ('-o', '--outfile'):
 | |
|             opfile = arg
 | |
|             i += 1
 | |
|         if opt in ('-h', '--help'):
 | |
|             hflag = True
 | |
|         if opt in ('-v', '--verbose'):
 | |
|             vflag = True
 | |
|         if opt in ('-s', '--separator'):
 | |
|             sep = arg
 | |
|             i += 1
 | |
|         if opt in ('-f', '--columns'):
 | |
|             desired_cols = arg
 | |
|             i += 1
 | |
|         if opt in ('-p', '--parsable'):
 | |
|             pretty_print = False
 | |
|         if opt in ('-z', '--zfetch'):
 | |
|             zflag = True
 | |
|         i += 1
 | |
| 
 | |
|     argv = sys.argv[i:]
 | |
|     sint = int(argv[0]) if argv else sint
 | |
|     count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)
 | |
| 
 | |
|     if hflag or (xflag and zflag) or ((zflag or xflag) and desired_cols):
 | |
|         usage()
 | |
| 
 | |
|     if vflag:
 | |
|         detailed_usage()
 | |
| 
 | |
|     if xflag:
 | |
|         hdr = xhdr
 | |
| 
 | |
|     if zflag:
 | |
|         hdr = zhdr
 | |
| 
 | |
|     update_hdr_intr()
 | |
| 
 | |
|     # check if L2ARC exists
 | |
|     snap_stats()
 | |
|     l2_size = cur.get("l2_size")
 | |
|     if l2_size:
 | |
|         l2exist = True
 | |
| 
 | |
|     if desired_cols:
 | |
|         hdr = desired_cols.split(",")
 | |
| 
 | |
|         invalid = []
 | |
|         incompat = []
 | |
|         for ele in hdr:
 | |
|             if ele not in cols:
 | |
|                 invalid.append(ele)
 | |
|             elif not l2exist and ele.startswith("l2"):
 | |
|                 sys.stdout.write("No L2ARC Here\n%s\n" % ele)
 | |
|                 incompat.append(ele)
 | |
| 
 | |
|         if len(invalid) > 0:
 | |
|             sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
 | |
|             usage()
 | |
| 
 | |
|         if len(incompat) > 0:
 | |
|             sys.stderr.write("Incompatible field specified! -- %s\n" %
 | |
|                              incompat)
 | |
|             usage()
 | |
| 
 | |
|     if aflag:
 | |
|         if l2exist:
 | |
|             hdr = cols.keys()
 | |
|         else:
 | |
|             hdr = [col for col in cols.keys() if not col.startswith("l2")]
 | |
| 
 | |
|     if opfile:
 | |
|         try:
 | |
|             out = open(opfile, "w")
 | |
|             sys.stdout = out
 | |
| 
 | |
|         except IOError:
 | |
|             sys.stderr.write("Cannot open %s for writing\n" % opfile)
 | |
|             sys.exit(1)
 | |
| 
 | |
| 
 | |
| def calculate():
 | |
|     global d
 | |
|     global v
 | |
|     global l2exist
 | |
| 
 | |
|     v = dict()
 | |
|     v["time"] = time.strftime("%H:%M:%S", time.localtime())
 | |
|     v["hits"] = d["hits"] / sint
 | |
|     v["iohs"] = d["iohits"] / sint
 | |
|     v["miss"] = d["misses"] / sint
 | |
|     v["read"] = v["hits"] + v["iohs"] + v["miss"]
 | |
|     v["hit%"] = 100 * v["hits"] / v["read"] if v["read"] > 0 else 0
 | |
|     v["ioh%"] = 100 * v["iohs"] / v["read"] if v["read"] > 0 else 0
 | |
|     v["miss%"] = 100 - v["hit%"] - v["ioh%"] if v["read"] > 0 else 0
 | |
| 
 | |
|     v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) / sint
 | |
|     v["dioh"] = (d["demand_data_iohits"] + d["demand_metadata_iohits"]) / sint
 | |
|     v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) / sint
 | |
| 
 | |
|     v["dread"] = v["dhit"] + v["dioh"] + v["dmis"]
 | |
|     v["dh%"] = 100 * v["dhit"] / v["dread"] if v["dread"] > 0 else 0
 | |
|     v["di%"] = 100 * v["dioh"] / v["dread"] if v["dread"] > 0 else 0
 | |
|     v["dm%"] = 100 - v["dh%"] - v["di%"] if v["dread"] > 0 else 0
 | |
| 
 | |
|     v["ddhit"] = d["demand_data_hits"] / sint
 | |
|     v["ddioh"] = d["demand_data_iohits"] / sint
 | |
|     v["ddmis"] = d["demand_data_misses"] / sint
 | |
| 
 | |
|     v["ddread"] = v["ddhit"] + v["ddioh"] + v["ddmis"]
 | |
|     v["ddh%"] = 100 * v["ddhit"] / v["ddread"] if v["ddread"] > 0 else 0
 | |
|     v["ddi%"] = 100 * v["ddioh"] / v["ddread"] if v["ddread"] > 0 else 0
 | |
|     v["ddm%"] = 100 - v["ddh%"] - v["ddi%"] if v["ddread"] > 0 else 0
 | |
| 
 | |
|     v["dmhit"] = d["demand_metadata_hits"] / sint
 | |
|     v["dmioh"] = d["demand_metadata_iohits"] / sint
 | |
|     v["dmmis"] = d["demand_metadata_misses"] / sint
 | |
| 
 | |
|     v["dmread"] = v["dmhit"] + v["dmioh"] + v["dmmis"]
 | |
|     v["dmh%"] = 100 * v["dmhit"] / v["dmread"] if v["dmread"] > 0 else 0
 | |
|     v["dmi%"] = 100 * v["dmioh"] / v["dmread"] if v["dmread"] > 0 else 0
 | |
|     v["dmm%"] = 100 - v["dmh%"] - v["dmi%"] if v["dmread"] > 0 else 0
 | |
| 
 | |
|     v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) / sint
 | |
|     v["pioh"] = (d["prefetch_data_iohits"] +
 | |
|                  d["prefetch_metadata_iohits"]) / sint
 | |
|     v["pmis"] = (d["prefetch_data_misses"] +
 | |
|                  d["prefetch_metadata_misses"]) / sint
 | |
| 
 | |
|     v["pread"] = v["phit"] + v["pioh"] + v["pmis"]
 | |
|     v["ph%"] = 100 * v["phit"] / v["pread"] if v["pread"] > 0 else 0
 | |
|     v["pi%"] = 100 * v["pioh"] / v["pread"] if v["pread"] > 0 else 0
 | |
|     v["pm%"] = 100 - v["ph%"] - v["pi%"] if v["pread"] > 0 else 0
 | |
| 
 | |
|     v["pdhit"] = d["prefetch_data_hits"] / sint
 | |
|     v["pdioh"] = d["prefetch_data_iohits"] / sint
 | |
|     v["pdmis"] = d["prefetch_data_misses"] / sint
 | |
| 
 | |
|     v["pdread"] = v["pdhit"] + v["pdioh"] + v["pdmis"]
 | |
|     v["pdh%"] = 100 * v["pdhit"] / v["pdread"] if v["pdread"] > 0 else 0
 | |
|     v["pdi%"] = 100 * v["pdioh"] / v["pdread"] if v["pdread"] > 0 else 0
 | |
|     v["pdm%"] = 100 - v["pdh%"] - v["pdi%"] if v["pdread"] > 0 else 0
 | |
| 
 | |
|     v["pmhit"] = d["prefetch_metadata_hits"] / sint
 | |
|     v["pmioh"] = d["prefetch_metadata_iohits"] / sint
 | |
|     v["pmmis"] = d["prefetch_metadata_misses"] / sint
 | |
| 
 | |
|     v["pmread"] = v["pmhit"] + v["pmioh"] + v["pmmis"]
 | |
|     v["pmh%"] = 100 * v["pmhit"] / v["pmread"] if v["pmread"] > 0 else 0
 | |
|     v["pmi%"] = 100 * v["pmioh"] / v["pmread"] if v["pmread"] > 0 else 0
 | |
|     v["pmm%"] = 100 - v["pmh%"] - v["pmi%"] if v["pmread"] > 0 else 0
 | |
| 
 | |
|     v["mhit"] = (d["prefetch_metadata_hits"] +
 | |
|                  d["demand_metadata_hits"]) / sint
 | |
|     v["mioh"] = (d["prefetch_metadata_iohits"] +
 | |
|                  d["demand_metadata_iohits"]) / sint
 | |
|     v["mmis"] = (d["prefetch_metadata_misses"] +
 | |
|                  d["demand_metadata_misses"]) / sint
 | |
| 
 | |
|     v["mread"] = v["mhit"] + v["mioh"] + v["mmis"]
 | |
|     v["mh%"] = 100 * v["mhit"] / v["mread"] if v["mread"] > 0 else 0
 | |
|     v["mi%"] = 100 * v["mioh"] / v["mread"] if v["mread"] > 0 else 0
 | |
|     v["mm%"] = 100 - v["mh%"] - v["mi%"] if v["mread"] > 0 else 0
 | |
| 
 | |
|     v["arcsz"] = cur["size"]
 | |
|     v["size"] = cur["size"]
 | |
|     v["c"] = cur["c"]
 | |
|     v["mfu"] = d["mfu_hits"] / sint
 | |
|     v["mru"] = d["mru_hits"] / sint
 | |
|     v["mrug"] = d["mru_ghost_hits"] / sint
 | |
|     v["mfug"] = d["mfu_ghost_hits"] / sint
 | |
|     v["unc"] = d["uncached_hits"] / sint
 | |
|     v["eskip"] = d["evict_skip"] / sint
 | |
|     v["el2skip"] = d["evict_l2_skip"] / sint
 | |
|     v["el2cach"] = d["evict_l2_cached"] / sint
 | |
|     v["el2el"] = d["evict_l2_eligible"] / sint
 | |
|     v["el2mfu"] = d["evict_l2_eligible_mfu"] / sint
 | |
|     v["el2mru"] = d["evict_l2_eligible_mru"] / sint
 | |
|     v["el2inel"] = d["evict_l2_ineligible"] / sint
 | |
|     v["mtxmis"] = d["mutex_miss"] / sint
 | |
|     v["ztotal"] = (d["zfetch_hits"] + d["zfetch_future"] + d["zfetch_stride"] +
 | |
|                    d["zfetch_past"] + d["zfetch_misses"]) / sint
 | |
|     v["zhits"] = d["zfetch_hits"] / sint
 | |
|     v["zahead"] = (d["zfetch_future"] + d["zfetch_stride"]) / sint
 | |
|     v["zpast"] = d["zfetch_past"] / sint
 | |
|     v["zmisses"] = d["zfetch_misses"] / sint
 | |
|     v["zmax"] = d["zfetch_max_streams"] / sint
 | |
|     v["zfuture"] = d["zfetch_future"] / sint
 | |
|     v["zstride"] = d["zfetch_stride"] / sint
 | |
|     v["zissued"] = d["zfetch_io_issued"] / sint
 | |
|     v["zactive"] = d["zfetch_io_active"] / sint
 | |
| 
 | |
|     # ARC structural breakdown, ARC types breakdown, ARC states breakdown
 | |
|     v["cachessz"] = cur["caches_size"]
 | |
|     for fs in fieldstats:
 | |
|         fields, stats = fs[0], fs[1:]
 | |
|         for field, fieldval in fields.items():
 | |
|             for group in stats:
 | |
|                 for stat, statval in group.items():
 | |
|                     if stat in ["fields", "percent"] or \
 | |
|                         ("fields" in group and field not in group["fields"]):
 | |
|                         continue
 | |
|                     colname = field + stat
 | |
|                     v[colname] = cur[fieldval[0] + statval[0]]
 | |
|                     if "percent" in group:
 | |
|                         v[colname + "%"] = 100 * v[colname] / \
 | |
|                             v[group["percent"]] if v[group["percent"]] > 0 else 0
 | |
| 
 | |
|     if l2exist:
 | |
|         v["l2hits"] = d["l2_hits"] / sint
 | |
|         v["l2miss"] = d["l2_misses"] / sint
 | |
|         v["l2read"] = v["l2hits"] + v["l2miss"]
 | |
|         v["l2hit%"] = 100 * v["l2hits"] / v["l2read"] if v["l2read"] > 0 else 0
 | |
| 
 | |
|         v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
 | |
|         v["l2asize"] = cur["l2_asize"]
 | |
|         v["l2size"] = cur["l2_size"]
 | |
|         v["l2bytes"] = d["l2_read_bytes"] / sint
 | |
|         v["l2wbytes"] = d["l2_write_bytes"] / sint
 | |
| 
 | |
|         v["l2pref"] = cur["l2_prefetch_asize"]
 | |
|         v["l2mfu"] = cur["l2_mfu_asize"]
 | |
|         v["l2mru"] = cur["l2_mru_asize"]
 | |
|         v["l2data"] = cur["l2_bufc_data_asize"]
 | |
|         v["l2meta"] = cur["l2_bufc_metadata_asize"]
 | |
|         v["l2pref%"] = 100 * v["l2pref"] / v["l2asize"]
 | |
|         v["l2mfu%"] = 100 * v["l2mfu"] / v["l2asize"]
 | |
|         v["l2mru%"] = 100 * v["l2mru"] / v["l2asize"]
 | |
|         v["l2data%"] = 100 * v["l2data"] / v["l2asize"]
 | |
|         v["l2meta%"] = 100 * v["l2meta"] / v["l2asize"]
 | |
| 
 | |
|     v["grow"] = 0 if cur["arc_no_grow"] else 1
 | |
|     v["need"] = cur["arc_need_free"]
 | |
|     v["free"] = cur["memory_free_bytes"]
 | |
|     v["avail"] = cur["memory_available_bytes"]
 | |
|     v["waste"] = cur["abd_chunk_waste_size"]
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     global sint
 | |
|     global count
 | |
|     global hdr_intr
 | |
| 
 | |
|     i = 0
 | |
|     count_flag = 0
 | |
| 
 | |
|     init()
 | |
|     if count > 0:
 | |
|         count_flag = 1
 | |
| 
 | |
|     signal(SIGINT, SIG_DFL)
 | |
|     signal(SIGWINCH, resize_handler)
 | |
|     while True:
 | |
|         if i == 0:
 | |
|             print_header()
 | |
| 
 | |
|         snap_stats()
 | |
|         calculate()
 | |
|         print_values()
 | |
| 
 | |
|         if count_flag == 1:
 | |
|             if count <= 1:
 | |
|                 break
 | |
|             count -= 1
 | |
| 
 | |
|         i = 0 if i >= hdr_intr else i + 1
 | |
|         time.sleep(sint)
 | |
| 
 | |
|     if out:
 | |
|         out.close()
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |