Message ID | 20180716091325.12621-1-suy.fnst@cn.fujitsu.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 2018年07月16日 17:13, Su Yue wrote: > For big filesystems, there are many items in trees(extent tree > specially). > For example, to dump one extent, we usually dump extent tree then pipe > result to grep. The time-consuming part is that dump tree traverses > items. And it eats cpu and memory too. > > This patch introduces an option '-k u64,u8,u64' for users(developers more > likely) to appoint a specific key to search and dump, then the path > from root node down the leaf will be dumped. > The search of the key costs most time of this way. > > Signed-off-by: Su Yue <suy.fnst@cn.fujitsu.com> Looks pretty good, just small nitpicks. Basically you could add my reviewed-by tag: Reviewed-by: Qu Wenruo <wqu@suse.com> > --- > Change log: > v2: > Rename btrfs_search_spec_key() to btrfs_search_print_path(). > Rename is_key_spec to is_spec_key_set. > Move btrfs_search_print_path() after open_ctree(), call it only once. > Remove duplicate code and replaced with btrfs_print_tree(). > Modify the usage about '-k'. > All are suggested by Qu Wenruo. > > cmds-inspect-dump-tree.c | 41 ++++++++++++++++++++- > print-tree.c | 77 ++++++++++++++++++++++++++++++++++++++++ > print-tree.h | 2 ++ > 3 files changed, 119 insertions(+), 1 deletion(-) > > diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c > index c8acd55a0c3a..80df826af0fd 100644 > --- a/cmds-inspect-dump-tree.c > +++ b/cmds-inspect-dump-tree.c > @@ -184,6 +184,21 @@ static u64 treeid_from_string(const char *str, const char **end) > return id; > } > > +static int parse_key(struct btrfs_key *key) > +{ > + > + int ret = sscanf(optarg, "%llu,%hhu,%llu", &key->objectid, > + &key->type, &key->offset); > + if (ret != 3) { > + error("error parsing key '%s'\n", optarg); > + ret = -EINVAL; > + } else { > + ret = 0; > + } > + > + return ret; > +} > + > const char * const cmd_inspect_dump_tree_usage[] = { > "btrfs inspect-internal dump-tree [options] device", > "Dump tree structures from a given device", > @@ -199,6 +214,7 @@ const char * const cmd_inspect_dump_tree_usage[] = { > "-u|--uuid print only the uuid tree", > "-b|--block <block_num> print info from the specified block only", > "-t|--tree <tree_id> print only tree with the given id (string or number)", > + "-k|--key <u64,u8,u64> search the specific key then print path, must with -t(conflicts with others)", What are the "others"? At least for me, other includes -r and -b. > "--follow use with -b, to show all children tree blocks of <block_num>", > NULL > }; > @@ -224,8 +240,10 @@ int cmd_inspect_dump_tree(int argc, char **argv) > unsigned open_ctree_flags; > u64 block_only = 0; > struct btrfs_root *tree_root_scan; > + struct btrfs_key spec_key = { 0 }; > u64 tree_id = 0; > bool follow = false; > + bool is_spec_key_set = false; > > /* > * For debug-tree, we care nothing about extent tree (it's just backref > @@ -248,10 +266,11 @@ int cmd_inspect_dump_tree(int argc, char **argv) > { "block", required_argument, NULL, 'b'}, > { "tree", required_argument, NULL, 't'}, > { "follow", no_argument, NULL, GETOPT_VAL_FOLLOW }, > + { "key", required_argument, NULL, 'k'}, > { NULL, 0, NULL, 0 } > }; > > - c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL); > + c = getopt_long(argc, argv, "deb:rRut:k:", long_options, NULL); > if (c < 0) > break; > switch (c) { > @@ -300,6 +319,12 @@ int cmd_inspect_dump_tree(int argc, char **argv) > } > break; > } > + case 'k': > + ret = parse_key(&spec_key); > + if (ret) > + exit(1); > + is_spec_key_set = true; > + break; > case GETOPT_VAL_FOLLOW: > follow = true; > break; > @@ -308,6 +333,9 @@ int cmd_inspect_dump_tree(int argc, char **argv) > } > } > > + if (!tree_id && is_spec_key_set) > + usage(cmd_inspect_dump_tree_usage); > + And options conflict should also be done here. Thanks, Qu > if (check_argc_exact(argc - optind, 1)) > usage(cmd_inspect_dump_tree_usage); > > @@ -325,6 +353,17 @@ int cmd_inspect_dump_tree(int argc, char **argv) > goto out; > } > > + if (is_spec_key_set) { > + root = info->tree_root; > + if (IS_ERR(root) || !root) { > + ret = root ? PTR_ERR(root) : -1; > + error("unable to open %s", argv[optind]); > + goto out; > + } > + > + btrfs_search_print_path(info, tree_id, &spec_key); > + goto close_root; > + } > if (block_only) { > root = info->chunk_root; > leaf = read_tree_block(info, block_only, 0); > diff --git a/print-tree.c b/print-tree.c > index a09ecfbb28f0..3868e42d5d29 100644 > --- a/print-tree.c > +++ b/print-tree.c > @@ -1459,3 +1459,80 @@ void btrfs_print_tree(struct extent_buffer *eb, int follow) > > return; > } > + > +void btrfs_search_print_path(struct btrfs_fs_info *fs_info, u64 root_objectid, > + struct btrfs_key *skey) > +{ > + int ret; > + int level; > + struct btrfs_root *root; > + struct btrfs_key key; > + struct extent_buffer *next; > + struct extent_buffer *eb; > + struct btrfs_path path; > + > + key.objectid = root_objectid; > + key.type = BTRFS_ROOT_ITEM_KEY; > + key.offset = (u64)-1; > + > + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) > + root = btrfs_read_fs_root_no_cache(fs_info, &key); > + else > + root = btrfs_read_fs_root(fs_info, &key); > + > + if (IS_ERR(root) || !root) { > + error("failed to read tree: %lld", key.objectid); > + return; > + } > + > + btrfs_init_path(&path); > + ret = btrfs_search_slot(NULL, root, skey, &path, 0, 0); > + if (ret < 0) { > + error("error search the key [%llu, %u, %llu] in root %llu", > + skey->objectid, skey->type, skey->offset, root->objectid); > + goto out; > + } > + > + if (ret > 0) { > + if (path.slots[0] > btrfs_header_nritems(path.nodes[0])) { > + ret = btrfs_next_item(root, &path); > + if (ret) { > + error( > + "error search the key [%llu, %u, %llu] in root %llu, no next key", > + skey->objectid, skey->type, > + skey->offset, root->objectid); > + goto out; > + } > + } > + > + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); > + printf("next to search key is [%llu, %u, %llu]\n", > + key.objectid, key.type, key.offset); > + } > + > + level = btrfs_header_level(root->node); > + for (; level >= 0 ; level--) { > + eb = path.nodes[level]; > + next = path.nodes[level - 1]; > + > + btrfs_print_tree(path.nodes[level], 0); > + > + if (level == 0) > + break; > + if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) { > + warning( > + "eb corrupted: parent bytenr %llu slot %d level %d child bytenr %llu level has %d expect %d, skipping the slot", > + btrfs_header_bytenr(eb), path.slots[level], > + btrfs_header_level(eb), > + btrfs_header_bytenr(next), > + btrfs_header_level(next), > + btrfs_header_level(eb) - 1); > + continue; > + } > + } > + > +out: > + if (root_objectid == BTRFS_TREE_RELOC_OBJECTID) > + btrfs_free_fs_root(root); > + btrfs_release_path(&path); > +} > diff --git a/print-tree.h b/print-tree.h > index 62667d7f6195..c7694596e0ee 100644 > --- a/print-tree.h > +++ b/print-tree.h > @@ -26,4 +26,6 @@ void print_chunk_item(struct extent_buffer *eb, struct btrfs_chunk *chunk); > void print_extent_item(struct extent_buffer *eb, int slot, int metadata); > void print_objectid(FILE *stream, u64 objectid, u8 type); > void print_key_type(FILE *stream, u64 objectid, u8 type); > +void btrfs_search_print_path(struct btrfs_fs_info *info, u64 root_objectid, > + struct btrfs_key *key); > #endif > -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Jul 16, 2018 at 05:13:25PM +0800, Su Yue wrote: > For big filesystems, there are many items in trees(extent tree > specially). > For example, to dump one extent, we usually dump extent tree then pipe > result to grep. The time-consuming part is that dump tree traverses > items. And it eats cpu and memory too. > > This patch introduces an option '-k u64,u8,u64' for users(developers more > likely) to appoint a specific key to search and dump, then the path > from root node down the leaf will be dumped. > The search of the key costs most time of this way. > > Signed-off-by: Su Yue <suy.fnst@cn.fujitsu.com> > --- > Change log: > v2: > Rename btrfs_search_spec_key() to btrfs_search_print_path(). > Rename is_key_spec to is_spec_key_set. > Move btrfs_search_print_path() after open_ctree(), call it only once. > Remove duplicate code and replaced with btrfs_print_tree(). > Modify the usage about '-k'. > All are suggested by Qu Wenruo. > > cmds-inspect-dump-tree.c | 41 ++++++++++++++++++++- > print-tree.c | 77 ++++++++++++++++++++++++++++++++++++++++ > print-tree.h | 2 ++ > 3 files changed, 119 insertions(+), 1 deletion(-) > > diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c > index c8acd55a0c3a..80df826af0fd 100644 > --- a/cmds-inspect-dump-tree.c > +++ b/cmds-inspect-dump-tree.c > @@ -184,6 +184,21 @@ static u64 treeid_from_string(const char *str, const char **end) > return id; > } > > +static int parse_key(struct btrfs_key *key) > +{ > + Extra newline. > + int ret = sscanf(optarg, "%llu,%hhu,%llu", &key->objectid, > + &key->type, &key->offset); Please don't do complex initializations in the declaration block. And use of the global variable 'optarg' is not good, pass the scanned string as a parameter. > + if (ret != 3) { > + error("error parsing key '%s'\n", optarg); no "\n" at the end of the string in error(...) > + ret = -EINVAL; > + } else { > + ret = 0; > + } > + > + return ret; > +} > + > const char * const cmd_inspect_dump_tree_usage[] = { > "btrfs inspect-internal dump-tree [options] device", > "Dump tree structures from a given device", > @@ -199,6 +214,7 @@ const char * const cmd_inspect_dump_tree_usage[] = { > "-u|--uuid print only the uuid tree", > "-b|--block <block_num> print info from the specified block only", > "-t|--tree <tree_id> print only tree with the given id (string or number)", > + "-k|--key <u64,u8,u64> search the specific key then print path, must with -t(conflicts with others)", As there are more conflicting options, it would be better to group them and just put a note about that. The message as you write it does not provide clear usage instructions. > "--follow use with -b, to show all children tree blocks of <block_num>", > NULL > }; > @@ -224,8 +240,10 @@ int cmd_inspect_dump_tree(int argc, char **argv) > unsigned open_ctree_flags; > u64 block_only = 0; > struct btrfs_root *tree_root_scan; > + struct btrfs_key spec_key = { 0 }; > u64 tree_id = 0; > bool follow = false; > + bool is_spec_key_set = false; > > /* > * For debug-tree, we care nothing about extent tree (it's just backref > @@ -248,10 +266,11 @@ int cmd_inspect_dump_tree(int argc, char **argv) > { "block", required_argument, NULL, 'b'}, > { "tree", required_argument, NULL, 't'}, > { "follow", no_argument, NULL, GETOPT_VAL_FOLLOW }, > + { "key", required_argument, NULL, 'k'}, > { NULL, 0, NULL, 0 } > }; > > - c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL); > + c = getopt_long(argc, argv, "deb:rRut:k:", long_options, NULL); > if (c < 0) > break; > switch (c) { > @@ -300,6 +319,12 @@ int cmd_inspect_dump_tree(int argc, char **argv) > } > break; > } > + case 'k': > + ret = parse_key(&spec_key); > + if (ret) > + exit(1); > + is_spec_key_set = true; What if the key is already set? > + break; > case GETOPT_VAL_FOLLOW: > follow = true; > break; > @@ -308,6 +333,9 @@ int cmd_inspect_dump_tree(int argc, char **argv) > } > } > > + if (!tree_id && is_spec_key_set) > + usage(cmd_inspect_dump_tree_usage); > + > if (check_argc_exact(argc - optind, 1)) > usage(cmd_inspect_dump_tree_usage); > > @@ -325,6 +353,17 @@ int cmd_inspect_dump_tree(int argc, char **argv) > goto out; > } > > + if (is_spec_key_set) { > + root = info->tree_root; > + if (IS_ERR(root) || !root) { > + ret = root ? PTR_ERR(root) : -1; > + error("unable to open %s", argv[optind]); > + goto out; > + } > + > + btrfs_search_print_path(info, tree_id, &spec_key); > + goto close_root; > + } > if (block_only) { > root = info->chunk_root; > leaf = read_tree_block(info, block_only, 0); > diff --git a/print-tree.c b/print-tree.c > index a09ecfbb28f0..3868e42d5d29 100644 > --- a/print-tree.c > +++ b/print-tree.c > @@ -1459,3 +1459,80 @@ void btrfs_print_tree(struct extent_buffer *eb, int follow) > > return; > } > + > +void btrfs_search_print_path(struct btrfs_fs_info *fs_info, u64 root_objectid, > + struct btrfs_key *skey) > +{ > + int ret; > + int level; > + struct btrfs_root *root; > + struct btrfs_key key; > + struct extent_buffer *next; > + struct extent_buffer *eb; > + struct btrfs_path path; > + > + key.objectid = root_objectid; > + key.type = BTRFS_ROOT_ITEM_KEY; > + key.offset = (u64)-1; > + > + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) > + root = btrfs_read_fs_root_no_cache(fs_info, &key); > + else > + root = btrfs_read_fs_root(fs_info, &key); > + > + if (IS_ERR(root) || !root) { > + error("failed to read tree: %lld", key.objectid); > + return; > + } > + > + btrfs_init_path(&path); > + ret = btrfs_search_slot(NULL, root, skey, &path, 0, 0); > + if (ret < 0) { > + error("error search the key [%llu, %u, %llu] in root %llu", > + skey->objectid, skey->type, skey->offset, root->objectid); > + goto out; > + } > + > + if (ret > 0) { > + if (path.slots[0] > btrfs_header_nritems(path.nodes[0])) { > + ret = btrfs_next_item(root, &path); > + if (ret) { > + error( > + "error search the key [%llu, %u, %llu] in root %llu, no next key", > + skey->objectid, skey->type, > + skey->offset, root->objectid); > + goto out; > + } > + } > + > + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); > + printf("next to search key is [%llu, %u, %llu]\n", > + key.objectid, key.type, key.offset); > + } > + > + level = btrfs_header_level(root->node); > + for (; level >= 0 ; level--) { Why isn't this written as for (level = btrfs_header_level(root->node); level >= 0; level--) { ? > + eb = path.nodes[level]; > + next = path.nodes[level - 1]; > + > + btrfs_print_tree(path.nodes[level], 0); > + > + if (level == 0) > + break; > + if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) { > + warning( > + "eb corrupted: parent bytenr %llu slot %d level %d child bytenr %llu level has %d expect %d, skipping the slot", Unindent strings to the left > + btrfs_header_bytenr(eb), path.slots[level], > + btrfs_header_level(eb), > + btrfs_header_bytenr(next), > + btrfs_header_level(next), > + btrfs_header_level(eb) - 1); > + continue; > + } > + } > + > +out: > + if (root_objectid == BTRFS_TREE_RELOC_OBJECTID) > + btrfs_free_fs_root(root); > + btrfs_release_path(&path); > +} > diff --git a/print-tree.h b/print-tree.h > index 62667d7f6195..c7694596e0ee 100644 > --- a/print-tree.h > +++ b/print-tree.h > @@ -26,4 +26,6 @@ void print_chunk_item(struct extent_buffer *eb, struct btrfs_chunk *chunk); > void print_extent_item(struct extent_buffer *eb, int slot, int metadata); > void print_objectid(FILE *stream, u64 objectid, u8 type); > void print_key_type(FILE *stream, u64 objectid, u8 type); > +void btrfs_search_print_path(struct btrfs_fs_info *info, u64 root_objectid, > + struct btrfs_key *key); Newline before #endif please. > #endif -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c index c8acd55a0c3a..80df826af0fd 100644 --- a/cmds-inspect-dump-tree.c +++ b/cmds-inspect-dump-tree.c @@ -184,6 +184,21 @@ static u64 treeid_from_string(const char *str, const char **end) return id; } +static int parse_key(struct btrfs_key *key) +{ + + int ret = sscanf(optarg, "%llu,%hhu,%llu", &key->objectid, + &key->type, &key->offset); + if (ret != 3) { + error("error parsing key '%s'\n", optarg); + ret = -EINVAL; + } else { + ret = 0; + } + + return ret; +} + const char * const cmd_inspect_dump_tree_usage[] = { "btrfs inspect-internal dump-tree [options] device", "Dump tree structures from a given device", @@ -199,6 +214,7 @@ const char * const cmd_inspect_dump_tree_usage[] = { "-u|--uuid print only the uuid tree", "-b|--block <block_num> print info from the specified block only", "-t|--tree <tree_id> print only tree with the given id (string or number)", + "-k|--key <u64,u8,u64> search the specific key then print path, must with -t(conflicts with others)", "--follow use with -b, to show all children tree blocks of <block_num>", NULL }; @@ -224,8 +240,10 @@ int cmd_inspect_dump_tree(int argc, char **argv) unsigned open_ctree_flags; u64 block_only = 0; struct btrfs_root *tree_root_scan; + struct btrfs_key spec_key = { 0 }; u64 tree_id = 0; bool follow = false; + bool is_spec_key_set = false; /* * For debug-tree, we care nothing about extent tree (it's just backref @@ -248,10 +266,11 @@ int cmd_inspect_dump_tree(int argc, char **argv) { "block", required_argument, NULL, 'b'}, { "tree", required_argument, NULL, 't'}, { "follow", no_argument, NULL, GETOPT_VAL_FOLLOW }, + { "key", required_argument, NULL, 'k'}, { NULL, 0, NULL, 0 } }; - c = getopt_long(argc, argv, "deb:rRut:", long_options, NULL); + c = getopt_long(argc, argv, "deb:rRut:k:", long_options, NULL); if (c < 0) break; switch (c) { @@ -300,6 +319,12 @@ int cmd_inspect_dump_tree(int argc, char **argv) } break; } + case 'k': + ret = parse_key(&spec_key); + if (ret) + exit(1); + is_spec_key_set = true; + break; case GETOPT_VAL_FOLLOW: follow = true; break; @@ -308,6 +333,9 @@ int cmd_inspect_dump_tree(int argc, char **argv) } } + if (!tree_id && is_spec_key_set) + usage(cmd_inspect_dump_tree_usage); + if (check_argc_exact(argc - optind, 1)) usage(cmd_inspect_dump_tree_usage); @@ -325,6 +353,17 @@ int cmd_inspect_dump_tree(int argc, char **argv) goto out; } + if (is_spec_key_set) { + root = info->tree_root; + if (IS_ERR(root) || !root) { + ret = root ? PTR_ERR(root) : -1; + error("unable to open %s", argv[optind]); + goto out; + } + + btrfs_search_print_path(info, tree_id, &spec_key); + goto close_root; + } if (block_only) { root = info->chunk_root; leaf = read_tree_block(info, block_only, 0); diff --git a/print-tree.c b/print-tree.c index a09ecfbb28f0..3868e42d5d29 100644 --- a/print-tree.c +++ b/print-tree.c @@ -1459,3 +1459,80 @@ void btrfs_print_tree(struct extent_buffer *eb, int follow) return; } + +void btrfs_search_print_path(struct btrfs_fs_info *fs_info, u64 root_objectid, + struct btrfs_key *skey) +{ + int ret; + int level; + struct btrfs_root *root; + struct btrfs_key key; + struct extent_buffer *next; + struct extent_buffer *eb; + struct btrfs_path path; + + key.objectid = root_objectid; + key.type = BTRFS_ROOT_ITEM_KEY; + key.offset = (u64)-1; + + if (key.objectid == BTRFS_TREE_RELOC_OBJECTID) + root = btrfs_read_fs_root_no_cache(fs_info, &key); + else + root = btrfs_read_fs_root(fs_info, &key); + + if (IS_ERR(root) || !root) { + error("failed to read tree: %lld", key.objectid); + return; + } + + btrfs_init_path(&path); + ret = btrfs_search_slot(NULL, root, skey, &path, 0, 0); + if (ret < 0) { + error("error search the key [%llu, %u, %llu] in root %llu", + skey->objectid, skey->type, skey->offset, root->objectid); + goto out; + } + + if (ret > 0) { + if (path.slots[0] > btrfs_header_nritems(path.nodes[0])) { + ret = btrfs_next_item(root, &path); + if (ret) { + error( + "error search the key [%llu, %u, %llu] in root %llu, no next key", + skey->objectid, skey->type, + skey->offset, root->objectid); + goto out; + } + } + + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + printf("next to search key is [%llu, %u, %llu]\n", + key.objectid, key.type, key.offset); + } + + level = btrfs_header_level(root->node); + for (; level >= 0 ; level--) { + eb = path.nodes[level]; + next = path.nodes[level - 1]; + + btrfs_print_tree(path.nodes[level], 0); + + if (level == 0) + break; + if (btrfs_header_level(next) != btrfs_header_level(eb) - 1) { + warning( + "eb corrupted: parent bytenr %llu slot %d level %d child bytenr %llu level has %d expect %d, skipping the slot", + btrfs_header_bytenr(eb), path.slots[level], + btrfs_header_level(eb), + btrfs_header_bytenr(next), + btrfs_header_level(next), + btrfs_header_level(eb) - 1); + continue; + } + } + +out: + if (root_objectid == BTRFS_TREE_RELOC_OBJECTID) + btrfs_free_fs_root(root); + btrfs_release_path(&path); +} diff --git a/print-tree.h b/print-tree.h index 62667d7f6195..c7694596e0ee 100644 --- a/print-tree.h +++ b/print-tree.h @@ -26,4 +26,6 @@ void print_chunk_item(struct extent_buffer *eb, struct btrfs_chunk *chunk); void print_extent_item(struct extent_buffer *eb, int slot, int metadata); void print_objectid(FILE *stream, u64 objectid, u8 type); void print_key_type(FILE *stream, u64 objectid, u8 type); +void btrfs_search_print_path(struct btrfs_fs_info *info, u64 root_objectid, + struct btrfs_key *key); #endif
For big filesystems, there are many items in trees(extent tree specially). For example, to dump one extent, we usually dump extent tree then pipe result to grep. The time-consuming part is that dump tree traverses items. And it eats cpu and memory too. This patch introduces an option '-k u64,u8,u64' for users(developers more likely) to appoint a specific key to search and dump, then the path from root node down the leaf will be dumped. The search of the key costs most time of this way. Signed-off-by: Su Yue <suy.fnst@cn.fujitsu.com> --- Change log: v2: Rename btrfs_search_spec_key() to btrfs_search_print_path(). Rename is_key_spec to is_spec_key_set. Move btrfs_search_print_path() after open_ctree(), call it only once. Remove duplicate code and replaced with btrfs_print_tree(). Modify the usage about '-k'. All are suggested by Qu Wenruo. cmds-inspect-dump-tree.c | 41 ++++++++++++++++++++- print-tree.c | 77 ++++++++++++++++++++++++++++++++++++++++ print-tree.h | 2 ++ 3 files changed, 119 insertions(+), 1 deletion(-)