xref: /freebsd/sys/contrib/openzfs/cmd/zilstat.in (revision ce4dcb97ca433b2a2f03fbae957dae0ff16f6f51)
1c7046f76SMartin Matuska#!/usr/bin/env @PYTHON_SHEBANG@
2c7046f76SMartin Matuska#
3c7046f76SMartin Matuska# Print out statistics for all zil stats. This information is
4c7046f76SMartin Matuska# available through the zil kstat.
5c7046f76SMartin Matuska#
6c7046f76SMartin Matuska# CDDL HEADER START
7c7046f76SMartin Matuska#
8c7046f76SMartin Matuska# The contents of this file are subject to the terms of the
9c7046f76SMartin Matuska# Common Development and Distribution License, Version 1.0 only
10c7046f76SMartin Matuska# (the "License").  You may not use this file except in compliance
11c7046f76SMartin Matuska# with the License.
12c7046f76SMartin Matuska#
13c7046f76SMartin Matuska# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
14c7046f76SMartin Matuska# or https://opensource.org/licenses/CDDL-1.0.
15c7046f76SMartin Matuska# See the License for the specific language governing permissions
16c7046f76SMartin Matuska# and limitations under the License.
17c7046f76SMartin Matuska#
18c7046f76SMartin Matuska# When distributing Covered Code, include this CDDL HEADER in each
19c7046f76SMartin Matuska# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
20c7046f76SMartin Matuska# If applicable, add the following below this CDDL HEADER, with the
21c7046f76SMartin Matuska# fields enclosed by brackets "[]" replaced with your own identifying
22c7046f76SMartin Matuska# information: Portions Copyright [yyyy] [name of copyright owner]
23c7046f76SMartin Matuska#
24c7046f76SMartin Matuska# This script must remain compatible with Python 3.6+.
25c7046f76SMartin Matuska#
26c7046f76SMartin Matuska
27c7046f76SMartin Matuskaimport sys
28c7046f76SMartin Matuskaimport subprocess
29c7046f76SMartin Matuskaimport time
30c7046f76SMartin Matuskaimport copy
31c7046f76SMartin Matuskaimport os
32c7046f76SMartin Matuskaimport re
33c7046f76SMartin Matuskaimport signal
34c7046f76SMartin Matuskafrom collections import defaultdict
35c7046f76SMartin Matuskaimport argparse
36c7046f76SMartin Matuskafrom argparse import RawTextHelpFormatter
37c7046f76SMartin Matuska
38c7046f76SMartin Matuskacols = {
39c7046f76SMartin Matuska	# hdr:       [size,      scale,      kstat name]
40c7046f76SMartin Matuska	"time":      [8,         -1,         "time"],
41c7046f76SMartin Matuska	"pool":      [12,        -1,         "pool"],
42c7046f76SMartin Matuska	"ds":        [12,        -1,         "dataset_name"],
43c7046f76SMartin Matuska	"obj":       [12,        -1,         "objset"],
444e8d558cSMartin Matuska	"cc":        [5,         1000,       "zil_commit_count"],
454e8d558cSMartin Matuska	"cwc":       [5,         1000,       "zil_commit_writer_count"],
46*ce4dcb97SMartin Matuska	"cec":       [5,         1000,       "zil_commit_error_count"],
47*ce4dcb97SMartin Matuska	"csc":       [5,         1000,       "zil_commit_stall_count"],
48*ce4dcb97SMartin Matuska	"cSc":       [5,         1000,       "zil_commit_suspend_count"],
494e8d558cSMartin Matuska	"ic":        [5,         1000,       "zil_itx_count"],
504e8d558cSMartin Matuska	"iic":       [5,         1000,       "zil_itx_indirect_count"],
514e8d558cSMartin Matuska	"iib":       [5,         1024,       "zil_itx_indirect_bytes"],
524e8d558cSMartin Matuska	"icc":       [5,         1000,       "zil_itx_copied_count"],
534e8d558cSMartin Matuska	"icb":       [5,         1024,       "zil_itx_copied_bytes"],
544e8d558cSMartin Matuska	"inc":       [5,         1000,       "zil_itx_needcopy_count"],
554e8d558cSMartin Matuska	"inb":       [5,         1024,       "zil_itx_needcopy_bytes"],
564e8d558cSMartin Matuska	"idc":       [5,         1000,       "icc+inc"],
574e8d558cSMartin Matuska	"idb":       [5,         1024,       "icb+inb"],
584e8d558cSMartin Matuska	"iwc":       [5,         1000,       "iic+idc"],
594e8d558cSMartin Matuska	"iwb":       [5,         1024,       "iib+idb"],
604e8d558cSMartin Matuska	"imnc":      [6,         1000,       "zil_itx_metaslab_normal_count"],
614e8d558cSMartin Matuska	"imnb":      [6,         1024,       "zil_itx_metaslab_normal_bytes"],
624e8d558cSMartin Matuska	"imnw":      [6,         1024,       "zil_itx_metaslab_normal_write"],
634e8d558cSMartin Matuska	"imna":      [6,         1024,       "zil_itx_metaslab_normal_alloc"],
644e8d558cSMartin Matuska	"imsc":      [6,         1000,       "zil_itx_metaslab_slog_count"],
654e8d558cSMartin Matuska	"imsb":      [6,         1024,       "zil_itx_metaslab_slog_bytes"],
664e8d558cSMartin Matuska	"imsw":      [6,         1024,       "zil_itx_metaslab_slog_write"],
674e8d558cSMartin Matuska	"imsa":      [6,         1024,       "zil_itx_metaslab_slog_alloc"],
684e8d558cSMartin Matuska	"imc":       [5,         1000,       "imnc+imsc"],
694e8d558cSMartin Matuska	"imb":       [5,         1024,       "imnb+imsb"],
704e8d558cSMartin Matuska	"imw":       [5,         1024,       "imnw+imsw"],
714e8d558cSMartin Matuska	"ima":       [5,         1024,       "imna+imsa"],
724e8d558cSMartin Matuska	"se%":       [3,         100,        "imb/ima"],
734e8d558cSMartin Matuska	"sen%":      [4,         100,        "imnb/imna"],
744e8d558cSMartin Matuska	"ses%":      [4,         100,        "imsb/imsa"],
754e8d558cSMartin Matuska	"te%":       [3,         100,        "imb/imw"],
764e8d558cSMartin Matuska	"ten%":      [4,         100,        "imnb/imnw"],
774e8d558cSMartin Matuska	"tes%":      [4,         100,        "imsb/imsw"],
78c7046f76SMartin Matuska}
79c7046f76SMartin Matuska
804e8d558cSMartin Matuskahdr = ["time", "ds", "cc", "ic", "idc", "idb", "iic", "iib",
814e8d558cSMartin Matuska	"imnc", "imnw", "imsc", "imsw"]
82c7046f76SMartin Matuska
834e8d558cSMartin Matuskaghdr = ["time", "cc", "ic", "idc", "idb", "iic", "iib",
844e8d558cSMartin Matuska	"imnc", "imnw", "imsc", "imsw"]
85c7046f76SMartin Matuska
86c7046f76SMartin Matuskacmd = ("Usage: zilstat [-hgdv] [-i interval] [-p pool_name]")
87c7046f76SMartin Matuska
88c7046f76SMartin Matuskacurr = {}
89c7046f76SMartin Matuskadiff = {}
90c7046f76SMartin Matuskakstat = {}
91c7046f76SMartin Matuskads_pairs = {}
92c7046f76SMartin Matuskapool_name = None
93c7046f76SMartin Matuskadataset_name = None
94c7046f76SMartin Matuskainterval = 0
95c7046f76SMartin Matuskasep = "  "
96c7046f76SMartin MatuskagFlag = True
97c7046f76SMartin MatuskadsFlag = False
98c7046f76SMartin Matuska
99c7046f76SMartin Matuskadef prettynum(sz, scale, num=0):
100c7046f76SMartin Matuska	suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
101c7046f76SMartin Matuska	index = 0
102c7046f76SMartin Matuska	save = 0
103c7046f76SMartin Matuska
104c7046f76SMartin Matuska	if scale == -1:
105c7046f76SMartin Matuska		return "%*s" % (sz, num)
106c7046f76SMartin Matuska
107c7046f76SMartin Matuska	# Rounding error, return 0
108c7046f76SMartin Matuska	elif 0 < num < 1:
109c7046f76SMartin Matuska		num = 0
110c7046f76SMartin Matuska
111c7046f76SMartin Matuska	while num > scale and index < 5:
112c7046f76SMartin Matuska		save = num
113c7046f76SMartin Matuska		num = num / scale
114c7046f76SMartin Matuska		index += 1
115c7046f76SMartin Matuska
116c7046f76SMartin Matuska	if index == 0:
117c7046f76SMartin Matuska		return "%*d" % (sz, num)
118c7046f76SMartin Matuska
119c7046f76SMartin Matuska	if (save / scale) < 10:
120c7046f76SMartin Matuska		return "%*.1f%s" % (sz - 1, num, suffix[index])
121c7046f76SMartin Matuska	else:
122c7046f76SMartin Matuska		return "%*d%s" % (sz - 1, num, suffix[index])
123c7046f76SMartin Matuska
124c7046f76SMartin Matuskadef print_header():
125c7046f76SMartin Matuska	global hdr
126c7046f76SMartin Matuska	global sep
127c7046f76SMartin Matuska	for col in hdr:
128c7046f76SMartin Matuska		new_col = col
1294e8d558cSMartin Matuska		if interval > 0 and cols[col][1] > 100:
130c7046f76SMartin Matuska			new_col += "/s"
131c7046f76SMartin Matuska		sys.stdout.write("%*s%s" % (cols[col][0], new_col, sep))
132c7046f76SMartin Matuska	sys.stdout.write("\n")
133c7046f76SMartin Matuska
134c7046f76SMartin Matuskadef print_values(v):
135c7046f76SMartin Matuska	global hdr
136c7046f76SMartin Matuska	global sep
137c7046f76SMartin Matuska	for col in hdr:
138c7046f76SMartin Matuska		val = v[cols[col][2]]
1394e8d558cSMartin Matuska		if interval > 0 and cols[col][1] > 100:
140c7046f76SMartin Matuska			val = v[cols[col][2]] // interval
141c7046f76SMartin Matuska		sys.stdout.write("%s%s" % (
142c7046f76SMartin Matuska			prettynum(cols[col][0], cols[col][1], val), sep))
143c7046f76SMartin Matuska	sys.stdout.write("\n")
144c7046f76SMartin Matuska
145c7046f76SMartin Matuskadef print_dict(d):
146c7046f76SMartin Matuska	for pool in d:
147c7046f76SMartin Matuska		for objset in d[pool]:
148c7046f76SMartin Matuska			print_values(d[pool][objset])
149c7046f76SMartin Matuska
150c7046f76SMartin Matuskadef detailed_usage():
151c7046f76SMartin Matuska	sys.stderr.write("%s\n" % cmd)
152c7046f76SMartin Matuska	sys.stderr.write("Field definitions are as follows:\n")
153c7046f76SMartin Matuska	for key in cols:
154c7046f76SMartin Matuska		sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
155c7046f76SMartin Matuska	sys.stderr.write("\n")
156c7046f76SMartin Matuska	sys.exit(0)
157c7046f76SMartin Matuska
158c7046f76SMartin Matuskadef init():
159c7046f76SMartin Matuska	global pool_name
160c7046f76SMartin Matuska	global dataset_name
161c7046f76SMartin Matuska	global interval
162c7046f76SMartin Matuska	global hdr
163c7046f76SMartin Matuska	global curr
164c7046f76SMartin Matuska	global gFlag
165c7046f76SMartin Matuska	global sep
166c7046f76SMartin Matuska
167c7046f76SMartin Matuska	curr = dict()
168c7046f76SMartin Matuska
169c7046f76SMartin Matuska	parser = argparse.ArgumentParser(description='Program to print zilstats',
170c7046f76SMartin Matuska                                	 add_help=True,
171c7046f76SMartin Matuska					 formatter_class=RawTextHelpFormatter,
172c7046f76SMartin Matuska					 epilog="\nUsage Examples\n"\
173c7046f76SMartin Matuska				 		"Note: Global zilstats is shown by default,"\
174c7046f76SMartin Matuska						" if none of a|p|d option is not provided\n"\
175c7046f76SMartin Matuska				 		"\tzilstat -a\n"\
176c7046f76SMartin Matuska						'\tzilstat -v\n'\
177c7046f76SMartin Matuska						'\tzilstat -p tank\n'\
178c7046f76SMartin Matuska						'\tzilstat -d tank/d1,tank/d2,tank/zv1\n'\
179c7046f76SMartin Matuska						'\tzilstat -i 1\n'\
180c7046f76SMartin Matuska						'\tzilstat -s \"***\"\n'\
181c7046f76SMartin Matuska						'\tzilstat -f zcwc,zimnb,zimsb\n')
182c7046f76SMartin Matuska
183c7046f76SMartin Matuska	parser.add_argument(
184c7046f76SMartin Matuska		"-v", "--verbose",
185c7046f76SMartin Matuska		action="store_true",
186c7046f76SMartin Matuska		help="List field headers and definitions"
187c7046f76SMartin Matuska	)
188c7046f76SMartin Matuska
189c7046f76SMartin Matuska	pool_grp = parser.add_mutually_exclusive_group()
190c7046f76SMartin Matuska
191c7046f76SMartin Matuska	pool_grp.add_argument(
192c7046f76SMartin Matuska		"-a", "--all",
193c7046f76SMartin Matuska		action="store_true",
194c7046f76SMartin Matuska		dest="all",
195c7046f76SMartin Matuska		help="Print all dataset stats"
196c7046f76SMartin Matuska	)
197c7046f76SMartin Matuska
198c7046f76SMartin Matuska	pool_grp.add_argument(
199c7046f76SMartin Matuska		"-p", "--pool",
200c7046f76SMartin Matuska		type=str,
201c7046f76SMartin Matuska		help="Print stats for all datasets of a speicfied pool"
202c7046f76SMartin Matuska	)
203c7046f76SMartin Matuska
204c7046f76SMartin Matuska	pool_grp.add_argument(
205c7046f76SMartin Matuska		"-d", "--dataset",
206c7046f76SMartin Matuska		type=str,
207c7046f76SMartin Matuska		help="Print given dataset(s) (Comma separated)"
208c7046f76SMartin Matuska	)
209c7046f76SMartin Matuska
210c7046f76SMartin Matuska	parser.add_argument(
211c7046f76SMartin Matuska		"-f", "--columns",
212c7046f76SMartin Matuska		type=str,
213c7046f76SMartin Matuska		help="Specify specific fields to print (see -v)"
214c7046f76SMartin Matuska	)
215c7046f76SMartin Matuska
216c7046f76SMartin Matuska	parser.add_argument(
217c7046f76SMartin Matuska		"-s", "--separator",
218c7046f76SMartin Matuska		type=str,
219c7046f76SMartin Matuska		help="Override default field separator with custom "
220c7046f76SMartin Matuska			 "character or string"
221c7046f76SMartin Matuska	)
222c7046f76SMartin Matuska
223c7046f76SMartin Matuska	parser.add_argument(
224c7046f76SMartin Matuska		"-i", "--interval",
225c7046f76SMartin Matuska		type=int,
226c7046f76SMartin Matuska		dest="interval",
227c7046f76SMartin Matuska		help="Print stats between specified interval"
228c7046f76SMartin Matuska			 " (in seconds)"
229c7046f76SMartin Matuska	)
230c7046f76SMartin Matuska
231c7046f76SMartin Matuska	parsed_args = parser.parse_args()
232c7046f76SMartin Matuska
233c7046f76SMartin Matuska	if parsed_args.verbose:
234c7046f76SMartin Matuska		detailed_usage()
235c7046f76SMartin Matuska
236c7046f76SMartin Matuska	if parsed_args.all:
237c7046f76SMartin Matuska		gFlag = False
238c7046f76SMartin Matuska
239c7046f76SMartin Matuska	if parsed_args.interval:
240c7046f76SMartin Matuska		interval = parsed_args.interval
241c7046f76SMartin Matuska
242c7046f76SMartin Matuska	if parsed_args.pool:
243c7046f76SMartin Matuska		pool_name = parsed_args.pool
244c7046f76SMartin Matuska		gFlag = False
245c7046f76SMartin Matuska
246c7046f76SMartin Matuska	if parsed_args.dataset:
247c7046f76SMartin Matuska		dataset_name = parsed_args.dataset
248c7046f76SMartin Matuska		gFlag = False
249c7046f76SMartin Matuska
250c7046f76SMartin Matuska	if parsed_args.separator:
251c7046f76SMartin Matuska		sep = parsed_args.separator
252c7046f76SMartin Matuska
253c7046f76SMartin Matuska	if gFlag:
254c7046f76SMartin Matuska		hdr = ghdr
255c7046f76SMartin Matuska
256c7046f76SMartin Matuska	if parsed_args.columns:
257c7046f76SMartin Matuska		hdr = parsed_args.columns.split(",")
258c7046f76SMartin Matuska
259c7046f76SMartin Matuska		invalid = []
260c7046f76SMartin Matuska		for ele in hdr:
2614e8d558cSMartin Matuska			if ele not in cols:
262c7046f76SMartin Matuska				invalid.append(ele)
263c7046f76SMartin Matuska
264c7046f76SMartin Matuska		if len(invalid) > 0:
265c7046f76SMartin Matuska			sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
266c7046f76SMartin Matuska			sys.exit(1)
267c7046f76SMartin Matuska
268c7046f76SMartin Matuska	if pool_name and dataset_name:
269c7046f76SMartin Matuska		print ("Error: Can not filter both dataset and pool")
270c7046f76SMartin Matuska		sys.exit(1)
271c7046f76SMartin Matuska
272c7046f76SMartin Matuskadef FileCheck(fname):
273c7046f76SMartin Matuska	try:
274c7046f76SMartin Matuska		return (open(fname))
275c7046f76SMartin Matuska	except IOError:
276c7046f76SMartin Matuska		print ("Unable to open zilstat proc file: " + fname)
277c7046f76SMartin Matuska		sys.exit(1)
278c7046f76SMartin Matuska
279c7046f76SMartin Matuskaif sys.platform.startswith('freebsd'):
280c7046f76SMartin Matuska	# Requires py-sysctl on FreeBSD
281c7046f76SMartin Matuska	import sysctl
282c7046f76SMartin Matuska
283c7046f76SMartin Matuska	def kstat_update(pool = None, objid = None):
284c7046f76SMartin Matuska		global kstat
285c7046f76SMartin Matuska		kstat = {}
286c7046f76SMartin Matuska		if not pool:
287c7046f76SMartin Matuska			file = "kstat.zfs.misc.zil"
288c7046f76SMartin Matuska			k = [ctl for ctl in sysctl.filter(file) \
289c7046f76SMartin Matuska				if ctl.type != sysctl.CTLTYPE_NODE]
290c7046f76SMartin Matuska			kstat_process_str(k, file, "GLOBAL", len(file + "."))
291c7046f76SMartin Matuska		elif objid:
292c7046f76SMartin Matuska			file = "kstat.zfs." + pool + ".dataset.objset-" + objid
293c7046f76SMartin Matuska			k = [ctl for ctl in sysctl.filter(file) if ctl.type \
294c7046f76SMartin Matuska				!= sysctl.CTLTYPE_NODE]
295c7046f76SMartin Matuska			kstat_process_str(k, file, objid, len(file + "."))
296c7046f76SMartin Matuska		else:
297c7046f76SMartin Matuska			file = "kstat.zfs." + pool + ".dataset"
298c7046f76SMartin Matuska			zil_start = len(file + ".")
299c7046f76SMartin Matuska			obj_start = len("kstat.zfs." + pool + ".")
300c7046f76SMartin Matuska			k = [ctl for ctl in sysctl.filter(file)
301c7046f76SMartin Matuska				if ctl.type != sysctl.CTLTYPE_NODE]
302c7046f76SMartin Matuska			for s in k:
303c7046f76SMartin Matuska				if not s or (s.name.find("zil") == -1 and \
304c7046f76SMartin Matuska					s.name.find("dataset_name") == -1):
305c7046f76SMartin Matuska					continue
306c7046f76SMartin Matuska				name, value = s.name, s.value
307c7046f76SMartin Matuska				objid = re.findall(r'0x[0-9A-F]+', \
308c7046f76SMartin Matuska					name[obj_start:], re.I)[0]
309c7046f76SMartin Matuska				if objid not in kstat:
310c7046f76SMartin Matuska					kstat[objid] = dict()
311c7046f76SMartin Matuska				zil_start = len(file + ".objset-" + \
312c7046f76SMartin Matuska					objid + ".")
313c7046f76SMartin Matuska				kstat[objid][name[zil_start:]] = value \
314c7046f76SMartin Matuska					if (name.find("dataset_name")) \
315c7046f76SMartin Matuska					else int(value)
316c7046f76SMartin Matuska
317c7046f76SMartin Matuska	def kstat_process_str(k, file, objset = "GLOBAL", zil_start = 0):
318c7046f76SMartin Matuska			global kstat
319c7046f76SMartin Matuska			if not k:
320c7046f76SMartin Matuska				print("Unable to process kstat for: " + file)
321c7046f76SMartin Matuska				sys.exit(1)
322c7046f76SMartin Matuska			kstat[objset] = dict()
323c7046f76SMartin Matuska			for s in k:
324c7046f76SMartin Matuska				if not s or (s.name.find("zil") == -1 and \
325c7046f76SMartin Matuska				    s.name.find("dataset_name") == -1):
326c7046f76SMartin Matuska					continue
327c7046f76SMartin Matuska				name, value = s.name, s.value
328c7046f76SMartin Matuska				kstat[objset][name[zil_start:]] = value \
329c7046f76SMartin Matuska				    if (name.find("dataset_name")) else int(value)
330c7046f76SMartin Matuska
331c7046f76SMartin Matuskaelif sys.platform.startswith('linux'):
332c7046f76SMartin Matuska	def kstat_update(pool = None, objid = None):
333c7046f76SMartin Matuska		global kstat
334c7046f76SMartin Matuska		kstat = {}
335c7046f76SMartin Matuska		if not pool:
336c7046f76SMartin Matuska			k = [line.strip() for line in \
337c7046f76SMartin Matuska				FileCheck("/proc/spl/kstat/zfs/zil")]
338c7046f76SMartin Matuska			kstat_process_str(k, "/proc/spl/kstat/zfs/zil")
339c7046f76SMartin Matuska		elif objid:
340c7046f76SMartin Matuska			file = "/proc/spl/kstat/zfs/" + pool + "/objset-" + objid
341c7046f76SMartin Matuska			k = [line.strip() for line in FileCheck(file)]
342c7046f76SMartin Matuska			kstat_process_str(k, file, objid)
343c7046f76SMartin Matuska		else:
344c7046f76SMartin Matuska			if not os.path.exists(f"/proc/spl/kstat/zfs/{pool}"):
345c7046f76SMartin Matuska				print("Pool \"" + pool + "\" does not exist, Exitting")
346c7046f76SMartin Matuska				sys.exit(1)
347c7046f76SMartin Matuska			objsets = os.listdir(f'/proc/spl/kstat/zfs/{pool}')
348c7046f76SMartin Matuska			for objid in objsets:
349c7046f76SMartin Matuska				if objid.find("objset-") == -1:
350c7046f76SMartin Matuska					continue
351c7046f76SMartin Matuska				file = "/proc/spl/kstat/zfs/" + pool + "/" + objid
352c7046f76SMartin Matuska				k = [line.strip() for line in FileCheck(file)]
353c7046f76SMartin Matuska				kstat_process_str(k, file, objid.replace("objset-", ""))
354c7046f76SMartin Matuska
355c7046f76SMartin Matuska	def kstat_process_str(k, file, objset = "GLOBAL", zil_start = 0):
356c7046f76SMartin Matuska			global kstat
357c7046f76SMartin Matuska			if not k:
358c7046f76SMartin Matuska				print("Unable to process kstat for: " + file)
359c7046f76SMartin Matuska				sys.exit(1)
360c7046f76SMartin Matuska
361c7046f76SMartin Matuska			kstat[objset] = dict()
362c7046f76SMartin Matuska			for s in k:
363c7046f76SMartin Matuska				if not s or (s.find("zil") == -1 and \
364c7046f76SMartin Matuska				    s.find("dataset_name") == -1):
365c7046f76SMartin Matuska					continue
366c7046f76SMartin Matuska				name, unused, value = s.split()
367c7046f76SMartin Matuska				kstat[objset][name] = value \
368c7046f76SMartin Matuska				    if (name == "dataset_name") else int(value)
369c7046f76SMartin Matuska
370c7046f76SMartin Matuskadef zil_process_kstat():
371c7046f76SMartin Matuska	global curr, pool_name, dataset_name, dsFlag, ds_pairs
372c7046f76SMartin Matuska	curr.clear()
373c7046f76SMartin Matuska	if gFlag == True:
374c7046f76SMartin Matuska		kstat_update()
375c7046f76SMartin Matuska		zil_build_dict()
376c7046f76SMartin Matuska	else:
377c7046f76SMartin Matuska		if pool_name:
378c7046f76SMartin Matuska			kstat_update(pool_name)
379c7046f76SMartin Matuska			zil_build_dict(pool_name)
380c7046f76SMartin Matuska		elif dataset_name:
381c7046f76SMartin Matuska			if dsFlag == False:
382c7046f76SMartin Matuska				dsFlag = True
383c7046f76SMartin Matuska				datasets = dataset_name.split(',')
384c7046f76SMartin Matuska				ds_pairs = defaultdict(list)
385c7046f76SMartin Matuska				for ds in datasets:
386c7046f76SMartin Matuska					try:
387c7046f76SMartin Matuska						objid = subprocess.check_output(['zfs',
388c7046f76SMartin Matuska						    'list', '-Hpo', 'objsetid', ds], \
389c7046f76SMartin Matuska						    stderr=subprocess.DEVNULL) \
390c7046f76SMartin Matuska						    .decode('utf-8').strip()
391c7046f76SMartin Matuska					except subprocess.CalledProcessError as e:
392c7046f76SMartin Matuska						print("Command: \"zfs list -Hpo objset "\
393c7046f76SMartin Matuska						+ str(ds) + "\" failed with error code:"\
394c7046f76SMartin Matuska						+ str(e.returncode))
395c7046f76SMartin Matuska						print("Please make sure that dataset \""\
396c7046f76SMartin Matuska						+ str(ds) + "\" exists")
397c7046f76SMartin Matuska						sys.exit(1)
398c7046f76SMartin Matuska					if not objid:
399c7046f76SMartin Matuska						continue
400c7046f76SMartin Matuska					ds_pairs[ds.split('/')[0]]. \
401c7046f76SMartin Matuska						append(hex(int(objid)))
402c7046f76SMartin Matuska			for pool, objids in ds_pairs.items():
403c7046f76SMartin Matuska				for objid in objids:
404c7046f76SMartin Matuska					kstat_update(pool, objid)
405c7046f76SMartin Matuska					zil_build_dict(pool)
406c7046f76SMartin Matuska		else:
407c7046f76SMartin Matuska			try:
408c7046f76SMartin Matuska				pools = subprocess.check_output(['zpool', 'list', '-Hpo',\
409c7046f76SMartin Matuska				    'name']).decode('utf-8').split()
410c7046f76SMartin Matuska			except subprocess.CalledProcessError as e:
411c7046f76SMartin Matuska				print("Command: \"zpool list -Hpo name\" failed with error"\
412c7046f76SMartin Matuska				    "code: " + str(e.returncode))
413c7046f76SMartin Matuska				sys.exit(1)
414c7046f76SMartin Matuska			for pool in pools:
415c7046f76SMartin Matuska				kstat_update(pool)
416c7046f76SMartin Matuska				zil_build_dict(pool)
417c7046f76SMartin Matuska
418c7046f76SMartin Matuskadef calculate_diff():
419c7046f76SMartin Matuska	global curr, diff
420c7046f76SMartin Matuska	prev = copy.deepcopy(curr)
421c7046f76SMartin Matuska	zil_process_kstat()
422c7046f76SMartin Matuska	diff = copy.deepcopy(curr)
423c7046f76SMartin Matuska	for pool in curr:
424c7046f76SMartin Matuska		for objset in curr[pool]:
4254e8d558cSMartin Matuska			for key in curr[pool][objset]:
4264e8d558cSMartin Matuska				if not isinstance(diff[pool][objset][key], int):
4274e8d558cSMartin Matuska					continue
428c7046f76SMartin Matuska				# If prev is NULL, this is the
429c7046f76SMartin Matuska				# first time we are here
430c7046f76SMartin Matuska				if not prev:
431c7046f76SMartin Matuska					diff[pool][objset][key] = 0
432c7046f76SMartin Matuska				else:
433c7046f76SMartin Matuska					diff[pool][objset][key] \
434c7046f76SMartin Matuska						= curr[pool][objset][key] \
435c7046f76SMartin Matuska						- prev[pool][objset][key]
436c7046f76SMartin Matuska
437c7046f76SMartin Matuskadef zil_build_dict(pool = "GLOBAL"):
438c7046f76SMartin Matuska	global kstat
439c7046f76SMartin Matuska	for objset in kstat:
440c7046f76SMartin Matuska		for key in kstat[objset]:
441c7046f76SMartin Matuska			val = kstat[objset][key]
442c7046f76SMartin Matuska			if pool not in curr:
443c7046f76SMartin Matuska				curr[pool] = dict()
444c7046f76SMartin Matuska			if objset not in curr[pool]:
445c7046f76SMartin Matuska				curr[pool][objset] = dict()
446c7046f76SMartin Matuska			curr[pool][objset][key] = val
4474e8d558cSMartin Matuska
4484e8d558cSMartin Matuskadef zil_extend_dict():
4494e8d558cSMartin Matuska	global diff
4504e8d558cSMartin Matuska	for pool in diff:
4514e8d558cSMartin Matuska		for objset in diff[pool]:
4524e8d558cSMartin Matuska			diff[pool][objset]["pool"] = pool
4534e8d558cSMartin Matuska			diff[pool][objset]["objset"] = objset
4544e8d558cSMartin Matuska			diff[pool][objset]["time"] = time.strftime("%H:%M:%S", \
455c7046f76SMartin Matuska				time.localtime())
4564e8d558cSMartin Matuska			diff[pool][objset]["icc+inc"] = \
4574e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_copied_count"] + \
4584e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_needcopy_count"]
4594e8d558cSMartin Matuska			diff[pool][objset]["icb+inb"] = \
4604e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_copied_bytes"] + \
4614e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_needcopy_bytes"]
4624e8d558cSMartin Matuska			diff[pool][objset]["iic+idc"] = \
4634e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_indirect_count"] + \
4644e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_copied_count"] + \
4654e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_needcopy_count"]
4664e8d558cSMartin Matuska			diff[pool][objset]["iib+idb"] = \
4674e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_indirect_bytes"] + \
4684e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_copied_bytes"] + \
4694e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_needcopy_bytes"]
4704e8d558cSMartin Matuska			diff[pool][objset]["imnc+imsc"] = \
4714e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_normal_count"] + \
4724e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_slog_count"]
4734e8d558cSMartin Matuska			diff[pool][objset]["imnb+imsb"] = \
4744e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_normal_bytes"] + \
4754e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_slog_bytes"]
4764e8d558cSMartin Matuska			diff[pool][objset]["imnw+imsw"] = \
4774e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_normal_write"] + \
4784e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_slog_write"]
4794e8d558cSMartin Matuska			diff[pool][objset]["imna+imsa"] = \
4804e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_normal_alloc"] + \
4814e8d558cSMartin Matuska				diff[pool][objset]["zil_itx_metaslab_slog_alloc"]
4824e8d558cSMartin Matuska			if diff[pool][objset]["imna+imsa"] > 0:
4834e8d558cSMartin Matuska				diff[pool][objset]["imb/ima"] = 100 * \
4844e8d558cSMartin Matuska					diff[pool][objset]["imnb+imsb"] // \
4854e8d558cSMartin Matuska					diff[pool][objset]["imna+imsa"]
4864e8d558cSMartin Matuska			else:
4874e8d558cSMartin Matuska				diff[pool][objset]["imb/ima"] = 100
4884e8d558cSMartin Matuska			if diff[pool][objset]["zil_itx_metaslab_normal_alloc"] > 0:
4894e8d558cSMartin Matuska				diff[pool][objset]["imnb/imna"] = 100 * \
4904e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_normal_bytes"] // \
4914e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_normal_alloc"]
4924e8d558cSMartin Matuska			else:
4934e8d558cSMartin Matuska				diff[pool][objset]["imnb/imna"] = 100
4944e8d558cSMartin Matuska			if diff[pool][objset]["zil_itx_metaslab_slog_alloc"] > 0:
4954e8d558cSMartin Matuska				diff[pool][objset]["imsb/imsa"] = 100 * \
4964e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_slog_bytes"] // \
4974e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_slog_alloc"]
4984e8d558cSMartin Matuska			else:
4994e8d558cSMartin Matuska				diff[pool][objset]["imsb/imsa"] = 100
5004e8d558cSMartin Matuska			if diff[pool][objset]["imnw+imsw"] > 0:
5014e8d558cSMartin Matuska				diff[pool][objset]["imb/imw"] = 100 * \
5024e8d558cSMartin Matuska					diff[pool][objset]["imnb+imsb"] // \
5034e8d558cSMartin Matuska					diff[pool][objset]["imnw+imsw"]
5044e8d558cSMartin Matuska			else:
5054e8d558cSMartin Matuska				diff[pool][objset]["imb/imw"] = 100
5064e8d558cSMartin Matuska			if diff[pool][objset]["zil_itx_metaslab_normal_alloc"] > 0:
5074e8d558cSMartin Matuska				diff[pool][objset]["imnb/imnw"] = 100 * \
5084e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_normal_bytes"] // \
5094e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_normal_write"]
5104e8d558cSMartin Matuska			else:
5114e8d558cSMartin Matuska				diff[pool][objset]["imnb/imnw"] = 100
5124e8d558cSMartin Matuska			if diff[pool][objset]["zil_itx_metaslab_slog_alloc"] > 0:
5134e8d558cSMartin Matuska				diff[pool][objset]["imsb/imsw"] = 100 * \
5144e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_slog_bytes"] // \
5154e8d558cSMartin Matuska					diff[pool][objset]["zil_itx_metaslab_slog_write"]
5164e8d558cSMartin Matuska			else:
5174e8d558cSMartin Matuska				diff[pool][objset]["imsb/imsw"] = 100
518c7046f76SMartin Matuska
519c7046f76SMartin Matuskadef sign_handler_epipe(sig, frame):
520c7046f76SMartin Matuska	print("Caught EPIPE signal: " + str(frame))
521c7046f76SMartin Matuska	print("Exitting...")
522c7046f76SMartin Matuska	sys.exit(0)
523c7046f76SMartin Matuska
524c7046f76SMartin Matuskadef main():
525c7046f76SMartin Matuska	global interval
5264e8d558cSMartin Matuska	global curr, diff
527c7046f76SMartin Matuska	hprint = False
528c7046f76SMartin Matuska	init()
529c7046f76SMartin Matuska	signal.signal(signal.SIGINT, signal.SIG_DFL)
530c7046f76SMartin Matuska	signal.signal(signal.SIGPIPE, sign_handler_epipe)
531c7046f76SMartin Matuska
532c7046f76SMartin Matuska	zil_process_kstat()
533c7046f76SMartin Matuska	if not curr:
534c7046f76SMartin Matuska		print ("Error: No stats to show")
535c7046f76SMartin Matuska		sys.exit(0)
536c7046f76SMartin Matuska	print_header()
5374e8d558cSMartin Matuska	if interval > 0:
5384e8d558cSMartin Matuska		time.sleep(interval)
5394e8d558cSMartin Matuska		while True:
5404e8d558cSMartin Matuska			calculate_diff()
5414e8d558cSMartin Matuska			if not diff:
5424e8d558cSMartin Matuska				print ("Error: No stats to show")
5434e8d558cSMartin Matuska				sys.exit(0)
5444e8d558cSMartin Matuska			zil_extend_dict()
5454e8d558cSMartin Matuska			print_dict(diff)
5464e8d558cSMartin Matuska			time.sleep(interval)
5474e8d558cSMartin Matuska	else:
5484e8d558cSMartin Matuska		diff = curr
5494e8d558cSMartin Matuska		zil_extend_dict()
5504e8d558cSMartin Matuska		print_dict(diff)
551c7046f76SMartin Matuska
552c7046f76SMartin Matuskaif __name__ == '__main__':
553c7046f76SMartin Matuska	main()
554c7046f76SMartin Matuska
555