From 96f1b347f8f726163c105704576110a4a8be4b84 Mon Sep 17 00:00:00 2001 From: Don Brady Date: Thu, 2 Feb 2017 15:03:48 -0700 Subject: [PATCH] Add nvlist payload stats for zdb -ll dump When dumping the ZFS vdev label with 'zdb -ll', also dump some nvlist stats to help analyze the current footprint. Reviewed-by: Tony Hutter Reviewed-by: Brian Behlendorf Reviewed-by: George Melikov Signed-off-by: Don Brady Closes #5724 --- cmd/zdb/zdb.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index 882fbae95..2e4c0a1cc 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved. - * Copyright (c) 2015, Intel Corporation. + * Copyright (c) 2015, 2017, Intel Corporation. */ #include @@ -2228,6 +2228,136 @@ dump_label_uberblocks(vdev_label_t *lbl, uint64_t ashift) } } +/* + * ZFS label nvlist stats + */ +typedef struct zdb_nvl_stats { + int zns_list_count; + int zns_leaf_count; + size_t zns_leaf_largest; + size_t zns_leaf_total; + nvlist_t *zns_string; + nvlist_t *zns_uint64; + nvlist_t *zns_boolean; +} zdb_nvl_stats_t; + +static void +collect_nvlist_stats(nvlist_t *nvl, zdb_nvl_stats_t *stats) +{ + nvlist_t *list, **array; + nvpair_t *nvp = NULL; + char *name; + uint_t i, items; + + stats->zns_list_count++; + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + name = nvpair_name(nvp); + + switch (nvpair_type(nvp)) { + case DATA_TYPE_STRING: + fnvlist_add_string(stats->zns_string, name, + fnvpair_value_string(nvp)); + break; + case DATA_TYPE_UINT64: + fnvlist_add_uint64(stats->zns_uint64, name, + fnvpair_value_uint64(nvp)); + break; + case DATA_TYPE_BOOLEAN: + fnvlist_add_boolean(stats->zns_boolean, name); + break; + case DATA_TYPE_NVLIST: + if (nvpair_value_nvlist(nvp, &list) == 0) + collect_nvlist_stats(list, stats); + break; + case DATA_TYPE_NVLIST_ARRAY: + if (nvpair_value_nvlist_array(nvp, &array, &items) != 0) + break; + + for (i = 0; i < items; i++) { + collect_nvlist_stats(array[i], stats); + + /* collect stats on leaf vdev */ + if (strcmp(name, "children") == 0) { + size_t size; + + (void) nvlist_size(array[i], &size, + NV_ENCODE_XDR); + stats->zns_leaf_total += size; + if (size > stats->zns_leaf_largest) + stats->zns_leaf_largest = size; + stats->zns_leaf_count++; + } + } + break; + default: + (void) printf("skip type %d!\n", (int)nvpair_type(nvp)); + } + } +} + +static void +dump_nvlist_stats(nvlist_t *nvl, size_t cap) +{ + zdb_nvl_stats_t stats = { 0 }; + size_t size, sum = 0, total; + size_t noise, average; + + /* requires nvlist with non-unique names for stat collection */ + VERIFY0(nvlist_alloc(&stats.zns_string, 0, 0)); + VERIFY0(nvlist_alloc(&stats.zns_uint64, 0, 0)); + VERIFY0(nvlist_alloc(&stats.zns_boolean, 0, 0)); + VERIFY0(nvlist_size(stats.zns_boolean, &noise, NV_ENCODE_XDR)); + + (void) printf("\n\nZFS Label NVList Config Stats:\n"); + + VERIFY0(nvlist_size(nvl, &total, NV_ENCODE_XDR)); + (void) printf(" %d bytes used, %d bytes free (using %4.1f%%)\n\n", + (int)total, (int)(cap - total), 100.0 * total / cap); + + collect_nvlist_stats(nvl, &stats); + + VERIFY0(nvlist_size(stats.zns_uint64, &size, NV_ENCODE_XDR)); + size -= noise; + sum += size; + (void) printf("%12s %4d %6d bytes (%5.2f%%)\n", "integers:", + (int)fnvlist_num_pairs(stats.zns_uint64), + (int)size, 100.0 * size / total); + + VERIFY0(nvlist_size(stats.zns_string, &size, NV_ENCODE_XDR)); + size -= noise; + sum += size; + (void) printf("%12s %4d %6d bytes (%5.2f%%)\n", "strings:", + (int)fnvlist_num_pairs(stats.zns_string), + (int)size, 100.0 * size / total); + + VERIFY0(nvlist_size(stats.zns_boolean, &size, NV_ENCODE_XDR)); + size -= noise; + sum += size; + (void) printf("%12s %4d %6d bytes (%5.2f%%)\n", "booleans:", + (int)fnvlist_num_pairs(stats.zns_boolean), + (int)size, 100.0 * size / total); + + size = total - sum; /* treat remainder as nvlist overhead */ + (void) printf("%12s %4d %6d bytes (%5.2f%%)\n\n", "nvlists:", + stats.zns_list_count, (int)size, 100.0 * size / total); + + average = stats.zns_leaf_total / stats.zns_leaf_count; + (void) printf("%12s %4d %6d bytes average\n", + "leaf vdevs:", stats.zns_leaf_count, (int)average); + (void) printf("%24d bytes largest\n", (int)stats.zns_leaf_largest); + + if (dump_opt['l'] >= 3) + (void) printf(" space for %d additional leaf vdevs\n", + (int)((cap - total) / average)); + + (void) printf("\n"); + + nvlist_free(stats.zns_string); + nvlist_free(stats.zns_uint64); + nvlist_free(stats.zns_boolean); +} + static void dump_label(const char *dev) { @@ -2285,6 +2415,8 @@ dump_label(const char *dev) nvlist_t *vdev_tree = NULL; dump_nvlist(config, 4); + if (l == 3 && dump_opt['l'] >= 2) + dump_nvlist_stats(config, buflen); if ((nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdev_tree) != 0) || (nvlist_lookup_uint64(vdev_tree,