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