xref: /freebsd/sys/contrib/openzfs/cmd/zpool_influxdb/zpool_influxdb.c (revision 0a97523d467443fa54171ef7a399b1c9043eac75)
17877fdebSMatt Macy /*
27877fdebSMatt Macy  * Gather top-level ZFS pool and resilver/scan statistics and print using
37877fdebSMatt Macy  * influxdb line protocol
47877fdebSMatt Macy  * usage: [options] [pool_name]
57877fdebSMatt Macy  * where options are:
67877fdebSMatt Macy  *   --execd, -e           run in telegraf execd input plugin mode, [CR] on
77877fdebSMatt Macy  *                         stdin causes a sample to be printed and wait for
87877fdebSMatt Macy  *                         the next [CR]
97877fdebSMatt Macy  *   --no-histograms, -n   don't print histogram data (reduces cardinality
107877fdebSMatt Macy  *                         if you don't care about histograms)
117877fdebSMatt Macy  *   --sum-histogram-buckets, -s sum histogram bucket values
127877fdebSMatt Macy  *
137877fdebSMatt Macy  * To integrate into telegraf use one of:
147877fdebSMatt Macy  * 1. the `inputs.execd` plugin with the `--execd` option
157877fdebSMatt Macy  * 2. the `inputs.exec` plugin to simply run with no options
167877fdebSMatt Macy  *
177877fdebSMatt Macy  * NOTE: libzfs is an unstable interface. YMMV.
187877fdebSMatt Macy  *
197877fdebSMatt Macy  * The design goals of this software include:
207877fdebSMatt Macy  * + be as lightweight as possible
217877fdebSMatt Macy  * + reduce the number of external dependencies as far as possible, hence
227877fdebSMatt Macy  *   there is no dependency on a client library for managing the metric
237877fdebSMatt Macy  *   collection -- info is printed, KISS
247877fdebSMatt Macy  * + broken pools or kernel bugs can cause this process to hang in an
257877fdebSMatt Macy  *   unkillable state. For this reason, it is best to keep the damage limited
267877fdebSMatt Macy  *   to a small process like zpool_influxdb rather than a larger collector.
277877fdebSMatt Macy  *
287877fdebSMatt Macy  * Copyright 2018-2020 Richard Elling
297877fdebSMatt Macy  *
307877fdebSMatt Macy  * This software is dual-licensed MIT and CDDL.
317877fdebSMatt Macy  *
327877fdebSMatt Macy  * The MIT License (MIT)
337877fdebSMatt Macy  *
347877fdebSMatt Macy  * Permission is hereby granted, free of charge, to any person obtaining a copy
357877fdebSMatt Macy  * of this software and associated documentation files (the "Software"), to deal
367877fdebSMatt Macy  * in the Software without restriction, including without limitation the rights
377877fdebSMatt Macy  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
387877fdebSMatt Macy  * copies of the Software, and to permit persons to whom the Software is
397877fdebSMatt Macy  * furnished to do so, subject to the following conditions:
407877fdebSMatt Macy  *
417877fdebSMatt Macy  * The above copyright notice and this permission notice shall be included in
427877fdebSMatt Macy  * all copies or substantial portions of the Software.
437877fdebSMatt Macy  *
447877fdebSMatt Macy  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
457877fdebSMatt Macy  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
467877fdebSMatt Macy  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
477877fdebSMatt Macy  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
487877fdebSMatt Macy  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
497877fdebSMatt Macy  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
507877fdebSMatt Macy  * SOFTWARE.
517877fdebSMatt Macy  *
527877fdebSMatt Macy  * CDDL HEADER START
537877fdebSMatt Macy  *
547877fdebSMatt Macy  * The contents of this file are subject to the terms of the
557877fdebSMatt Macy  * Common Development and Distribution License (the "License").
567877fdebSMatt Macy  * You may not use this file except in compliance with the License.
577877fdebSMatt Macy  *
587877fdebSMatt Macy  * The contents of this file are subject to the terms of the
597877fdebSMatt Macy  * Common Development and Distribution License Version 1.0 (CDDL-1.0).
607877fdebSMatt Macy  * You can obtain a copy of the license from the top-level file
617877fdebSMatt Macy  * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
627877fdebSMatt Macy  * You may not use this file except in compliance with the license.
637877fdebSMatt Macy  *
647877fdebSMatt Macy  * See the License for the specific language governing permissions
657877fdebSMatt Macy  * and limitations under the License.
667877fdebSMatt Macy  *
677877fdebSMatt Macy  * CDDL HEADER END
687877fdebSMatt Macy  */
697877fdebSMatt Macy #include <string.h>
707877fdebSMatt Macy #include <getopt.h>
717877fdebSMatt Macy #include <stdio.h>
727877fdebSMatt Macy #include <stdint.h>
737877fdebSMatt Macy #include <inttypes.h>
7416038816SMartin Matuska #include <libzfs.h>
757877fdebSMatt Macy 
767877fdebSMatt Macy #define	POOL_MEASUREMENT	"zpool_stats"
777877fdebSMatt Macy #define	SCAN_MEASUREMENT	"zpool_scan_stats"
787877fdebSMatt Macy #define	VDEV_MEASUREMENT	"zpool_vdev_stats"
797877fdebSMatt Macy #define	POOL_LATENCY_MEASUREMENT	"zpool_latency"
807877fdebSMatt Macy #define	POOL_QUEUE_MEASUREMENT	"zpool_vdev_queue"
817877fdebSMatt Macy #define	MIN_LAT_INDEX	10  /* minimum latency index 10 = 1024ns */
827877fdebSMatt Macy #define	POOL_IO_SIZE_MEASUREMENT	"zpool_io_size"
837877fdebSMatt Macy #define	MIN_SIZE_INDEX	9  /* minimum size index 9 = 512 bytes */
847877fdebSMatt Macy 
857877fdebSMatt Macy /* global options */
867877fdebSMatt Macy int execd_mode = 0;
877877fdebSMatt Macy int no_histograms = 0;
887877fdebSMatt Macy int sum_histogram_buckets = 0;
897877fdebSMatt Macy char metric_data_type = 'u';
907877fdebSMatt Macy uint64_t metric_value_mask = UINT64_MAX;
917877fdebSMatt Macy uint64_t timestamp = 0;
927877fdebSMatt Macy int complained_about_sync = 0;
93a0b956f5SMartin Matuska const char *tags = "";
947877fdebSMatt Macy 
957877fdebSMatt Macy typedef int (*stat_printer_f)(nvlist_t *, const char *, const char *);
967877fdebSMatt Macy 
977877fdebSMatt Macy /*
987877fdebSMatt Macy  * influxdb line protocol rules for escaping are important because the
997877fdebSMatt Macy  * zpool name can include characters that need to be escaped
1007877fdebSMatt Macy  *
1017877fdebSMatt Macy  * caller is responsible for freeing result
1027877fdebSMatt Macy  */
1037877fdebSMatt Macy static char *
escape_string(const char * s)10416038816SMartin Matuska escape_string(const char *s)
1057877fdebSMatt Macy {
10616038816SMartin Matuska 	const char *c;
10716038816SMartin Matuska 	char *d;
1087877fdebSMatt Macy 	char *t = (char *)malloc(ZFS_MAX_DATASET_NAME_LEN * 2);
1097877fdebSMatt Macy 	if (t == NULL) {
1107877fdebSMatt Macy 		fprintf(stderr, "error: cannot allocate memory\n");
1117877fdebSMatt Macy 		exit(1);
1127877fdebSMatt Macy 	}
1137877fdebSMatt Macy 
1147877fdebSMatt Macy 	for (c = s, d = t; *c != '\0'; c++, d++) {
1157877fdebSMatt Macy 		switch (*c) {
1167877fdebSMatt Macy 		case ' ':
1177877fdebSMatt Macy 		case ',':
1187877fdebSMatt Macy 		case '=':
1197877fdebSMatt Macy 		case '\\':
1207877fdebSMatt Macy 			*d++ = '\\';
121c03c5b1cSMartin Matuska 			zfs_fallthrough;
1227877fdebSMatt Macy 		default:
1237877fdebSMatt Macy 			*d = *c;
1247877fdebSMatt Macy 		}
1257877fdebSMatt Macy 	}
1267877fdebSMatt Macy 	*d = '\0';
1277877fdebSMatt Macy 	return (t);
1287877fdebSMatt Macy }
1297877fdebSMatt Macy 
1307877fdebSMatt Macy /*
1317877fdebSMatt Macy  * print key=value where value is a uint64_t
1327877fdebSMatt Macy  */
1337877fdebSMatt Macy static void
print_kv(const char * key,uint64_t value)134a0b956f5SMartin Matuska print_kv(const char *key, uint64_t value)
1357877fdebSMatt Macy {
1367877fdebSMatt Macy 	printf("%s=%llu%c", key,
1377877fdebSMatt Macy 	    (u_longlong_t)value & metric_value_mask, metric_data_type);
1387877fdebSMatt Macy }
1397877fdebSMatt Macy 
1407877fdebSMatt Macy /*
1417877fdebSMatt Macy  * print_scan_status() prints the details as often seen in the "zpool status"
1427877fdebSMatt Macy  * output. However, unlike the zpool command, which is intended for humans,
1437877fdebSMatt Macy  * this output is suitable for long-term tracking in influxdb.
1447877fdebSMatt Macy  * TODO: update to include issued scan data
1457877fdebSMatt Macy  */
1467877fdebSMatt Macy static int
print_scan_status(nvlist_t * nvroot,const char * pool_name)1477877fdebSMatt Macy print_scan_status(nvlist_t *nvroot, const char *pool_name)
1487877fdebSMatt Macy {
1497877fdebSMatt Macy 	uint_t c;
1507877fdebSMatt Macy 	int64_t elapsed;
1517877fdebSMatt Macy 	uint64_t examined, pass_exam, paused_time, paused_ts, rate;
1527877fdebSMatt Macy 	uint64_t remaining_time;
1537877fdebSMatt Macy 	pool_scan_stat_t *ps = NULL;
1547877fdebSMatt Macy 	double pct_done;
155a0b956f5SMartin Matuska 	const char *const state[DSS_NUM_STATES] = {
1567877fdebSMatt Macy 	    "none", "scanning", "finished", "canceled"};
157a0b956f5SMartin Matuska 	const char *func;
1587877fdebSMatt Macy 
1597877fdebSMatt Macy 	(void) nvlist_lookup_uint64_array(nvroot,
1607877fdebSMatt Macy 	    ZPOOL_CONFIG_SCAN_STATS,
1617877fdebSMatt Macy 	    (uint64_t **)&ps, &c);
1627877fdebSMatt Macy 
1637877fdebSMatt Macy 	/*
1647877fdebSMatt Macy 	 * ignore if there are no stats
1657877fdebSMatt Macy 	 */
1667877fdebSMatt Macy 	if (ps == NULL)
1677877fdebSMatt Macy 		return (0);
1687877fdebSMatt Macy 
1697877fdebSMatt Macy 	/*
1707877fdebSMatt Macy 	 * return error if state is bogus
1717877fdebSMatt Macy 	 */
1727877fdebSMatt Macy 	if (ps->pss_state >= DSS_NUM_STATES ||
1737877fdebSMatt Macy 	    ps->pss_func >= POOL_SCAN_FUNCS) {
1747877fdebSMatt Macy 		if (complained_about_sync % 1000 == 0) {
1757877fdebSMatt Macy 			fprintf(stderr, "error: cannot decode scan stats: "
1767877fdebSMatt Macy 			    "ZFS is out of sync with compiled zpool_influxdb");
1777877fdebSMatt Macy 			complained_about_sync++;
1787877fdebSMatt Macy 		}
1797877fdebSMatt Macy 		return (1);
1807877fdebSMatt Macy 	}
1817877fdebSMatt Macy 
1827877fdebSMatt Macy 	switch (ps->pss_func) {
1837877fdebSMatt Macy 	case POOL_SCAN_NONE:
1847877fdebSMatt Macy 		func = "none_requested";
1857877fdebSMatt Macy 		break;
1867877fdebSMatt Macy 	case POOL_SCAN_SCRUB:
1877877fdebSMatt Macy 		func = "scrub";
1887877fdebSMatt Macy 		break;
1897877fdebSMatt Macy 	case POOL_SCAN_RESILVER:
1907877fdebSMatt Macy 		func = "resilver";
1917877fdebSMatt Macy 		break;
1927877fdebSMatt Macy #ifdef POOL_SCAN_REBUILD
1937877fdebSMatt Macy 	case POOL_SCAN_REBUILD:
1947877fdebSMatt Macy 		func = "rebuild";
1957877fdebSMatt Macy 		break;
1967877fdebSMatt Macy #endif
1977877fdebSMatt Macy 	default:
1987877fdebSMatt Macy 		func = "scan";
1997877fdebSMatt Macy 	}
2007877fdebSMatt Macy 
2017877fdebSMatt Macy 	/* overall progress */
2027877fdebSMatt Macy 	examined = ps->pss_examined ? ps->pss_examined : 1;
2037877fdebSMatt Macy 	pct_done = 0.0;
2047877fdebSMatt Macy 	if (ps->pss_to_examine > 0)
2057877fdebSMatt Macy 		pct_done = 100.0 * examined / ps->pss_to_examine;
2067877fdebSMatt Macy 
2077877fdebSMatt Macy #ifdef EZFS_SCRUB_PAUSED
2087877fdebSMatt Macy 	paused_ts = ps->pss_pass_scrub_pause;
2097877fdebSMatt Macy 	paused_time = ps->pss_pass_scrub_spent_paused;
2107877fdebSMatt Macy #else
2117877fdebSMatt Macy 	paused_ts = 0;
2127877fdebSMatt Macy 	paused_time = 0;
2137877fdebSMatt Macy #endif
2147877fdebSMatt Macy 
2157877fdebSMatt Macy 	/* calculations for this pass */
2167877fdebSMatt Macy 	if (ps->pss_state == DSS_SCANNING) {
2177877fdebSMatt Macy 		elapsed = (int64_t)time(NULL) - (int64_t)ps->pss_pass_start -
2187877fdebSMatt Macy 		    (int64_t)paused_time;
2197877fdebSMatt Macy 		elapsed = (elapsed > 0) ? elapsed : 1;
2207877fdebSMatt Macy 		pass_exam = ps->pss_pass_exam ? ps->pss_pass_exam : 1;
2217877fdebSMatt Macy 		rate = pass_exam / elapsed;
2227877fdebSMatt Macy 		rate = (rate > 0) ? rate : 1;
2237877fdebSMatt Macy 		remaining_time = ps->pss_to_examine - examined / rate;
2247877fdebSMatt Macy 	} else {
2257877fdebSMatt Macy 		elapsed =
2267877fdebSMatt Macy 		    (int64_t)ps->pss_end_time - (int64_t)ps->pss_pass_start -
2277877fdebSMatt Macy 		    (int64_t)paused_time;
2287877fdebSMatt Macy 		elapsed = (elapsed > 0) ? elapsed : 1;
2297877fdebSMatt Macy 		pass_exam = ps->pss_pass_exam ? ps->pss_pass_exam : 1;
2307877fdebSMatt Macy 		rate = pass_exam / elapsed;
2317877fdebSMatt Macy 		remaining_time = 0;
2327877fdebSMatt Macy 	}
2337877fdebSMatt Macy 	rate = rate ? rate : 1;
2347877fdebSMatt Macy 
2357877fdebSMatt Macy 	/* influxdb line protocol format: "tags metrics timestamp" */
2367877fdebSMatt Macy 	printf("%s%s,function=%s,name=%s,state=%s ",
2377877fdebSMatt Macy 	    SCAN_MEASUREMENT, tags, func, pool_name, state[ps->pss_state]);
2387877fdebSMatt Macy 	print_kv("end_ts", ps->pss_end_time);
2397877fdebSMatt Macy 	print_kv(",errors", ps->pss_errors);
2407877fdebSMatt Macy 	print_kv(",examined", examined);
241*0a97523dSMartin Matuska 	print_kv(",skipped", ps->pss_skipped);
2427877fdebSMatt Macy 	print_kv(",issued", ps->pss_issued);
2437877fdebSMatt Macy 	print_kv(",pass_examined", pass_exam);
2447877fdebSMatt Macy 	print_kv(",pass_issued", ps->pss_pass_issued);
2457877fdebSMatt Macy 	print_kv(",paused_ts", paused_ts);
2467877fdebSMatt Macy 	print_kv(",paused_t", paused_time);
2477877fdebSMatt Macy 	printf(",pct_done=%.2f", pct_done);
2487877fdebSMatt Macy 	print_kv(",processed", ps->pss_processed);
2497877fdebSMatt Macy 	print_kv(",rate", rate);
2507877fdebSMatt Macy 	print_kv(",remaining_t", remaining_time);
2517877fdebSMatt Macy 	print_kv(",start_ts", ps->pss_start_time);
2527877fdebSMatt Macy 	print_kv(",to_examine", ps->pss_to_examine);
2537877fdebSMatt Macy 	printf(" %llu\n", (u_longlong_t)timestamp);
2547877fdebSMatt Macy 	return (0);
2557877fdebSMatt Macy }
2567877fdebSMatt Macy 
2577877fdebSMatt Macy /*
2587877fdebSMatt Macy  * get a vdev name that corresponds to the top-level vdev names
2597877fdebSMatt Macy  * printed by `zpool status`
2607877fdebSMatt Macy  */
2617877fdebSMatt Macy static char *
get_vdev_name(nvlist_t * nvroot,const char * parent_name)2627877fdebSMatt Macy get_vdev_name(nvlist_t *nvroot, const char *parent_name)
2637877fdebSMatt Macy {
2647877fdebSMatt Macy 	static char vdev_name[256];
2657877fdebSMatt Macy 	uint64_t vdev_id = 0;
2667877fdebSMatt Macy 
2672a58b312SMartin Matuska 	const char *vdev_type = "unknown";
268be181ee2SMartin Matuska 	(void) nvlist_lookup_string(nvroot, ZPOOL_CONFIG_TYPE, &vdev_type);
269a0b956f5SMartin Matuska 
2707877fdebSMatt Macy 	if (nvlist_lookup_uint64(
271a0b956f5SMartin Matuska 	    nvroot, ZPOOL_CONFIG_ID, &vdev_id) != 0)
2727877fdebSMatt Macy 		vdev_id = UINT64_MAX;
273a0b956f5SMartin Matuska 
2747877fdebSMatt Macy 	if (parent_name == NULL) {
2757877fdebSMatt Macy 		(void) snprintf(vdev_name, sizeof (vdev_name), "%s",
2767877fdebSMatt Macy 		    vdev_type);
2777877fdebSMatt Macy 	} else {
2787877fdebSMatt Macy 		(void) snprintf(vdev_name, sizeof (vdev_name),
279c03c5b1cSMartin Matuska 		    "%.220s/%s-%llu",
2807877fdebSMatt Macy 		    parent_name, vdev_type, (u_longlong_t)vdev_id);
2817877fdebSMatt Macy 	}
2827877fdebSMatt Macy 	return (vdev_name);
2837877fdebSMatt Macy }
2847877fdebSMatt Macy 
2857877fdebSMatt Macy /*
2867877fdebSMatt Macy  * get a string suitable for an influxdb tag that describes this vdev
2877877fdebSMatt Macy  *
2887877fdebSMatt Macy  * By default only the vdev hierarchical name is shown, separated by '/'
2897877fdebSMatt Macy  * If the vdev has an associated path, which is typical of leaf vdevs,
2907877fdebSMatt Macy  * then the path is added.
2917877fdebSMatt Macy  * It would be nice to have the devid instead of the path, but under
2927877fdebSMatt Macy  * Linux we cannot be sure a devid will exist and we'd rather have
2937877fdebSMatt Macy  * something than nothing, so we'll use path instead.
2947877fdebSMatt Macy  */
2957877fdebSMatt Macy static char *
get_vdev_desc(nvlist_t * nvroot,const char * parent_name)2967877fdebSMatt Macy get_vdev_desc(nvlist_t *nvroot, const char *parent_name)
2977877fdebSMatt Macy {
2987877fdebSMatt Macy 	static char vdev_desc[2 * MAXPATHLEN];
2997877fdebSMatt Macy 	char vdev_value[MAXPATHLEN];
3007877fdebSMatt Macy 	char *s, *t;
3017877fdebSMatt Macy 
3022a58b312SMartin Matuska 	const char *vdev_type = "unknown";
303a0b956f5SMartin Matuska 	uint64_t vdev_id = UINT64_MAX;
3042a58b312SMartin Matuska 	const char *vdev_path = NULL;
305be181ee2SMartin Matuska 	(void) nvlist_lookup_string(nvroot, ZPOOL_CONFIG_TYPE, &vdev_type);
306be181ee2SMartin Matuska 	(void) nvlist_lookup_uint64(nvroot, ZPOOL_CONFIG_ID, &vdev_id);
307be181ee2SMartin Matuska 	(void) nvlist_lookup_string(nvroot, ZPOOL_CONFIG_PATH, &vdev_path);
3087877fdebSMatt Macy 
3097877fdebSMatt Macy 	if (parent_name == NULL) {
3107877fdebSMatt Macy 		s = escape_string(vdev_type);
3117877fdebSMatt Macy 		(void) snprintf(vdev_value, sizeof (vdev_value), "vdev=%s", s);
3127877fdebSMatt Macy 		free(s);
3137877fdebSMatt Macy 	} else {
3147877fdebSMatt Macy 		s = escape_string((char *)parent_name);
3157877fdebSMatt Macy 		t = escape_string(vdev_type);
3167877fdebSMatt Macy 		(void) snprintf(vdev_value, sizeof (vdev_value),
3177877fdebSMatt Macy 		    "vdev=%s/%s-%llu", s, t, (u_longlong_t)vdev_id);
3187877fdebSMatt Macy 		free(s);
3197877fdebSMatt Macy 		free(t);
3207877fdebSMatt Macy 	}
3217877fdebSMatt Macy 	if (vdev_path == NULL) {
3227877fdebSMatt Macy 		(void) snprintf(vdev_desc, sizeof (vdev_desc), "%s",
3237877fdebSMatt Macy 		    vdev_value);
3247877fdebSMatt Macy 	} else {
3257877fdebSMatt Macy 		s = escape_string(vdev_path);
3267877fdebSMatt Macy 		(void) snprintf(vdev_desc, sizeof (vdev_desc), "path=%s,%s",
3277877fdebSMatt Macy 		    s, vdev_value);
3287877fdebSMatt Macy 		free(s);
3297877fdebSMatt Macy 	}
3307877fdebSMatt Macy 	return (vdev_desc);
3317877fdebSMatt Macy }
3327877fdebSMatt Macy 
3337877fdebSMatt Macy /*
3347877fdebSMatt Macy  * vdev summary stats are a combination of the data shown by
3357877fdebSMatt Macy  * `zpool status` and `zpool list -v`
3367877fdebSMatt Macy  */
3377877fdebSMatt Macy static int
print_summary_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)3387877fdebSMatt Macy print_summary_stats(nvlist_t *nvroot, const char *pool_name,
3397877fdebSMatt Macy     const char *parent_name)
3407877fdebSMatt Macy {
3417877fdebSMatt Macy 	uint_t c;
3427877fdebSMatt Macy 	vdev_stat_t *vs;
3437877fdebSMatt Macy 	char *vdev_desc = NULL;
3447877fdebSMatt Macy 	vdev_desc = get_vdev_desc(nvroot, parent_name);
3457877fdebSMatt Macy 	if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
3467877fdebSMatt Macy 	    (uint64_t **)&vs, &c) != 0) {
3477877fdebSMatt Macy 		return (1);
3487877fdebSMatt Macy 	}
3497877fdebSMatt Macy 	printf("%s%s,name=%s,state=%s,%s ", POOL_MEASUREMENT, tags,
3507877fdebSMatt Macy 	    pool_name, zpool_state_to_name((vdev_state_t)vs->vs_state,
3517877fdebSMatt Macy 	    (vdev_aux_t)vs->vs_aux), vdev_desc);
3527877fdebSMatt Macy 	print_kv("alloc", vs->vs_alloc);
3537877fdebSMatt Macy 	print_kv(",free", vs->vs_space - vs->vs_alloc);
3547877fdebSMatt Macy 	print_kv(",size", vs->vs_space);
3557877fdebSMatt Macy 	print_kv(",read_bytes", vs->vs_bytes[ZIO_TYPE_READ]);
3567877fdebSMatt Macy 	print_kv(",read_errors", vs->vs_read_errors);
3577877fdebSMatt Macy 	print_kv(",read_ops", vs->vs_ops[ZIO_TYPE_READ]);
3587877fdebSMatt Macy 	print_kv(",write_bytes", vs->vs_bytes[ZIO_TYPE_WRITE]);
3597877fdebSMatt Macy 	print_kv(",write_errors", vs->vs_write_errors);
3607877fdebSMatt Macy 	print_kv(",write_ops", vs->vs_ops[ZIO_TYPE_WRITE]);
3617877fdebSMatt Macy 	print_kv(",checksum_errors", vs->vs_checksum_errors);
3627877fdebSMatt Macy 	print_kv(",fragmentation", vs->vs_fragmentation);
3637877fdebSMatt Macy 	printf(" %llu\n", (u_longlong_t)timestamp);
3647877fdebSMatt Macy 	return (0);
3657877fdebSMatt Macy }
3667877fdebSMatt Macy 
3677877fdebSMatt Macy /*
3687877fdebSMatt Macy  * vdev latency stats are histograms stored as nvlist arrays of uint64.
3697877fdebSMatt Macy  * Latency stats include the ZIO scheduler classes plus lower-level
3707877fdebSMatt Macy  * vdev latencies.
3717877fdebSMatt Macy  *
3727877fdebSMatt Macy  * In many cases, the top-level "root" view obscures the underlying
3737877fdebSMatt Macy  * top-level vdev operations. For example, if a pool has a log, special,
3747877fdebSMatt Macy  * or cache device, then each can behave very differently. It is useful
3757877fdebSMatt Macy  * to see how each is responding.
3767877fdebSMatt Macy  */
3777877fdebSMatt Macy static int
print_vdev_latency_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)3787877fdebSMatt Macy print_vdev_latency_stats(nvlist_t *nvroot, const char *pool_name,
3797877fdebSMatt Macy     const char *parent_name)
3807877fdebSMatt Macy {
3817877fdebSMatt Macy 	uint_t c, end = 0;
3827877fdebSMatt Macy 	nvlist_t *nv_ex;
3837877fdebSMatt Macy 	char *vdev_desc = NULL;
3847877fdebSMatt Macy 
3857877fdebSMatt Macy 	/* short_names become part of the metric name and are influxdb-ready */
3867877fdebSMatt Macy 	struct lat_lookup {
387a0b956f5SMartin Matuska 	    const char *name;
388a0b956f5SMartin Matuska 	    const char *short_name;
3897877fdebSMatt Macy 	    uint64_t sum;
3907877fdebSMatt Macy 	    uint64_t *array;
3917877fdebSMatt Macy 	};
3927877fdebSMatt Macy 	struct lat_lookup lat_type[] = {
3937877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO,   "total_read", 0},
3947877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_TOT_W_LAT_HISTO,   "total_write", 0},
3957877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_DISK_R_LAT_HISTO,  "disk_read", 0},
3967877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_DISK_W_LAT_HISTO,  "disk_write", 0},
3977877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_R_LAT_HISTO,  "sync_read", 0},
3987877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_W_LAT_HISTO,  "sync_write", 0},
3997877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_R_LAT_HISTO, "async_read", 0},
4007877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO, "async_write", 0},
4017877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO,   "scrub", 0},
4027877fdebSMatt Macy #ifdef ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO
4037877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO,    "trim", 0},
4047877fdebSMatt Macy #endif
40521b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_REBUILD_LAT_HISTO,    "rebuild", 0},
4067877fdebSMatt Macy 	    {NULL,	NULL}
4077877fdebSMatt Macy 	};
4087877fdebSMatt Macy 
4097877fdebSMatt Macy 	if (nvlist_lookup_nvlist(nvroot,
4107877fdebSMatt Macy 	    ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
4117877fdebSMatt Macy 		return (6);
4127877fdebSMatt Macy 	}
4137877fdebSMatt Macy 
4147877fdebSMatt Macy 	vdev_desc = get_vdev_desc(nvroot, parent_name);
4157877fdebSMatt Macy 
4167877fdebSMatt Macy 	for (int i = 0; lat_type[i].name; i++) {
4177877fdebSMatt Macy 		if (nvlist_lookup_uint64_array(nv_ex,
4187877fdebSMatt Macy 		    lat_type[i].name, &lat_type[i].array, &c) != 0) {
4197877fdebSMatt Macy 			fprintf(stderr, "error: can't get %s\n",
4207877fdebSMatt Macy 			    lat_type[i].name);
4217877fdebSMatt Macy 			return (3);
4227877fdebSMatt Macy 		}
4237877fdebSMatt Macy 		/* end count count, all of the arrays are the same size */
4247877fdebSMatt Macy 		end = c - 1;
4257877fdebSMatt Macy 	}
4267877fdebSMatt Macy 
4277877fdebSMatt Macy 	for (int bucket = 0; bucket <= end; bucket++) {
4287877fdebSMatt Macy 		if (bucket < MIN_LAT_INDEX) {
4297877fdebSMatt Macy 			/* don't print, but collect the sum */
4307877fdebSMatt Macy 			for (int i = 0; lat_type[i].name; i++) {
4317877fdebSMatt Macy 				lat_type[i].sum += lat_type[i].array[bucket];
4327877fdebSMatt Macy 			}
4337877fdebSMatt Macy 			continue;
4347877fdebSMatt Macy 		}
4357877fdebSMatt Macy 		if (bucket < end) {
4367877fdebSMatt Macy 			printf("%s%s,le=%0.6f,name=%s,%s ",
4377877fdebSMatt Macy 			    POOL_LATENCY_MEASUREMENT, tags,
4387877fdebSMatt Macy 			    (float)(1ULL << bucket) * 1e-9,
4397877fdebSMatt Macy 			    pool_name, vdev_desc);
4407877fdebSMatt Macy 		} else {
4417877fdebSMatt Macy 			printf("%s%s,le=+Inf,name=%s,%s ",
4427877fdebSMatt Macy 			    POOL_LATENCY_MEASUREMENT, tags, pool_name,
4437877fdebSMatt Macy 			    vdev_desc);
4447877fdebSMatt Macy 		}
4457877fdebSMatt Macy 		for (int i = 0; lat_type[i].name; i++) {
4467877fdebSMatt Macy 			if (bucket <= MIN_LAT_INDEX || sum_histogram_buckets) {
4477877fdebSMatt Macy 				lat_type[i].sum += lat_type[i].array[bucket];
4487877fdebSMatt Macy 			} else {
4497877fdebSMatt Macy 				lat_type[i].sum = lat_type[i].array[bucket];
4507877fdebSMatt Macy 			}
4517877fdebSMatt Macy 			print_kv(lat_type[i].short_name, lat_type[i].sum);
4527877fdebSMatt Macy 			if (lat_type[i + 1].name != NULL) {
4537877fdebSMatt Macy 				printf(",");
4547877fdebSMatt Macy 			}
4557877fdebSMatt Macy 		}
4567877fdebSMatt Macy 		printf(" %llu\n", (u_longlong_t)timestamp);
4577877fdebSMatt Macy 	}
4587877fdebSMatt Macy 	return (0);
4597877fdebSMatt Macy }
4607877fdebSMatt Macy 
4617877fdebSMatt Macy /*
4627877fdebSMatt Macy  * vdev request size stats are histograms stored as nvlist arrays of uint64.
4637877fdebSMatt Macy  * Request size stats include the ZIO scheduler classes plus lower-level
4647877fdebSMatt Macy  * vdev sizes. Both independent (ind) and aggregated (agg) sizes are reported.
4657877fdebSMatt Macy  *
4667877fdebSMatt Macy  * In many cases, the top-level "root" view obscures the underlying
4677877fdebSMatt Macy  * top-level vdev operations. For example, if a pool has a log, special,
4687877fdebSMatt Macy  * or cache device, then each can behave very differently. It is useful
4697877fdebSMatt Macy  * to see how each is responding.
4707877fdebSMatt Macy  */
4717877fdebSMatt Macy static int
print_vdev_size_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)4727877fdebSMatt Macy print_vdev_size_stats(nvlist_t *nvroot, const char *pool_name,
4737877fdebSMatt Macy     const char *parent_name)
4747877fdebSMatt Macy {
4757877fdebSMatt Macy 	uint_t c, end = 0;
4767877fdebSMatt Macy 	nvlist_t *nv_ex;
4777877fdebSMatt Macy 	char *vdev_desc = NULL;
4787877fdebSMatt Macy 
4797877fdebSMatt Macy 	/* short_names become the field name */
4807877fdebSMatt Macy 	struct size_lookup {
481a0b956f5SMartin Matuska 	    const char *name;
482a0b956f5SMartin Matuska 	    const char *short_name;
4837877fdebSMatt Macy 	    uint64_t sum;
4847877fdebSMatt Macy 	    uint64_t *array;
4857877fdebSMatt Macy 	};
4867877fdebSMatt Macy 	struct size_lookup size_type[] = {
4877877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_IND_R_HISTO,   "sync_read_ind"},
4887877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_IND_W_HISTO,   "sync_write_ind"},
4897877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_IND_R_HISTO,  "async_read_ind"},
4907877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_IND_W_HISTO,  "async_write_ind"},
4917877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_IND_SCRUB_HISTO,    "scrub_read_ind"},
4927877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_AGG_R_HISTO,   "sync_read_agg"},
4937877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_AGG_W_HISTO,   "sync_write_agg"},
4947877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_AGG_R_HISTO,  "async_read_agg"},
4957877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_AGG_W_HISTO,  "async_write_agg"},
4967877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_AGG_SCRUB_HISTO,    "scrub_read_agg"},
4977877fdebSMatt Macy #ifdef ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO
4987877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO,    "trim_write_ind"},
4997877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_AGG_TRIM_HISTO,    "trim_write_agg"},
5007877fdebSMatt Macy #endif
50121b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_IND_REBUILD_HISTO,    "rebuild_write_ind"},
50221b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_AGG_REBUILD_HISTO,    "rebuild_write_agg"},
5037877fdebSMatt Macy 	    {NULL,	NULL}
5047877fdebSMatt Macy 	};
5057877fdebSMatt Macy 
5067877fdebSMatt Macy 	if (nvlist_lookup_nvlist(nvroot,
5077877fdebSMatt Macy 	    ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
5087877fdebSMatt Macy 		return (6);
5097877fdebSMatt Macy 	}
5107877fdebSMatt Macy 
5117877fdebSMatt Macy 	vdev_desc = get_vdev_desc(nvroot, parent_name);
5127877fdebSMatt Macy 
5137877fdebSMatt Macy 	for (int i = 0; size_type[i].name; i++) {
5147877fdebSMatt Macy 		if (nvlist_lookup_uint64_array(nv_ex, size_type[i].name,
5157877fdebSMatt Macy 		    &size_type[i].array, &c) != 0) {
5167877fdebSMatt Macy 			fprintf(stderr, "error: can't get %s\n",
5177877fdebSMatt Macy 			    size_type[i].name);
5187877fdebSMatt Macy 			return (3);
5197877fdebSMatt Macy 		}
5207877fdebSMatt Macy 		/* end count count, all of the arrays are the same size */
5217877fdebSMatt Macy 		end = c - 1;
5227877fdebSMatt Macy 	}
5237877fdebSMatt Macy 
5247877fdebSMatt Macy 	for (int bucket = 0; bucket <= end; bucket++) {
5257877fdebSMatt Macy 		if (bucket < MIN_SIZE_INDEX) {
5267877fdebSMatt Macy 			/* don't print, but collect the sum */
5277877fdebSMatt Macy 			for (int i = 0; size_type[i].name; i++) {
5287877fdebSMatt Macy 				size_type[i].sum += size_type[i].array[bucket];
5297877fdebSMatt Macy 			}
5307877fdebSMatt Macy 			continue;
5317877fdebSMatt Macy 		}
5327877fdebSMatt Macy 
5337877fdebSMatt Macy 		if (bucket < end) {
5347877fdebSMatt Macy 			printf("%s%s,le=%llu,name=%s,%s ",
5357877fdebSMatt Macy 			    POOL_IO_SIZE_MEASUREMENT, tags, 1ULL << bucket,
5367877fdebSMatt Macy 			    pool_name, vdev_desc);
5377877fdebSMatt Macy 		} else {
5387877fdebSMatt Macy 			printf("%s%s,le=+Inf,name=%s,%s ",
5397877fdebSMatt Macy 			    POOL_IO_SIZE_MEASUREMENT, tags, pool_name,
5407877fdebSMatt Macy 			    vdev_desc);
5417877fdebSMatt Macy 		}
5427877fdebSMatt Macy 		for (int i = 0; size_type[i].name; i++) {
5437877fdebSMatt Macy 			if (bucket <= MIN_SIZE_INDEX || sum_histogram_buckets) {
5447877fdebSMatt Macy 				size_type[i].sum += size_type[i].array[bucket];
5457877fdebSMatt Macy 			} else {
5467877fdebSMatt Macy 				size_type[i].sum = size_type[i].array[bucket];
5477877fdebSMatt Macy 			}
5487877fdebSMatt Macy 			print_kv(size_type[i].short_name, size_type[i].sum);
5497877fdebSMatt Macy 			if (size_type[i + 1].name != NULL) {
5507877fdebSMatt Macy 				printf(",");
5517877fdebSMatt Macy 			}
5527877fdebSMatt Macy 		}
5537877fdebSMatt Macy 		printf(" %llu\n", (u_longlong_t)timestamp);
5547877fdebSMatt Macy 	}
5557877fdebSMatt Macy 	return (0);
5567877fdebSMatt Macy }
5577877fdebSMatt Macy 
5587877fdebSMatt Macy /*
5597877fdebSMatt Macy  * ZIO scheduler queue stats are stored as gauges. This is unfortunate
5607877fdebSMatt Macy  * because the values can change very rapidly and any point-in-time
5617877fdebSMatt Macy  * value will quickly be obsoleted. It is also not easy to downsample.
5627877fdebSMatt Macy  * Thus only the top-level queue stats might be beneficial... maybe.
5637877fdebSMatt Macy  */
5647877fdebSMatt Macy static int
print_queue_stats(nvlist_t * nvroot,const char * pool_name,const char * parent_name)5657877fdebSMatt Macy print_queue_stats(nvlist_t *nvroot, const char *pool_name,
5667877fdebSMatt Macy     const char *parent_name)
5677877fdebSMatt Macy {
5687877fdebSMatt Macy 	nvlist_t *nv_ex;
5697877fdebSMatt Macy 	uint64_t value;
5707877fdebSMatt Macy 
5717877fdebSMatt Macy 	/* short_names are used for the field name */
5727877fdebSMatt Macy 	struct queue_lookup {
573a0b956f5SMartin Matuska 	    const char *name;
574a0b956f5SMartin Matuska 	    const char *short_name;
5757877fdebSMatt Macy 	};
5767877fdebSMatt Macy 	struct queue_lookup queue_type[] = {
5777877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE,	"sync_r_active"},
5787877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_W_ACTIVE_QUEUE,	"sync_w_active"},
5797877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE,	"async_r_active"},
5807877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE,	"async_w_active"},
5817877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE,	"async_scrub_active"},
58221b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE,	"rebuild_active"},
5837877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE,	"sync_r_pend"},
5847877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_W_PEND_QUEUE,	"sync_w_pend"},
5857877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_R_PEND_QUEUE,	"async_r_pend"},
5867877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_W_PEND_QUEUE,	"async_w_pend"},
5877877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE,	"async_scrub_pend"},
58821b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE,	"rebuild_pend"},
5897877fdebSMatt Macy 	    {NULL,	NULL}
5907877fdebSMatt Macy 	};
5917877fdebSMatt Macy 
5927877fdebSMatt Macy 	if (nvlist_lookup_nvlist(nvroot,
5937877fdebSMatt Macy 	    ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
5947877fdebSMatt Macy 		return (6);
5957877fdebSMatt Macy 	}
5967877fdebSMatt Macy 
5977877fdebSMatt Macy 	printf("%s%s,name=%s,%s ", POOL_QUEUE_MEASUREMENT, tags, pool_name,
5987877fdebSMatt Macy 	    get_vdev_desc(nvroot, parent_name));
5997877fdebSMatt Macy 	for (int i = 0; queue_type[i].name; i++) {
6007877fdebSMatt Macy 		if (nvlist_lookup_uint64(nv_ex,
6017877fdebSMatt Macy 		    queue_type[i].name, &value) != 0) {
6027877fdebSMatt Macy 			fprintf(stderr, "error: can't get %s\n",
6037877fdebSMatt Macy 			    queue_type[i].name);
6047877fdebSMatt Macy 			return (3);
6057877fdebSMatt Macy 		}
6067877fdebSMatt Macy 		print_kv(queue_type[i].short_name, value);
6077877fdebSMatt Macy 		if (queue_type[i + 1].name != NULL) {
6087877fdebSMatt Macy 			printf(",");
6097877fdebSMatt Macy 		}
6107877fdebSMatt Macy 	}
6117877fdebSMatt Macy 	printf(" %llu\n", (u_longlong_t)timestamp);
6127877fdebSMatt Macy 	return (0);
6137877fdebSMatt Macy }
6147877fdebSMatt Macy 
6157877fdebSMatt Macy /*
6167877fdebSMatt Macy  * top-level vdev stats are at the pool level
6177877fdebSMatt Macy  */
6187877fdebSMatt Macy static int
print_top_level_vdev_stats(nvlist_t * nvroot,const char * pool_name)6197877fdebSMatt Macy print_top_level_vdev_stats(nvlist_t *nvroot, const char *pool_name)
6207877fdebSMatt Macy {
6217877fdebSMatt Macy 	nvlist_t *nv_ex;
6227877fdebSMatt Macy 	uint64_t value;
6237877fdebSMatt Macy 
6247877fdebSMatt Macy 	/* short_names become part of the metric name */
6257877fdebSMatt Macy 	struct queue_lookup {
626a0b956f5SMartin Matuska 	    const char *name;
627a0b956f5SMartin Matuska 	    const char *short_name;
6287877fdebSMatt Macy 	};
6297877fdebSMatt Macy 	struct queue_lookup queue_type[] = {
6307877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE, "sync_r_active_queue"},
6317877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_W_ACTIVE_QUEUE, "sync_w_active_queue"},
6327877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE, "async_r_active_queue"},
6337877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE, "async_w_active_queue"},
6347877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE, "async_scrub_active_queue"},
63521b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE, "rebuild_active_queue"},
6367877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE, "sync_r_pend_queue"},
6377877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SYNC_W_PEND_QUEUE, "sync_w_pend_queue"},
6387877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_R_PEND_QUEUE, "async_r_pend_queue"},
6397877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_ASYNC_W_PEND_QUEUE, "async_w_pend_queue"},
6407877fdebSMatt Macy 	    {ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE, "async_scrub_pend_queue"},
64121b492edSMartin Matuska 	    {ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE, "rebuild_pend_queue"},
6427877fdebSMatt Macy 	    {NULL, NULL}
6437877fdebSMatt Macy 	};
6447877fdebSMatt Macy 
6457877fdebSMatt Macy 	if (nvlist_lookup_nvlist(nvroot,
6467877fdebSMatt Macy 	    ZPOOL_CONFIG_VDEV_STATS_EX, &nv_ex) != 0) {
6477877fdebSMatt Macy 		return (6);
6487877fdebSMatt Macy 	}
6497877fdebSMatt Macy 
6507877fdebSMatt Macy 	printf("%s%s,name=%s,vdev=root ", VDEV_MEASUREMENT, tags,
6517877fdebSMatt Macy 	    pool_name);
6527877fdebSMatt Macy 	for (int i = 0; queue_type[i].name; i++) {
6537877fdebSMatt Macy 		if (nvlist_lookup_uint64(nv_ex,
6547877fdebSMatt Macy 		    queue_type[i].name, &value) != 0) {
6557877fdebSMatt Macy 			fprintf(stderr, "error: can't get %s\n",
6567877fdebSMatt Macy 			    queue_type[i].name);
6577877fdebSMatt Macy 			return (3);
6587877fdebSMatt Macy 		}
6597877fdebSMatt Macy 		if (i > 0)
6607877fdebSMatt Macy 			printf(",");
6617877fdebSMatt Macy 		print_kv(queue_type[i].short_name, value);
6627877fdebSMatt Macy 	}
6637877fdebSMatt Macy 
6647877fdebSMatt Macy 	printf(" %llu\n", (u_longlong_t)timestamp);
6657877fdebSMatt Macy 	return (0);
6667877fdebSMatt Macy }
6677877fdebSMatt Macy 
6687877fdebSMatt Macy /*
6697877fdebSMatt Macy  * recursive stats printer
6707877fdebSMatt Macy  */
6717877fdebSMatt Macy static int
print_recursive_stats(stat_printer_f func,nvlist_t * nvroot,const char * pool_name,const char * parent_name,int descend)6727877fdebSMatt Macy print_recursive_stats(stat_printer_f func, nvlist_t *nvroot,
6737877fdebSMatt Macy     const char *pool_name, const char *parent_name, int descend)
6747877fdebSMatt Macy {
6757877fdebSMatt Macy 	uint_t c, children;
6767877fdebSMatt Macy 	nvlist_t **child;
6777877fdebSMatt Macy 	char vdev_name[256];
6787877fdebSMatt Macy 	int err;
6797877fdebSMatt Macy 
6807877fdebSMatt Macy 	err = func(nvroot, pool_name, parent_name);
6817877fdebSMatt Macy 	if (err)
6827877fdebSMatt Macy 		return (err);
6837877fdebSMatt Macy 
6847877fdebSMatt Macy 	if (descend && nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
6857877fdebSMatt Macy 	    &child, &children) == 0) {
6863f9d360cSMartin Matuska 		(void) strlcpy(vdev_name, get_vdev_name(nvroot, parent_name),
6877877fdebSMatt Macy 		    sizeof (vdev_name));
6887877fdebSMatt Macy 
6897877fdebSMatt Macy 		for (c = 0; c < children; c++) {
690be181ee2SMartin Matuska 			err = print_recursive_stats(func, child[c], pool_name,
6917877fdebSMatt Macy 			    vdev_name, descend);
692be181ee2SMartin Matuska 			if (err)
693be181ee2SMartin Matuska 				return (err);
6947877fdebSMatt Macy 		}
6957877fdebSMatt Macy 	}
6967877fdebSMatt Macy 	return (0);
6977877fdebSMatt Macy }
6987877fdebSMatt Macy 
6997877fdebSMatt Macy /*
7007877fdebSMatt Macy  * call-back to print the stats from the pool config
7017877fdebSMatt Macy  *
7027877fdebSMatt Macy  * Note: if the pool is broken, this can hang indefinitely and perhaps in an
7037877fdebSMatt Macy  * unkillable state.
7047877fdebSMatt Macy  */
7057877fdebSMatt Macy static int
print_stats(zpool_handle_t * zhp,void * data)7067877fdebSMatt Macy print_stats(zpool_handle_t *zhp, void *data)
7077877fdebSMatt Macy {
7087877fdebSMatt Macy 	uint_t c;
7097877fdebSMatt Macy 	int err;
7107877fdebSMatt Macy 	boolean_t missing;
7117877fdebSMatt Macy 	nvlist_t *config, *nvroot;
7127877fdebSMatt Macy 	vdev_stat_t *vs;
7137877fdebSMatt Macy 	struct timespec tv;
7147877fdebSMatt Macy 	char *pool_name;
7157877fdebSMatt Macy 
7167877fdebSMatt Macy 	/* if not this pool return quickly */
7177877fdebSMatt Macy 	if (data &&
71816038816SMartin Matuska 	    strncmp(data, zpool_get_name(zhp), ZFS_MAX_DATASET_NAME_LEN) != 0) {
7197877fdebSMatt Macy 		zpool_close(zhp);
7207877fdebSMatt Macy 		return (0);
7217877fdebSMatt Macy 	}
7227877fdebSMatt Macy 
7237877fdebSMatt Macy 	if (zpool_refresh_stats(zhp, &missing) != 0) {
7247877fdebSMatt Macy 		zpool_close(zhp);
7257877fdebSMatt Macy 		return (1);
7267877fdebSMatt Macy 	}
7277877fdebSMatt Macy 
7287877fdebSMatt Macy 	config = zpool_get_config(zhp, NULL);
7297877fdebSMatt Macy 	if (clock_gettime(CLOCK_REALTIME, &tv) != 0)
7307877fdebSMatt Macy 		timestamp = (uint64_t)time(NULL) * 1000000000;
7317877fdebSMatt Macy 	else
7327877fdebSMatt Macy 		timestamp =
7337877fdebSMatt Macy 		    ((uint64_t)tv.tv_sec * 1000000000) + (uint64_t)tv.tv_nsec;
7347877fdebSMatt Macy 
7357877fdebSMatt Macy 	if (nvlist_lookup_nvlist(
7367877fdebSMatt Macy 	    config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0) {
7377877fdebSMatt Macy 	zpool_close(zhp);
7387877fdebSMatt Macy 		return (2);
7397877fdebSMatt Macy 	}
7407877fdebSMatt Macy 	if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
7417877fdebSMatt Macy 	    (uint64_t **)&vs, &c) != 0) {
7427877fdebSMatt Macy 	zpool_close(zhp);
7437877fdebSMatt Macy 		return (3);
7447877fdebSMatt Macy 	}
7457877fdebSMatt Macy 
74616038816SMartin Matuska 	pool_name = escape_string(zpool_get_name(zhp));
7477877fdebSMatt Macy 	err = print_recursive_stats(print_summary_stats, nvroot,
7487877fdebSMatt Macy 	    pool_name, NULL, 1);
7497877fdebSMatt Macy 	/* if any of these return an error, skip the rest */
7507877fdebSMatt Macy 	if (err == 0)
7517877fdebSMatt Macy 	err = print_top_level_vdev_stats(nvroot, pool_name);
7527877fdebSMatt Macy 
7537877fdebSMatt Macy 	if (no_histograms == 0) {
7547877fdebSMatt Macy 	if (err == 0)
7557877fdebSMatt Macy 		err = print_recursive_stats(print_vdev_latency_stats, nvroot,
7567877fdebSMatt Macy 		    pool_name, NULL, 1);
7577877fdebSMatt Macy 	if (err == 0)
7587877fdebSMatt Macy 		err = print_recursive_stats(print_vdev_size_stats, nvroot,
7597877fdebSMatt Macy 		    pool_name, NULL, 1);
7607877fdebSMatt Macy 	if (err == 0)
7617877fdebSMatt Macy 		err = print_recursive_stats(print_queue_stats, nvroot,
7627877fdebSMatt Macy 		    pool_name, NULL, 0);
7637877fdebSMatt Macy 	}
7647877fdebSMatt Macy 	if (err == 0)
7657877fdebSMatt Macy 		err = print_scan_status(nvroot, pool_name);
7667877fdebSMatt Macy 
7677877fdebSMatt Macy 	free(pool_name);
7687877fdebSMatt Macy 	zpool_close(zhp);
7697877fdebSMatt Macy 	return (err);
7707877fdebSMatt Macy }
7717877fdebSMatt Macy 
7727877fdebSMatt Macy static void
usage(char * name)7737877fdebSMatt Macy usage(char *name)
7747877fdebSMatt Macy {
7757877fdebSMatt Macy 	fprintf(stderr, "usage: %s [--execd][--no-histograms]"
7767877fdebSMatt Macy 	    "[--sum-histogram-buckets] [--signed-int] [poolname]\n", name);
7777877fdebSMatt Macy 	exit(EXIT_FAILURE);
7787877fdebSMatt Macy }
7797877fdebSMatt Macy 
7807877fdebSMatt Macy int
main(int argc,char * argv[])7817877fdebSMatt Macy main(int argc, char *argv[])
7827877fdebSMatt Macy {
7837877fdebSMatt Macy 	int opt;
7847877fdebSMatt Macy 	int ret = 8;
785a0b956f5SMartin Matuska 	char *line = NULL, *ttags = NULL;
7867877fdebSMatt Macy 	size_t len, tagslen = 0;
7877877fdebSMatt Macy 	struct option long_options[] = {
7887877fdebSMatt Macy 	    {"execd", no_argument, NULL, 'e'},
7897877fdebSMatt Macy 	    {"help", no_argument, NULL, 'h'},
7907877fdebSMatt Macy 	    {"no-histograms", no_argument, NULL, 'n'},
7917877fdebSMatt Macy 	    {"signed-int", no_argument, NULL, 'i'},
7927877fdebSMatt Macy 	    {"sum-histogram-buckets", no_argument, NULL, 's'},
7937877fdebSMatt Macy 	    {"tags", required_argument, NULL, 't'},
7947877fdebSMatt Macy 	    {0, 0, 0, 0}
7957877fdebSMatt Macy 	};
7967877fdebSMatt Macy 	while ((opt = getopt_long(
7977877fdebSMatt Macy 	    argc, argv, "ehinst:", long_options, NULL)) != -1) {
7987877fdebSMatt Macy 		switch (opt) {
7997877fdebSMatt Macy 		case 'e':
8007877fdebSMatt Macy 			execd_mode = 1;
8017877fdebSMatt Macy 			break;
8027877fdebSMatt Macy 		case 'i':
8037877fdebSMatt Macy 			metric_data_type = 'i';
8047877fdebSMatt Macy 			metric_value_mask = INT64_MAX;
8057877fdebSMatt Macy 			break;
8067877fdebSMatt Macy 		case 'n':
8077877fdebSMatt Macy 			no_histograms = 1;
8087877fdebSMatt Macy 			break;
8097877fdebSMatt Macy 		case 's':
8107877fdebSMatt Macy 			sum_histogram_buckets = 1;
8117877fdebSMatt Macy 			break;
8127877fdebSMatt Macy 		case 't':
813a0b956f5SMartin Matuska 			free(ttags);
8147877fdebSMatt Macy 			tagslen = strlen(optarg) + 2;
815a0b956f5SMartin Matuska 			ttags = calloc(1, tagslen);
816a0b956f5SMartin Matuska 			if (ttags == NULL) {
8177877fdebSMatt Macy 				fprintf(stderr,
8187877fdebSMatt Macy 				    "error: cannot allocate memory "
8197877fdebSMatt Macy 				    "for tags\n");
8207877fdebSMatt Macy 				exit(1);
8217877fdebSMatt Macy 			}
822a0b956f5SMartin Matuska 			(void) snprintf(ttags, tagslen, ",%s", optarg);
823a0b956f5SMartin Matuska 			tags = ttags;
8247877fdebSMatt Macy 			break;
8257877fdebSMatt Macy 		default:
8267877fdebSMatt Macy 			usage(argv[0]);
8277877fdebSMatt Macy 		}
8287877fdebSMatt Macy 	}
8297877fdebSMatt Macy 
8307877fdebSMatt Macy 	libzfs_handle_t *g_zfs;
8317877fdebSMatt Macy 	if ((g_zfs = libzfs_init()) == NULL) {
8327877fdebSMatt Macy 		fprintf(stderr,
8337877fdebSMatt Macy 		    "error: cannot initialize libzfs. "
8347877fdebSMatt Macy 		    "Is the zfs module loaded or zrepl running?\n");
8357877fdebSMatt Macy 		exit(EXIT_FAILURE);
8367877fdebSMatt Macy 	}
8377877fdebSMatt Macy 	if (execd_mode == 0) {
8387877fdebSMatt Macy 		ret = zpool_iter(g_zfs, print_stats, argv[optind]);
8397877fdebSMatt Macy 		return (ret);
8407877fdebSMatt Macy 	}
8417877fdebSMatt Macy 	while (getline(&line, &len, stdin) != -1) {
8427877fdebSMatt Macy 		ret = zpool_iter(g_zfs, print_stats, argv[optind]);
8437877fdebSMatt Macy 		fflush(stdout);
8447877fdebSMatt Macy 	}
8457877fdebSMatt Macy 	return (ret);
8467877fdebSMatt Macy }
847