2 * Gather top-level ZFS pool and resilver/scan statistics and print using
3 * influxdb line protocol
4 * usage: [options] [pool_name]
6 * --execd, -e run in telegraf execd input plugin mode, [CR] on
7 * stdin causes a sample to be printed and wait for
9 * --no-histograms, -n don't print histogram data (reduces cardinality
10 * if you don't care about histograms)
11 * --sum-histogram-buckets, -s sum histogram bucket values
13 * To integrate into telegraf use one of:
14 * 1. the `inputs.execd` plugin with the `--execd` option
15 * 2. the `inputs.exec` plugin to simply run with no options
17 * NOTE: libzfs is an unstable interface. YMMV.
19 * The design goals of this software include:
20 * + be as lightweight as possible
21 * + reduce the number of external dependencies as far as possible, hence
22 * there is no dependency on a client library for managing the metric
23 * collection -- info is printed, KISS
24 * + broken pools or kernel bugs can cause this process to hang in an
25 * unkillable state. For this reason, it is best to keep the damage limited
26 * to a small process like zpool_influxdb rather than a larger collector.
28 * Copyright 2018-2020 Richard Elling
30 * This software is dual-licensed MIT and CDDL.
32 * The MIT License (MIT)
34 * Permission is hereby granted, free of charge, to any person obtaining a copy
35 * of this software and associated documentation files (the "Software"), to deal
36 * in the Software without restriction, including without limitation the rights
37 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 * copies of the Software, and to permit persons to whom the Software is
39 * furnished to do so, subject to the following conditions:
41 * The above copyright notice and this permission notice shall be included in
42 * all copies or substantial portions of the Software.
44 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54 * The contents of this file are subject to the terms of the
55 * Common Development and Distribution License (the "License").
56 * You may not use this file except in compliance with the License.
58 * The contents of this file are subject to the terms of the
59 * Common Development and Distribution License Version 1.0 (CDDL-1.0).
60 * You can obtain a copy of the license from the top-level file
61 * "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
62 * You may not use this file except in compliance with the license.
64 * See the License for the specific language governing permissions
65 * and limitations under the License.
76 #define POOL_MEASUREMENT "zpool_stats"
77 #define SCAN_MEASUREMENT "zpool_scan_stats"
78 #define VDEV_MEASUREMENT "zpool_vdev_stats"
79 #define POOL_LATENCY_MEASUREMENT "zpool_latency"
80 #define POOL_QUEUE_MEASUREMENT "zpool_vdev_queue"
81 #define MIN_LAT_INDEX 10 /* minimum latency index 10 = 1024ns */
82 #define POOL_IO_SIZE_MEASUREMENT "zpool_io_size"
83 #define MIN_SIZE_INDEX 9 /* minimum size index 9 = 512 bytes */
87 int no_histograms
= 0;
88 int sum_histogram_buckets
= 0;
89 char metric_data_type
= 'u';
90 uint64_t metric_value_mask
= UINT64_MAX
;
91 uint64_t timestamp
= 0;
92 int complained_about_sync
= 0;
93 const char *tags
= "";
95 typedef int (*stat_printer_f
)(nvlist_t
*, const char *, const char *);
98 * influxdb line protocol rules for escaping are important because the
99 * zpool name can include characters that need to be escaped
101 * caller is responsible for freeing result
104 escape_string(const char *s
)
108 char *t
= (char *)malloc(ZFS_MAX_DATASET_NAME_LEN
* 2);
110 fprintf(stderr
, "error: cannot allocate memory\n");
114 for (c
= s
, d
= t
; *c
!= '\0'; c
++, d
++) {
131 * print key=value where value is a uint64_t
134 print_kv(const char *key
, uint64_t value
)
136 printf("%s=%llu%c", key
,
137 (u_longlong_t
)value
& metric_value_mask
, metric_data_type
);
141 * print_scan_status() prints the details as often seen in the "zpool status"
142 * output. However, unlike the zpool command, which is intended for humans,
143 * this output is suitable for long-term tracking in influxdb.
144 * TODO: update to include issued scan data
147 print_scan_status(nvlist_t
*nvroot
, const char *pool_name
)
151 uint64_t examined
, pass_exam
, paused_time
, paused_ts
, rate
;
152 uint64_t remaining_time
;
153 pool_scan_stat_t
*ps
= NULL
;
155 const char *const state
[DSS_NUM_STATES
] = {
156 "none", "scanning", "finished", "canceled"};
159 (void) nvlist_lookup_uint64_array(nvroot
,
160 ZPOOL_CONFIG_SCAN_STATS
,
161 (uint64_t **)&ps
, &c
);
164 * ignore if there are no stats
170 * return error if state is bogus
172 if (ps
->pss_state
>= DSS_NUM_STATES
||
173 ps
->pss_func
>= POOL_SCAN_FUNCS
) {
174 if (complained_about_sync
% 1000 == 0) {
175 fprintf(stderr
, "error: cannot decode scan stats: "
176 "ZFS is out of sync with compiled zpool_influxdb");
177 complained_about_sync
++;
182 switch (ps
->pss_func
) {
184 func
= "none_requested";
186 case POOL_SCAN_SCRUB
:
189 case POOL_SCAN_RESILVER
:
192 #ifdef POOL_SCAN_REBUILD
193 case POOL_SCAN_REBUILD
:
201 /* overall progress */
202 examined
= ps
->pss_examined
? ps
->pss_examined
: 1;
204 if (ps
->pss_to_examine
> 0)
205 pct_done
= 100.0 * examined
/ ps
->pss_to_examine
;
207 #ifdef EZFS_SCRUB_PAUSED
208 paused_ts
= ps
->pss_pass_scrub_pause
;
209 paused_time
= ps
->pss_pass_scrub_spent_paused
;
215 /* calculations for this pass */
216 if (ps
->pss_state
== DSS_SCANNING
) {
217 elapsed
= (int64_t)time(NULL
) - (int64_t)ps
->pss_pass_start
-
218 (int64_t)paused_time
;
219 elapsed
= (elapsed
> 0) ? elapsed
: 1;
220 pass_exam
= ps
->pss_pass_exam
? ps
->pss_pass_exam
: 1;
221 rate
= pass_exam
/ elapsed
;
222 rate
= (rate
> 0) ? rate
: 1;
223 remaining_time
= ps
->pss_to_examine
- examined
/ rate
;
226 (int64_t)ps
->pss_end_time
- (int64_t)ps
->pss_pass_start
-
227 (int64_t)paused_time
;
228 elapsed
= (elapsed
> 0) ? elapsed
: 1;
229 pass_exam
= ps
->pss_pass_exam
? ps
->pss_pass_exam
: 1;
230 rate
= pass_exam
/ elapsed
;
233 rate
= rate
? rate
: 1;
235 /* influxdb line protocol format: "tags metrics timestamp" */
236 printf("%s%s,function=%s,name=%s,state=%s ",
237 SCAN_MEASUREMENT
, tags
, func
, pool_name
, state
[ps
->pss_state
]);
238 print_kv("end_ts", ps
->pss_end_time
);
239 print_kv(",errors", ps
->pss_errors
);
240 print_kv(",examined", examined
);
241 print_kv(",skipped", ps
->pss_skipped
);
242 print_kv(",issued", ps
->pss_issued
);
243 print_kv(",pass_examined", pass_exam
);
244 print_kv(",pass_issued", ps
->pss_pass_issued
);
245 print_kv(",paused_ts", paused_ts
);
246 print_kv(",paused_t", paused_time
);
247 printf(",pct_done=%.2f", pct_done
);
248 print_kv(",processed", ps
->pss_processed
);
249 print_kv(",rate", rate
);
250 print_kv(",remaining_t", remaining_time
);
251 print_kv(",start_ts", ps
->pss_start_time
);
252 print_kv(",to_examine", ps
->pss_to_examine
);
253 printf(" %llu\n", (u_longlong_t
)timestamp
);
258 * get a vdev name that corresponds to the top-level vdev names
259 * printed by `zpool status`
262 get_vdev_name(nvlist_t
*nvroot
, const char *parent_name
)
264 static char vdev_name
[256];
265 uint64_t vdev_id
= 0;
267 const char *vdev_type
= "unknown";
268 (void) nvlist_lookup_string(nvroot
, ZPOOL_CONFIG_TYPE
, &vdev_type
);
270 if (nvlist_lookup_uint64(
271 nvroot
, ZPOOL_CONFIG_ID
, &vdev_id
) != 0)
272 vdev_id
= UINT64_MAX
;
274 if (parent_name
== NULL
) {
275 (void) snprintf(vdev_name
, sizeof (vdev_name
), "%s",
278 (void) snprintf(vdev_name
, sizeof (vdev_name
),
280 parent_name
, vdev_type
, (u_longlong_t
)vdev_id
);
286 * get a string suitable for an influxdb tag that describes this vdev
288 * By default only the vdev hierarchical name is shown, separated by '/'
289 * If the vdev has an associated path, which is typical of leaf vdevs,
290 * then the path is added.
291 * It would be nice to have the devid instead of the path, but under
292 * Linux we cannot be sure a devid will exist and we'd rather have
293 * something than nothing, so we'll use path instead.
296 get_vdev_desc(nvlist_t
*nvroot
, const char *parent_name
)
298 static char vdev_desc
[2 * MAXPATHLEN
];
299 char vdev_value
[MAXPATHLEN
];
302 const char *vdev_type
= "unknown";
303 uint64_t vdev_id
= UINT64_MAX
;
304 const char *vdev_path
= NULL
;
305 (void) nvlist_lookup_string(nvroot
, ZPOOL_CONFIG_TYPE
, &vdev_type
);
306 (void) nvlist_lookup_uint64(nvroot
, ZPOOL_CONFIG_ID
, &vdev_id
);
307 (void) nvlist_lookup_string(nvroot
, ZPOOL_CONFIG_PATH
, &vdev_path
);
309 if (parent_name
== NULL
) {
310 s
= escape_string(vdev_type
);
311 (void) snprintf(vdev_value
, sizeof (vdev_value
), "vdev=%s", s
);
314 s
= escape_string((char *)parent_name
);
315 t
= escape_string(vdev_type
);
316 (void) snprintf(vdev_value
, sizeof (vdev_value
),
317 "vdev=%s/%s-%llu", s
, t
, (u_longlong_t
)vdev_id
);
321 if (vdev_path
== NULL
) {
322 (void) snprintf(vdev_desc
, sizeof (vdev_desc
), "%s",
325 s
= escape_string(vdev_path
);
326 (void) snprintf(vdev_desc
, sizeof (vdev_desc
), "path=%s,%s",
334 * vdev summary stats are a combination of the data shown by
335 * `zpool status` and `zpool list -v`
338 print_summary_stats(nvlist_t
*nvroot
, const char *pool_name
,
339 const char *parent_name
)
343 char *vdev_desc
= NULL
;
344 vdev_desc
= get_vdev_desc(nvroot
, parent_name
);
345 if (nvlist_lookup_uint64_array(nvroot
, ZPOOL_CONFIG_VDEV_STATS
,
346 (uint64_t **)&vs
, &c
) != 0) {
349 printf("%s%s,name=%s,state=%s,%s ", POOL_MEASUREMENT
, tags
,
350 pool_name
, zpool_state_to_name((vdev_state_t
)vs
->vs_state
,
351 (vdev_aux_t
)vs
->vs_aux
), vdev_desc
);
352 print_kv("alloc", vs
->vs_alloc
);
353 print_kv(",free", vs
->vs_space
- vs
->vs_alloc
);
354 print_kv(",size", vs
->vs_space
);
355 print_kv(",read_bytes", vs
->vs_bytes
[ZIO_TYPE_READ
]);
356 print_kv(",read_errors", vs
->vs_read_errors
);
357 print_kv(",read_ops", vs
->vs_ops
[ZIO_TYPE_READ
]);
358 print_kv(",write_bytes", vs
->vs_bytes
[ZIO_TYPE_WRITE
]);
359 print_kv(",write_errors", vs
->vs_write_errors
);
360 print_kv(",write_ops", vs
->vs_ops
[ZIO_TYPE_WRITE
]);
361 print_kv(",checksum_errors", vs
->vs_checksum_errors
);
362 print_kv(",fragmentation", vs
->vs_fragmentation
);
363 printf(" %llu\n", (u_longlong_t
)timestamp
);
368 * vdev latency stats are histograms stored as nvlist arrays of uint64.
369 * Latency stats include the ZIO scheduler classes plus lower-level
372 * In many cases, the top-level "root" view obscures the underlying
373 * top-level vdev operations. For example, if a pool has a log, special,
374 * or cache device, then each can behave very differently. It is useful
375 * to see how each is responding.
378 print_vdev_latency_stats(nvlist_t
*nvroot
, const char *pool_name
,
379 const char *parent_name
)
383 char *vdev_desc
= NULL
;
385 /* short_names become part of the metric name and are influxdb-ready */
388 const char *short_name
;
392 struct lat_lookup lat_type
[] = {
393 {ZPOOL_CONFIG_VDEV_TOT_R_LAT_HISTO
, "total_read", 0},
394 {ZPOOL_CONFIG_VDEV_TOT_W_LAT_HISTO
, "total_write", 0},
395 {ZPOOL_CONFIG_VDEV_DISK_R_LAT_HISTO
, "disk_read", 0},
396 {ZPOOL_CONFIG_VDEV_DISK_W_LAT_HISTO
, "disk_write", 0},
397 {ZPOOL_CONFIG_VDEV_SYNC_R_LAT_HISTO
, "sync_read", 0},
398 {ZPOOL_CONFIG_VDEV_SYNC_W_LAT_HISTO
, "sync_write", 0},
399 {ZPOOL_CONFIG_VDEV_ASYNC_R_LAT_HISTO
, "async_read", 0},
400 {ZPOOL_CONFIG_VDEV_ASYNC_W_LAT_HISTO
, "async_write", 0},
401 {ZPOOL_CONFIG_VDEV_SCRUB_LAT_HISTO
, "scrub", 0},
402 #ifdef ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO
403 {ZPOOL_CONFIG_VDEV_TRIM_LAT_HISTO
, "trim", 0},
405 {ZPOOL_CONFIG_VDEV_REBUILD_LAT_HISTO
, "rebuild", 0},
409 if (nvlist_lookup_nvlist(nvroot
,
410 ZPOOL_CONFIG_VDEV_STATS_EX
, &nv_ex
) != 0) {
414 vdev_desc
= get_vdev_desc(nvroot
, parent_name
);
416 for (int i
= 0; lat_type
[i
].name
; i
++) {
417 if (nvlist_lookup_uint64_array(nv_ex
,
418 lat_type
[i
].name
, &lat_type
[i
].array
, &c
) != 0) {
419 fprintf(stderr
, "error: can't get %s\n",
423 /* end count count, all of the arrays are the same size */
427 for (int bucket
= 0; bucket
<= end
; bucket
++) {
428 if (bucket
< MIN_LAT_INDEX
) {
429 /* don't print, but collect the sum */
430 for (int i
= 0; lat_type
[i
].name
; i
++) {
431 lat_type
[i
].sum
+= lat_type
[i
].array
[bucket
];
436 printf("%s%s,le=%0.6f,name=%s,%s ",
437 POOL_LATENCY_MEASUREMENT
, tags
,
438 (float)(1ULL << bucket
) * 1e-9,
439 pool_name
, vdev_desc
);
441 printf("%s%s,le=+Inf,name=%s,%s ",
442 POOL_LATENCY_MEASUREMENT
, tags
, pool_name
,
445 for (int i
= 0; lat_type
[i
].name
; i
++) {
446 if (bucket
<= MIN_LAT_INDEX
|| sum_histogram_buckets
) {
447 lat_type
[i
].sum
+= lat_type
[i
].array
[bucket
];
449 lat_type
[i
].sum
= lat_type
[i
].array
[bucket
];
451 print_kv(lat_type
[i
].short_name
, lat_type
[i
].sum
);
452 if (lat_type
[i
+ 1].name
!= NULL
) {
456 printf(" %llu\n", (u_longlong_t
)timestamp
);
462 * vdev request size stats are histograms stored as nvlist arrays of uint64.
463 * Request size stats include the ZIO scheduler classes plus lower-level
464 * vdev sizes. Both independent (ind) and aggregated (agg) sizes are reported.
466 * In many cases, the top-level "root" view obscures the underlying
467 * top-level vdev operations. For example, if a pool has a log, special,
468 * or cache device, then each can behave very differently. It is useful
469 * to see how each is responding.
472 print_vdev_size_stats(nvlist_t
*nvroot
, const char *pool_name
,
473 const char *parent_name
)
477 char *vdev_desc
= NULL
;
479 /* short_names become the field name */
482 const char *short_name
;
486 struct size_lookup size_type
[] = {
487 {ZPOOL_CONFIG_VDEV_SYNC_IND_R_HISTO
, "sync_read_ind"},
488 {ZPOOL_CONFIG_VDEV_SYNC_IND_W_HISTO
, "sync_write_ind"},
489 {ZPOOL_CONFIG_VDEV_ASYNC_IND_R_HISTO
, "async_read_ind"},
490 {ZPOOL_CONFIG_VDEV_ASYNC_IND_W_HISTO
, "async_write_ind"},
491 {ZPOOL_CONFIG_VDEV_IND_SCRUB_HISTO
, "scrub_read_ind"},
492 {ZPOOL_CONFIG_VDEV_SYNC_AGG_R_HISTO
, "sync_read_agg"},
493 {ZPOOL_CONFIG_VDEV_SYNC_AGG_W_HISTO
, "sync_write_agg"},
494 {ZPOOL_CONFIG_VDEV_ASYNC_AGG_R_HISTO
, "async_read_agg"},
495 {ZPOOL_CONFIG_VDEV_ASYNC_AGG_W_HISTO
, "async_write_agg"},
496 {ZPOOL_CONFIG_VDEV_AGG_SCRUB_HISTO
, "scrub_read_agg"},
497 #ifdef ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO
498 {ZPOOL_CONFIG_VDEV_IND_TRIM_HISTO
, "trim_write_ind"},
499 {ZPOOL_CONFIG_VDEV_AGG_TRIM_HISTO
, "trim_write_agg"},
501 {ZPOOL_CONFIG_VDEV_IND_REBUILD_HISTO
, "rebuild_write_ind"},
502 {ZPOOL_CONFIG_VDEV_AGG_REBUILD_HISTO
, "rebuild_write_agg"},
506 if (nvlist_lookup_nvlist(nvroot
,
507 ZPOOL_CONFIG_VDEV_STATS_EX
, &nv_ex
) != 0) {
511 vdev_desc
= get_vdev_desc(nvroot
, parent_name
);
513 for (int i
= 0; size_type
[i
].name
; i
++) {
514 if (nvlist_lookup_uint64_array(nv_ex
, size_type
[i
].name
,
515 &size_type
[i
].array
, &c
) != 0) {
516 fprintf(stderr
, "error: can't get %s\n",
520 /* end count count, all of the arrays are the same size */
524 for (int bucket
= 0; bucket
<= end
; bucket
++) {
525 if (bucket
< MIN_SIZE_INDEX
) {
526 /* don't print, but collect the sum */
527 for (int i
= 0; size_type
[i
].name
; i
++) {
528 size_type
[i
].sum
+= size_type
[i
].array
[bucket
];
534 printf("%s%s,le=%llu,name=%s,%s ",
535 POOL_IO_SIZE_MEASUREMENT
, tags
, 1ULL << bucket
,
536 pool_name
, vdev_desc
);
538 printf("%s%s,le=+Inf,name=%s,%s ",
539 POOL_IO_SIZE_MEASUREMENT
, tags
, pool_name
,
542 for (int i
= 0; size_type
[i
].name
; i
++) {
543 if (bucket
<= MIN_SIZE_INDEX
|| sum_histogram_buckets
) {
544 size_type
[i
].sum
+= size_type
[i
].array
[bucket
];
546 size_type
[i
].sum
= size_type
[i
].array
[bucket
];
548 print_kv(size_type
[i
].short_name
, size_type
[i
].sum
);
549 if (size_type
[i
+ 1].name
!= NULL
) {
553 printf(" %llu\n", (u_longlong_t
)timestamp
);
559 * ZIO scheduler queue stats are stored as gauges. This is unfortunate
560 * because the values can change very rapidly and any point-in-time
561 * value will quickly be obsoleted. It is also not easy to downsample.
562 * Thus only the top-level queue stats might be beneficial... maybe.
565 print_queue_stats(nvlist_t
*nvroot
, const char *pool_name
,
566 const char *parent_name
)
571 /* short_names are used for the field name */
572 struct queue_lookup
{
574 const char *short_name
;
576 struct queue_lookup queue_type
[] = {
577 {ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE
, "sync_r_active"},
578 {ZPOOL_CONFIG_VDEV_SYNC_W_ACTIVE_QUEUE
, "sync_w_active"},
579 {ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE
, "async_r_active"},
580 {ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE
, "async_w_active"},
581 {ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE
, "async_scrub_active"},
582 {ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE
, "rebuild_active"},
583 {ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE
, "sync_r_pend"},
584 {ZPOOL_CONFIG_VDEV_SYNC_W_PEND_QUEUE
, "sync_w_pend"},
585 {ZPOOL_CONFIG_VDEV_ASYNC_R_PEND_QUEUE
, "async_r_pend"},
586 {ZPOOL_CONFIG_VDEV_ASYNC_W_PEND_QUEUE
, "async_w_pend"},
587 {ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE
, "async_scrub_pend"},
588 {ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE
, "rebuild_pend"},
592 if (nvlist_lookup_nvlist(nvroot
,
593 ZPOOL_CONFIG_VDEV_STATS_EX
, &nv_ex
) != 0) {
597 printf("%s%s,name=%s,%s ", POOL_QUEUE_MEASUREMENT
, tags
, pool_name
,
598 get_vdev_desc(nvroot
, parent_name
));
599 for (int i
= 0; queue_type
[i
].name
; i
++) {
600 if (nvlist_lookup_uint64(nv_ex
,
601 queue_type
[i
].name
, &value
) != 0) {
602 fprintf(stderr
, "error: can't get %s\n",
606 print_kv(queue_type
[i
].short_name
, value
);
607 if (queue_type
[i
+ 1].name
!= NULL
) {
611 printf(" %llu\n", (u_longlong_t
)timestamp
);
616 * top-level vdev stats are at the pool level
619 print_top_level_vdev_stats(nvlist_t
*nvroot
, const char *pool_name
)
624 /* short_names become part of the metric name */
625 struct queue_lookup
{
627 const char *short_name
;
629 struct queue_lookup queue_type
[] = {
630 {ZPOOL_CONFIG_VDEV_SYNC_R_ACTIVE_QUEUE
, "sync_r_active_queue"},
631 {ZPOOL_CONFIG_VDEV_SYNC_W_ACTIVE_QUEUE
, "sync_w_active_queue"},
632 {ZPOOL_CONFIG_VDEV_ASYNC_R_ACTIVE_QUEUE
, "async_r_active_queue"},
633 {ZPOOL_CONFIG_VDEV_ASYNC_W_ACTIVE_QUEUE
, "async_w_active_queue"},
634 {ZPOOL_CONFIG_VDEV_SCRUB_ACTIVE_QUEUE
, "async_scrub_active_queue"},
635 {ZPOOL_CONFIG_VDEV_REBUILD_ACTIVE_QUEUE
, "rebuild_active_queue"},
636 {ZPOOL_CONFIG_VDEV_SYNC_R_PEND_QUEUE
, "sync_r_pend_queue"},
637 {ZPOOL_CONFIG_VDEV_SYNC_W_PEND_QUEUE
, "sync_w_pend_queue"},
638 {ZPOOL_CONFIG_VDEV_ASYNC_R_PEND_QUEUE
, "async_r_pend_queue"},
639 {ZPOOL_CONFIG_VDEV_ASYNC_W_PEND_QUEUE
, "async_w_pend_queue"},
640 {ZPOOL_CONFIG_VDEV_SCRUB_PEND_QUEUE
, "async_scrub_pend_queue"},
641 {ZPOOL_CONFIG_VDEV_REBUILD_PEND_QUEUE
, "rebuild_pend_queue"},
645 if (nvlist_lookup_nvlist(nvroot
,
646 ZPOOL_CONFIG_VDEV_STATS_EX
, &nv_ex
) != 0) {
650 printf("%s%s,name=%s,vdev=root ", VDEV_MEASUREMENT
, tags
,
652 for (int i
= 0; queue_type
[i
].name
; i
++) {
653 if (nvlist_lookup_uint64(nv_ex
,
654 queue_type
[i
].name
, &value
) != 0) {
655 fprintf(stderr
, "error: can't get %s\n",
661 print_kv(queue_type
[i
].short_name
, value
);
664 printf(" %llu\n", (u_longlong_t
)timestamp
);
669 * recursive stats printer
672 print_recursive_stats(stat_printer_f func
, nvlist_t
*nvroot
,
673 const char *pool_name
, const char *parent_name
, int descend
)
680 err
= func(nvroot
, pool_name
, parent_name
);
684 if (descend
&& nvlist_lookup_nvlist_array(nvroot
, ZPOOL_CONFIG_CHILDREN
,
685 &child
, &children
) == 0) {
686 (void) strlcpy(vdev_name
, get_vdev_name(nvroot
, parent_name
),
689 for (c
= 0; c
< children
; c
++) {
690 err
= print_recursive_stats(func
, child
[c
], pool_name
,
700 * call-back to print the stats from the pool config
702 * Note: if the pool is broken, this can hang indefinitely and perhaps in an
706 print_stats(zpool_handle_t
*zhp
, void *data
)
711 nvlist_t
*config
, *nvroot
;
716 /* if not this pool return quickly */
718 strncmp(data
, zpool_get_name(zhp
), ZFS_MAX_DATASET_NAME_LEN
) != 0) {
723 if (zpool_refresh_stats(zhp
, &missing
) != 0) {
728 config
= zpool_get_config(zhp
, NULL
);
729 if (clock_gettime(CLOCK_REALTIME
, &tv
) != 0)
730 timestamp
= (uint64_t)time(NULL
) * 1000000000;
733 ((uint64_t)tv
.tv_sec
* 1000000000) + (uint64_t)tv
.tv_nsec
;
735 if (nvlist_lookup_nvlist(
736 config
, ZPOOL_CONFIG_VDEV_TREE
, &nvroot
) != 0) {
740 if (nvlist_lookup_uint64_array(nvroot
, ZPOOL_CONFIG_VDEV_STATS
,
741 (uint64_t **)&vs
, &c
) != 0) {
746 pool_name
= escape_string(zpool_get_name(zhp
));
747 err
= print_recursive_stats(print_summary_stats
, nvroot
,
749 /* if any of these return an error, skip the rest */
751 err
= print_top_level_vdev_stats(nvroot
, pool_name
);
753 if (no_histograms
== 0) {
755 err
= print_recursive_stats(print_vdev_latency_stats
, nvroot
,
758 err
= print_recursive_stats(print_vdev_size_stats
, nvroot
,
761 err
= print_recursive_stats(print_queue_stats
, nvroot
,
765 err
= print_scan_status(nvroot
, pool_name
);
775 fprintf(stderr
, "usage: %s [--execd][--no-histograms]"
776 "[--sum-histogram-buckets] [--signed-int] [poolname]\n", name
);
781 main(int argc
, char *argv
[])
785 char *line
= NULL
, *ttags
= NULL
;
786 size_t len
, tagslen
= 0;
787 struct option long_options
[] = {
788 {"execd", no_argument
, NULL
, 'e'},
789 {"help", no_argument
, NULL
, 'h'},
790 {"no-histograms", no_argument
, NULL
, 'n'},
791 {"signed-int", no_argument
, NULL
, 'i'},
792 {"sum-histogram-buckets", no_argument
, NULL
, 's'},
793 {"tags", required_argument
, NULL
, 't'},
796 while ((opt
= getopt_long(
797 argc
, argv
, "ehinst:", long_options
, NULL
)) != -1) {
803 metric_data_type
= 'i';
804 metric_value_mask
= INT64_MAX
;
810 sum_histogram_buckets
= 1;
814 tagslen
= strlen(optarg
) + 2;
815 ttags
= calloc(1, tagslen
);
818 "error: cannot allocate memory "
822 (void) snprintf(ttags
, tagslen
, ",%s", optarg
);
830 libzfs_handle_t
*g_zfs
;
831 if ((g_zfs
= libzfs_init()) == NULL
) {
833 "error: cannot initialize libzfs. "
834 "Is the zfs module loaded or zrepl running?\n");
837 if (execd_mode
== 0) {
838 ret
= zpool_iter(g_zfs
, print_stats
, argv
[optind
]);
841 while (getline(&line
, &len
, stdin
) != -1) {
842 ret
= zpool_iter(g_zfs
, print_stats
, argv
[optind
]);