On 2018年07月16日 13:55, 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(developer 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.

Indeed a pretty useful debug oriented feature.

> 
> Signed-off-by: Su Yue <[email protected]>
> ---
> In my experiment, not usual case though.
> Image was provided by Chris Murphy, thanks.
> 
> #btrfs filesystem df /mnt
> Data, single: total=767.00GiB, used=766.58GiB
> System, DUP: total=32.00MiB, used=112.00KiB
> Metadata, DUP: total=5.50GiB, used=4.69GiB
> Metadata, single: total=16.00MiB, used=0.00B
> GlobalReserve, single: total=512.00MiB, used=0.00B
> 
> Before:
> #time bash -c 'btrfs inspect-internal dump-tree -t 2 ~/test/test.img | grep 
> "1295194308608" >> /dev/null'
> real    0m34.024s
> user    0m3.269s
> sys     0m4.047s
> 
> Patched and use -k:
> #time bash -c './btrfs inspect-internal dump-tree -t 2  -k 1295194308608,0,0 
> ~/test/test.img >> /dev/null'
> 
> real    0m1.408s
> user    0m0.006s
> sys     0m0.014s
> 
> The new way is 34 times faster than 'grep' way, uses less memory and
> cpu, and it prints path useful for debug.
> 
> ---
>  cmds-inspect-dump-tree.c |  54 +++++++++++++++++--
>  print-tree.c             | 112 +++++++++++++++++++++++++++++++++++++++
>  print-tree.h             |   2 +
>  3 files changed, 163 insertions(+), 5 deletions(-)
> 
> diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
> index c8acd55a0c3a..2a1a40c8270d 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",

Shouldn't it conflict with -b and -r?

>       "--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_key_spec = false;

What about @key_set? @is_key_spec looks a little strange here.

>  
>       /*
>        * 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'},

Small alignment mismatch, missing a space before "key".

>                       { 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_key_spec = 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_key_spec)
> +             usage(cmd_inspect_dump_tree_usage);
> +
>       if (check_argc_exact(argc - optind, 1))
>               usage(cmd_inspect_dump_tree_usage);
>  
> @@ -398,7 +426,11 @@ again:
>                       goto close_root;
>               }
>               printf("root tree\n");
> -             btrfs_print_tree(info->tree_root->node, 1);
> +             if (is_key_spec)
> +                     btrfs_print_spec_key(info, BTRFS_ROOT_TREE_OBJECTID,
> +                                          &spec_key);
> +             else
> +                     btrfs_print_tree(info->tree_root->node, 1);
>               goto close_root;
>       }
>  
> @@ -408,7 +440,11 @@ again:
>                       goto close_root;
>               }
>               printf("chunk tree\n");
> -             btrfs_print_tree(info->chunk_root->node, 1);
> +             if (is_key_spec)
> +                     btrfs_print_spec_key(info, BTRFS_CHUNK_TREE_OBJECTID,
> +                                          &spec_key);
> +             else
> +                     btrfs_print_tree(info->chunk_root->node, 1);
>               goto close_root;
>       }
>  

I really don't thing we need to put btrfS_print_spec_key() here.

It's in fact a special case, no need to nest it into the loop.
Not to mention it appears 3 times in the main loop.

> @@ -418,7 +454,11 @@ again:
>                       goto close_root;
>               }
>               printf("log root tree\n");
> -             btrfs_print_tree(info->log_root_tree->node, 1);
> +             if (is_key_spec)
> +                     btrfs_print_spec_key(info, BTRFS_TREE_LOG_OBJECTID,
> +                                          &spec_key);
> +             else
> +                     btrfs_print_tree(info->log_root_tree->node, 1);
>               goto close_root;
>       }
>  
> @@ -564,7 +604,11 @@ again:
>                                              btrfs_header_level(buf));
>                               } else {
>                                       printf(" \n");
> -                                     btrfs_print_tree(buf, 1);
> +                                     if (is_key_spec)
> +                                             btrfs_print_spec_key(info,
> +                                                  found_key.objectid, 
> &spec_key);
> +                                     else
> +                                             btrfs_print_tree(buf, 1);
>                               }
>                       }
>                       free_extent_buffer(buf);
> diff --git a/print-tree.c b/print-tree.c
> index a09ecfbb28f0..f3a19fed8591 100644
> --- a/print-tree.c
> +++ b/print-tree.c
> @@ -1459,3 +1459,115 @@ void btrfs_print_tree(struct extent_buffer *eb, int 
> follow)
>  
>       return;
>  }
> +
> +void btrfs_print_spec_key(struct btrfs_fs_info *fs_info, u64 root_objectid,
> +                       struct btrfs_key *skey)
> +{
> +     int i;
> +     u32 nr;
> +     u32 ptr_num;
> +     int ret;
> +     int level;
> +     struct btrfs_root *root;
> +     struct btrfs_disk_key disk_key;
> +     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];
> +             nr = btrfs_header_nritems(eb);

In fact, btrfs_print_tree() could replace all the following code.
Which should further shrink the code size.

Thanks,
Qu

> +             if (btrfs_is_leaf(eb)) {
> +                     btrfs_print_leaf(eb);
> +                     goto out;
> +             }
> +
> +             /* We are crossing eb boundary, this node must be corrupted */
> +             if (nr > BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb))
> +                     warning(
> +             "node nr_items corrupted, has %u limit %u, continue anyway",
> +                             nr, BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb));
> +             printf(
> +             "node %llu level %d items %d free %u generation %llu owner ",
> +                    (unsigned long long)eb->start,
> +                    btrfs_header_level(eb), nr,
> +                    (u32)BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb) - nr,
> +                    (unsigned long long)btrfs_header_generation(eb));
> +             print_objectid(stdout, btrfs_header_owner(eb), 0);
> +             printf("\n");
> +             print_uuids(eb);
> +             fflush(stdout);
> +             ptr_num = BTRFS_NODEPTRS_PER_EXTENT_BUFFER(eb);
> +             for (i = 0; i < nr && i < ptr_num; i++) {
> +                     u64 blocknr = btrfs_node_blockptr(eb, i);
> +
> +                     btrfs_node_key(eb, &disk_key, i);
> +                     btrfs_disk_key_to_cpu(&key, &disk_key);
> +                     printf("\t");
> +                     btrfs_print_key(&disk_key);
> +                     printf(" block %llu (%llu) gen %llu\n",
> +                            (unsigned long long)blocknr,
> +                            (unsigned long long)blocknr / eb->len,
> +                            (unsigned long 
> long)btrfs_node_ptr_generation(eb, i));
> +                     fflush(stdout);
> +             }
> +
> +             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), i,
> +                             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..94f231875a6c 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_print_spec_key(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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to