diff mbox series

[bpf-next,v4,3/4] bpftool: btf: Support dumping a specific types from file

Message ID 5ec7617fd9c28ff721947aceb80937dc10fca770.1734052995.git.dxu@dxuuu.xyz (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpftool: btf: Support dumping a single type from file | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/build_tools success Errors and warnings before: 0 (+0) this patch: 0 (+0)
netdev/cc_maintainers warning 1 maintainers not CCed: rameezrehman408@hotmail.com
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: line length of 85 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-11 success Logs for aarch64-gcc / veristat-meta
bpf/vmtest-bpf-next-VM_Test-18 success Logs for s390x-gcc / veristat-meta
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-19 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-gcc / veristat-kernel / x86_64-gcc veristat_kernel
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-17 / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-17 / veristat-meta
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-44 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-45 success Logs for x86_64-llvm-18 / veristat-kernel
bpf/vmtest-bpf-next-VM_Test-46 success Logs for x86_64-llvm-18 / veristat-meta
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-gcc / veristat-meta / x86_64-gcc veristat_meta
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-43 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc

Commit Message

Daniel Xu Dec. 13, 2024, 1:24 a.m. UTC
Some projects, for example xdp-tools [0], prefer to check in a minimized
vmlinux.h rather than the complete file which can get rather large.

However, when you try to add a minimized version of a complex struct (eg
struct xfrm_state), things can get quite complex if you're trying to
manually untangle and deduplicate the dependencies.

This commit teaches bpftool to do a minimized dump of a specific types by
providing a optional root_id argument(s).

Example usage:

    $ ./bpftool btf dump file ~/dev/linux/vmlinux | rg "STRUCT 'xfrm_state'"
    [12643] STRUCT 'xfrm_state' size=912 vlen=58

    $ ./bpftool btf dump file ~/dev/linux/vmlinux root_id 12643 format c
    #ifndef __VMLINUX_H__
    #define __VMLINUX_H__

    [..]

    struct xfrm_type_offload;

    struct xfrm_sec_ctx;

    struct xfrm_state {
            possible_net_t xs_net;
            union {
                    struct hlist_node gclist;
                    struct hlist_node bydst;
            };
            union {
                    struct hlist_node dev_gclist;
                    struct hlist_node bysrc;
            };
            struct hlist_node byspi;
    [..]

[0]: https://github.com/xdp-project/xdp-tools/blob/master/headers/bpf/vmlinux.h

Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
---
 .../bpf/bpftool/Documentation/bpftool-btf.rst |  8 +++-
 tools/bpf/bpftool/btf.c                       | 39 ++++++++++++++++++-
 2 files changed, 43 insertions(+), 4 deletions(-)

Comments

Quentin Monnet Dec. 13, 2024, 3:17 p.m. UTC | #1
2024-12-12 18:24 UTC-0700 ~ Daniel Xu <dxu@dxuuu.xyz>
> Some projects, for example xdp-tools [0], prefer to check in a minimized
> vmlinux.h rather than the complete file which can get rather large.
> 
> However, when you try to add a minimized version of a complex struct (eg
> struct xfrm_state), things can get quite complex if you're trying to
> manually untangle and deduplicate the dependencies.
> 
> This commit teaches bpftool to do a minimized dump of a specific types by
> providing a optional root_id argument(s).
> 
> Example usage:
> 
>     $ ./bpftool btf dump file ~/dev/linux/vmlinux | rg "STRUCT 'xfrm_state'"
>     [12643] STRUCT 'xfrm_state' size=912 vlen=58
> 
>     $ ./bpftool btf dump file ~/dev/linux/vmlinux root_id 12643 format c
>     #ifndef __VMLINUX_H__
>     #define __VMLINUX_H__
> 
>     [..]
> 
>     struct xfrm_type_offload;
> 
>     struct xfrm_sec_ctx;
> 
>     struct xfrm_state {
>             possible_net_t xs_net;
>             union {
>                     struct hlist_node gclist;
>                     struct hlist_node bydst;
>             };
>             union {
>                     struct hlist_node dev_gclist;
>                     struct hlist_node bysrc;
>             };
>             struct hlist_node byspi;
>     [..]
> 
> [0]: https://github.com/xdp-project/xdp-tools/blob/master/headers/bpf/vmlinux.h
> 
> Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
> ---
>  .../bpf/bpftool/Documentation/bpftool-btf.rst |  8 +++-
>  tools/bpf/bpftool/btf.c                       | 39 ++++++++++++++++++-
>  2 files changed, 43 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> index 245569f43035..dbe6d6d94e4c 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> @@ -24,7 +24,7 @@ BTF COMMANDS
>  =============
>  
>  | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
> -| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
> +| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
>  | **bpftool** **btf help**
>  |
>  | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
> @@ -43,7 +43,7 @@ bpftool btf { show | list } [id *BTF_ID*]
>      that hold open file descriptors (FDs) against BTF objects. On such kernels
>      bpftool will automatically emit this information as well.
>  
> -bpftool btf dump *BTF_SRC* [format *FORMAT*]
> +bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
>      Dump BTF entries from a given *BTF_SRC*.
>  
>      When **id** is specified, BTF object with that ID will be loaded and all
> @@ -67,6 +67,10 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*]
>      formatting, the output is sorted by default. Use the **unsorted** option
>      to avoid sorting the output.
>  
> +    **root_id** option can be used to filter a dump to a single type and all
> +    its dependent types. It cannot be used with any other types of filtering.
> +    It can be passed multiple times to dump multiple types.
> +
>  bpftool btf help
>      Print short help message.
>  
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index 3e995faf9efa..2636655ac180 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
> @@ -27,6 +27,8 @@
>  #define KFUNC_DECL_TAG		"bpf_kfunc"
>  #define FASTCALL_DECL_TAG	"bpf_fastcall"
>  
> +#define MAX_ROOT_IDS		16
> +
>  static const char * const btf_kind_str[NR_BTF_KINDS] = {
>  	[BTF_KIND_UNKN]		= "UNKNOWN",
>  	[BTF_KIND_INT]		= "INT",
> @@ -880,7 +882,8 @@ static int do_dump(int argc, char **argv)
>  {
>  	bool dump_c = false, sort_dump_c = true;
>  	struct btf *btf = NULL, *base = NULL;
> -	__u32 root_type_ids[2];
> +	__u32 root_type_ids[MAX_ROOT_IDS];
> +	bool have_id_filtering;
>  	int root_type_cnt = 0;
>  	__u32 btf_id = -1;
>  	const char *src;
> @@ -974,6 +977,8 @@ static int do_dump(int argc, char **argv)
>  		goto done;
>  	}
>  
> +	have_id_filtering = !!root_type_cnt;
> +
>  	while (argc) {
>  		if (is_prefix(*argv, "format")) {
>  			NEXT_ARG();
> @@ -993,6 +998,36 @@ static int do_dump(int argc, char **argv)
>  				goto done;
>  			}
>  			NEXT_ARG();
> +		} else if (is_prefix(*argv, "root_id")) {
> +			__u32 root_id;
> +			char *end;
> +
> +			if (have_id_filtering) {
> +				p_err("cannot use root_id with other type filtering");
> +				err = -EINVAL;
> +				goto done;
> +			} else if (root_type_cnt == MAX_ROOT_IDS) {
> +				p_err("only %d root_id are supported", MAX_ROOT_IDS);


I doubt users will often reach this limit, but if they do, the message
can be confusing, because MAX_ROOT_IDS also accounts for root_type_ids[]
cells used when we pass map arguments ("key" or "value" or "kv"), so you
could pass 15 "root_id" on the command line and get a message telling
only 16 are supported.

Maybe add a counter to tell how many were defined from the rest of the
command line, and adjust the value in the error message?


> +				err = -E2BIG;
> +				goto done;
> +			}
> +
> +			NEXT_ARG();
> +			root_id = strtoul(*argv, &end, 0);
> +			if (*end) {
> +				err = -1;
> +				p_err("can't parse %s as root ID", *argv);
> +				goto done;
> +			}
> +			for (i = 0; i < root_type_cnt; i++) {
> +				if (root_type_ids[i] == root_id) {
> +					err = -EINVAL;
> +					p_err("duplicate root_id %d supplied", root_id);
> +					goto done;
> +				}
> +			}
> +			root_type_ids[root_type_cnt++] = root_id;
> +			NEXT_ARG();
>  		} else if (is_prefix(*argv, "unsorted")) {
>  			sort_dump_c = false;
>  			NEXT_ARG();
> @@ -1403,7 +1438,7 @@ static int do_help(int argc, char **argv)
>  
>  	fprintf(stderr,
>  		"Usage: %1$s %2$s { show | list } [id BTF_ID]\n"
> -		"       %1$s %2$s dump BTF_SRC [format FORMAT]\n"
> +		"       %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
>  		"       %1$s %2$s help\n"
>  		"\n"
>  		"       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
Daniel Xu Dec. 13, 2024, 4:45 p.m. UTC | #2
Hi Quentin,

On Fri, Dec 13, 2024 at 03:17:36PM GMT, Quentin Monnet wrote:
> 2024-12-12 18:24 UTC-0700 ~ Daniel Xu <dxu@dxuuu.xyz>
> > Some projects, for example xdp-tools [0], prefer to check in a minimized
> > vmlinux.h rather than the complete file which can get rather large.
> > 
> > However, when you try to add a minimized version of a complex struct (eg
> > struct xfrm_state), things can get quite complex if you're trying to
> > manually untangle and deduplicate the dependencies.
> > 
> > This commit teaches bpftool to do a minimized dump of a specific types by
> > providing a optional root_id argument(s).
> > 
> > Example usage:
> > 
> >     $ ./bpftool btf dump file ~/dev/linux/vmlinux | rg "STRUCT 'xfrm_state'"
> >     [12643] STRUCT 'xfrm_state' size=912 vlen=58
> > 
> >     $ ./bpftool btf dump file ~/dev/linux/vmlinux root_id 12643 format c
> >     #ifndef __VMLINUX_H__
> >     #define __VMLINUX_H__
> > 
> >     [..]
> > 
> >     struct xfrm_type_offload;
> > 
> >     struct xfrm_sec_ctx;
> > 
> >     struct xfrm_state {
> >             possible_net_t xs_net;
> >             union {
> >                     struct hlist_node gclist;
> >                     struct hlist_node bydst;
> >             };
> >             union {
> >                     struct hlist_node dev_gclist;
> >                     struct hlist_node bysrc;
> >             };
> >             struct hlist_node byspi;
> >     [..]
> > 
> > [0]: https://github.com/xdp-project/xdp-tools/blob/master/headers/bpf/vmlinux.h
> > 
> > Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
> > ---
> >  .../bpf/bpftool/Documentation/bpftool-btf.rst |  8 +++-
> >  tools/bpf/bpftool/btf.c                       | 39 ++++++++++++++++++-
> >  2 files changed, 43 insertions(+), 4 deletions(-)
> > 
> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> > index 245569f43035..dbe6d6d94e4c 100644
> > --- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> > +++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> > @@ -24,7 +24,7 @@ BTF COMMANDS
> >  =============
> >  
> >  | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
> > -| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
> > +| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
> >  | **bpftool** **btf help**
> >  |
> >  | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
> > @@ -43,7 +43,7 @@ bpftool btf { show | list } [id *BTF_ID*]
> >      that hold open file descriptors (FDs) against BTF objects. On such kernels
> >      bpftool will automatically emit this information as well.
> >  
> > -bpftool btf dump *BTF_SRC* [format *FORMAT*]
> > +bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
> >      Dump BTF entries from a given *BTF_SRC*.
> >  
> >      When **id** is specified, BTF object with that ID will be loaded and all
> > @@ -67,6 +67,10 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*]
> >      formatting, the output is sorted by default. Use the **unsorted** option
> >      to avoid sorting the output.
> >  
> > +    **root_id** option can be used to filter a dump to a single type and all
> > +    its dependent types. It cannot be used with any other types of filtering.
> > +    It can be passed multiple times to dump multiple types.
> > +
> >  bpftool btf help
> >      Print short help message.
> >  
> > diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> > index 3e995faf9efa..2636655ac180 100644
> > --- a/tools/bpf/bpftool/btf.c
> > +++ b/tools/bpf/bpftool/btf.c
> > @@ -27,6 +27,8 @@
> >  #define KFUNC_DECL_TAG		"bpf_kfunc"
> >  #define FASTCALL_DECL_TAG	"bpf_fastcall"
> >  
> > +#define MAX_ROOT_IDS		16
> > +
> >  static const char * const btf_kind_str[NR_BTF_KINDS] = {
> >  	[BTF_KIND_UNKN]		= "UNKNOWN",
> >  	[BTF_KIND_INT]		= "INT",
> > @@ -880,7 +882,8 @@ static int do_dump(int argc, char **argv)
> >  {
> >  	bool dump_c = false, sort_dump_c = true;
> >  	struct btf *btf = NULL, *base = NULL;
> > -	__u32 root_type_ids[2];
> > +	__u32 root_type_ids[MAX_ROOT_IDS];
> > +	bool have_id_filtering;
> >  	int root_type_cnt = 0;
> >  	__u32 btf_id = -1;
> >  	const char *src;
> > @@ -974,6 +977,8 @@ static int do_dump(int argc, char **argv)
> >  		goto done;
> >  	}
> >  
> > +	have_id_filtering = !!root_type_cnt;
> > +
> >  	while (argc) {
> >  		if (is_prefix(*argv, "format")) {
> >  			NEXT_ARG();
> > @@ -993,6 +998,36 @@ static int do_dump(int argc, char **argv)
> >  				goto done;
> >  			}
> >  			NEXT_ARG();
> > +		} else if (is_prefix(*argv, "root_id")) {
> > +			__u32 root_id;
> > +			char *end;
> > +
> > +			if (have_id_filtering) {
> > +				p_err("cannot use root_id with other type filtering");
> > +				err = -EINVAL;
> > +				goto done;
> > +			} else if (root_type_cnt == MAX_ROOT_IDS) {
> > +				p_err("only %d root_id are supported", MAX_ROOT_IDS);
> 
> 
> I doubt users will often reach this limit, but if they do, the message
> can be confusing, because MAX_ROOT_IDS also accounts for root_type_ids[]
> cells used when we pass map arguments ("key" or "value" or "kv"), so you
> could pass 15 "root_id" on the command line and get a message telling
> only 16 are supported.
> 
> Maybe add a counter to tell how many were defined from the rest of the
> command line, and adjust the value in the error message?

The above `if (have_id_filtering)` check prevents mixing key/value/kv
map args with root_id. That ought to prevent overcounting, right?

Thanks,
Daniel
Quentin Monnet Dec. 13, 2024, 4:55 p.m. UTC | #3
2024-12-13 09:45 UTC-0700 ~ Daniel Xu <dxu@dxuuu.xyz>
> Hi Quentin,
> 
> On Fri, Dec 13, 2024 at 03:17:36PM GMT, Quentin Monnet wrote:
>> 2024-12-12 18:24 UTC-0700 ~ Daniel Xu <dxu@dxuuu.xyz>
>>> Some projects, for example xdp-tools [0], prefer to check in a minimized
>>> vmlinux.h rather than the complete file which can get rather large.
>>>
>>> However, when you try to add a minimized version of a complex struct (eg
>>> struct xfrm_state), things can get quite complex if you're trying to
>>> manually untangle and deduplicate the dependencies.
>>>
>>> This commit teaches bpftool to do a minimized dump of a specific types by
>>> providing a optional root_id argument(s).
>>>
>>> Example usage:
>>>
>>>     $ ./bpftool btf dump file ~/dev/linux/vmlinux | rg "STRUCT 'xfrm_state'"
>>>     [12643] STRUCT 'xfrm_state' size=912 vlen=58
>>>
>>>     $ ./bpftool btf dump file ~/dev/linux/vmlinux root_id 12643 format c
>>>     #ifndef __VMLINUX_H__
>>>     #define __VMLINUX_H__
>>>
>>>     [..]
>>>
>>>     struct xfrm_type_offload;
>>>
>>>     struct xfrm_sec_ctx;
>>>
>>>     struct xfrm_state {
>>>             possible_net_t xs_net;
>>>             union {
>>>                     struct hlist_node gclist;
>>>                     struct hlist_node bydst;
>>>             };
>>>             union {
>>>                     struct hlist_node dev_gclist;
>>>                     struct hlist_node bysrc;
>>>             };
>>>             struct hlist_node byspi;
>>>     [..]
>>>
>>> [0]: https://github.com/xdp-project/xdp-tools/blob/master/headers/bpf/vmlinux.h
>>>
>>> Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
>>> ---
>>>  .../bpf/bpftool/Documentation/bpftool-btf.rst |  8 +++-
>>>  tools/bpf/bpftool/btf.c                       | 39 ++++++++++++++++++-
>>>  2 files changed, 43 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
>>> index 245569f43035..dbe6d6d94e4c 100644
>>> --- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
>>> +++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
>>> @@ -24,7 +24,7 @@ BTF COMMANDS
>>>  =============
>>>  
>>>  | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
>>> -| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
>>> +| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
>>>  | **bpftool** **btf help**
>>>  |
>>>  | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
>>> @@ -43,7 +43,7 @@ bpftool btf { show | list } [id *BTF_ID*]
>>>      that hold open file descriptors (FDs) against BTF objects. On such kernels
>>>      bpftool will automatically emit this information as well.
>>>  
>>> -bpftool btf dump *BTF_SRC* [format *FORMAT*]
>>> +bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
>>>      Dump BTF entries from a given *BTF_SRC*.
>>>  
>>>      When **id** is specified, BTF object with that ID will be loaded and all
>>> @@ -67,6 +67,10 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*]
>>>      formatting, the output is sorted by default. Use the **unsorted** option
>>>      to avoid sorting the output.
>>>  
>>> +    **root_id** option can be used to filter a dump to a single type and all
>>> +    its dependent types. It cannot be used with any other types of filtering.
>>> +    It can be passed multiple times to dump multiple types.
>>> +
>>>  bpftool btf help
>>>      Print short help message.
>>>  
>>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
>>> index 3e995faf9efa..2636655ac180 100644
>>> --- a/tools/bpf/bpftool/btf.c
>>> +++ b/tools/bpf/bpftool/btf.c
>>> @@ -27,6 +27,8 @@
>>>  #define KFUNC_DECL_TAG		"bpf_kfunc"
>>>  #define FASTCALL_DECL_TAG	"bpf_fastcall"
>>>  
>>> +#define MAX_ROOT_IDS		16
>>> +
>>>  static const char * const btf_kind_str[NR_BTF_KINDS] = {
>>>  	[BTF_KIND_UNKN]		= "UNKNOWN",
>>>  	[BTF_KIND_INT]		= "INT",
>>> @@ -880,7 +882,8 @@ static int do_dump(int argc, char **argv)
>>>  {
>>>  	bool dump_c = false, sort_dump_c = true;
>>>  	struct btf *btf = NULL, *base = NULL;
>>> -	__u32 root_type_ids[2];
>>> +	__u32 root_type_ids[MAX_ROOT_IDS];
>>> +	bool have_id_filtering;
>>>  	int root_type_cnt = 0;
>>>  	__u32 btf_id = -1;
>>>  	const char *src;
>>> @@ -974,6 +977,8 @@ static int do_dump(int argc, char **argv)
>>>  		goto done;
>>>  	}
>>>  
>>> +	have_id_filtering = !!root_type_cnt;
>>> +
>>>  	while (argc) {
>>>  		if (is_prefix(*argv, "format")) {
>>>  			NEXT_ARG();
>>> @@ -993,6 +998,36 @@ static int do_dump(int argc, char **argv)
>>>  				goto done;
>>>  			}
>>>  			NEXT_ARG();
>>> +		} else if (is_prefix(*argv, "root_id")) {
>>> +			__u32 root_id;
>>> +			char *end;
>>> +
>>> +			if (have_id_filtering) {
>>> +				p_err("cannot use root_id with other type filtering");
>>> +				err = -EINVAL;
>>> +				goto done;
>>> +			} else if (root_type_cnt == MAX_ROOT_IDS) {
>>> +				p_err("only %d root_id are supported", MAX_ROOT_IDS);
>>
>>
>> I doubt users will often reach this limit, but if they do, the message
>> can be confusing, because MAX_ROOT_IDS also accounts for root_type_ids[]
>> cells used when we pass map arguments ("key" or "value" or "kv"), so you
>> could pass 15 "root_id" on the command line and get a message telling
>> only 16 are supported.
>>
>> Maybe add a counter to tell how many were defined from the rest of the
>> command line, and adjust the value in the error message?
> 
> The above `if (have_id_filtering)` check prevents mixing key/value/kv
> map args with root_id. That ought to prevent overcounting, right?

Ah, you're right, you even mentioned it in the docs, sorry. All good on
that side, then.

Regarding the restriction, would you mind making the mention from the
man page a bit more explicit, please? Something along:

    [...] It cannot be used with any other types of filtering, such as
    the "key", "value", or "kv" arguments when dumping BTF for a map.
    **root_id** can be passed multiple times...

Thanks,
Quentin
Daniel Xu Dec. 13, 2024, 6:01 p.m. UTC | #4
On Fri, Dec 13, 2024 at 04:55:34PM GMT, Quentin Monnet wrote:
> 2024-12-13 09:45 UTC-0700 ~ Daniel Xu <dxu@dxuuu.xyz>
> > Hi Quentin,
> > 
> > On Fri, Dec 13, 2024 at 03:17:36PM GMT, Quentin Monnet wrote:
> >> 2024-12-12 18:24 UTC-0700 ~ Daniel Xu <dxu@dxuuu.xyz>
> >>> Some projects, for example xdp-tools [0], prefer to check in a minimized
> >>> vmlinux.h rather than the complete file which can get rather large.
> >>>
> >>> However, when you try to add a minimized version of a complex struct (eg
> >>> struct xfrm_state), things can get quite complex if you're trying to
> >>> manually untangle and deduplicate the dependencies.
> >>>
> >>> This commit teaches bpftool to do a minimized dump of a specific types by
> >>> providing a optional root_id argument(s).
> >>>
> >>> Example usage:
> >>>
> >>>     $ ./bpftool btf dump file ~/dev/linux/vmlinux | rg "STRUCT 'xfrm_state'"
> >>>     [12643] STRUCT 'xfrm_state' size=912 vlen=58
> >>>
> >>>     $ ./bpftool btf dump file ~/dev/linux/vmlinux root_id 12643 format c
> >>>     #ifndef __VMLINUX_H__
> >>>     #define __VMLINUX_H__
> >>>
> >>>     [..]
> >>>
> >>>     struct xfrm_type_offload;
> >>>
> >>>     struct xfrm_sec_ctx;
> >>>
> >>>     struct xfrm_state {
> >>>             possible_net_t xs_net;
> >>>             union {
> >>>                     struct hlist_node gclist;
> >>>                     struct hlist_node bydst;
> >>>             };
> >>>             union {
> >>>                     struct hlist_node dev_gclist;
> >>>                     struct hlist_node bysrc;
> >>>             };
> >>>             struct hlist_node byspi;
> >>>     [..]
> >>>
> >>> [0]: https://github.com/xdp-project/xdp-tools/blob/master/headers/bpf/vmlinux.h
> >>>
> >>> Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
> >>> ---
> >>>  .../bpf/bpftool/Documentation/bpftool-btf.rst |  8 +++-
> >>>  tools/bpf/bpftool/btf.c                       | 39 ++++++++++++++++++-
> >>>  2 files changed, 43 insertions(+), 4 deletions(-)
> >>>
> >>> diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> >>> index 245569f43035..dbe6d6d94e4c 100644
> >>> --- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> >>> +++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
> >>> @@ -24,7 +24,7 @@ BTF COMMANDS
> >>>  =============
> >>>  
> >>>  | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
> >>> -| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
> >>> +| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
> >>>  | **bpftool** **btf help**
> >>>  |
> >>>  | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
> >>> @@ -43,7 +43,7 @@ bpftool btf { show | list } [id *BTF_ID*]
> >>>      that hold open file descriptors (FDs) against BTF objects. On such kernels
> >>>      bpftool will automatically emit this information as well.
> >>>  
> >>> -bpftool btf dump *BTF_SRC* [format *FORMAT*]
> >>> +bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
> >>>      Dump BTF entries from a given *BTF_SRC*.
> >>>  
> >>>      When **id** is specified, BTF object with that ID will be loaded and all
> >>> @@ -67,6 +67,10 @@ bpftool btf dump *BTF_SRC* [format *FORMAT*]
> >>>      formatting, the output is sorted by default. Use the **unsorted** option
> >>>      to avoid sorting the output.
> >>>  
> >>> +    **root_id** option can be used to filter a dump to a single type and all
> >>> +    its dependent types. It cannot be used with any other types of filtering.
> >>> +    It can be passed multiple times to dump multiple types.
> >>> +
> >>>  bpftool btf help
> >>>      Print short help message.
> >>>  
> >>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> >>> index 3e995faf9efa..2636655ac180 100644
> >>> --- a/tools/bpf/bpftool/btf.c
> >>> +++ b/tools/bpf/bpftool/btf.c
> >>> @@ -27,6 +27,8 @@
> >>>  #define KFUNC_DECL_TAG		"bpf_kfunc"
> >>>  #define FASTCALL_DECL_TAG	"bpf_fastcall"
> >>>  
> >>> +#define MAX_ROOT_IDS		16
> >>> +
> >>>  static const char * const btf_kind_str[NR_BTF_KINDS] = {
> >>>  	[BTF_KIND_UNKN]		= "UNKNOWN",
> >>>  	[BTF_KIND_INT]		= "INT",
> >>> @@ -880,7 +882,8 @@ static int do_dump(int argc, char **argv)
> >>>  {
> >>>  	bool dump_c = false, sort_dump_c = true;
> >>>  	struct btf *btf = NULL, *base = NULL;
> >>> -	__u32 root_type_ids[2];
> >>> +	__u32 root_type_ids[MAX_ROOT_IDS];
> >>> +	bool have_id_filtering;
> >>>  	int root_type_cnt = 0;
> >>>  	__u32 btf_id = -1;
> >>>  	const char *src;
> >>> @@ -974,6 +977,8 @@ static int do_dump(int argc, char **argv)
> >>>  		goto done;
> >>>  	}
> >>>  
> >>> +	have_id_filtering = !!root_type_cnt;
> >>> +
> >>>  	while (argc) {
> >>>  		if (is_prefix(*argv, "format")) {
> >>>  			NEXT_ARG();
> >>> @@ -993,6 +998,36 @@ static int do_dump(int argc, char **argv)
> >>>  				goto done;
> >>>  			}
> >>>  			NEXT_ARG();
> >>> +		} else if (is_prefix(*argv, "root_id")) {
> >>> +			__u32 root_id;
> >>> +			char *end;
> >>> +
> >>> +			if (have_id_filtering) {
> >>> +				p_err("cannot use root_id with other type filtering");
> >>> +				err = -EINVAL;
> >>> +				goto done;
> >>> +			} else if (root_type_cnt == MAX_ROOT_IDS) {
> >>> +				p_err("only %d root_id are supported", MAX_ROOT_IDS);
> >>
> >>
> >> I doubt users will often reach this limit, but if they do, the message
> >> can be confusing, because MAX_ROOT_IDS also accounts for root_type_ids[]
> >> cells used when we pass map arguments ("key" or "value" or "kv"), so you
> >> could pass 15 "root_id" on the command line and get a message telling
> >> only 16 are supported.
> >>
> >> Maybe add a counter to tell how many were defined from the rest of the
> >> command line, and adjust the value in the error message?
> > 
> > The above `if (have_id_filtering)` check prevents mixing key/value/kv
> > map args with root_id. That ought to prevent overcounting, right?
> 
> Ah, you're right, you even mentioned it in the docs, sorry. All good on
> that side, then.
> 
> Regarding the restriction, would you mind making the mention from the
> man page a bit more explicit, please? Something along:
> 
>     [...] It cannot be used with any other types of filtering, such as
>     the "key", "value", or "kv" arguments when dumping BTF for a map.
>     **root_id** can be passed multiple times...

Ack, will do.
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/Documentation/bpftool-btf.rst b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
index 245569f43035..dbe6d6d94e4c 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-btf.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-btf.rst
@@ -24,7 +24,7 @@  BTF COMMANDS
 =============
 
 | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*]
-| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*]
+| **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*]
 | **bpftool** **btf help**
 |
 | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* }
@@ -43,7 +43,7 @@  bpftool btf { show | list } [id *BTF_ID*]
     that hold open file descriptors (FDs) against BTF objects. On such kernels
     bpftool will automatically emit this information as well.
 
-bpftool btf dump *BTF_SRC* [format *FORMAT*]
+bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*]
     Dump BTF entries from a given *BTF_SRC*.
 
     When **id** is specified, BTF object with that ID will be loaded and all
@@ -67,6 +67,10 @@  bpftool btf dump *BTF_SRC* [format *FORMAT*]
     formatting, the output is sorted by default. Use the **unsorted** option
     to avoid sorting the output.
 
+    **root_id** option can be used to filter a dump to a single type and all
+    its dependent types. It cannot be used with any other types of filtering.
+    It can be passed multiple times to dump multiple types.
+
 bpftool btf help
     Print short help message.
 
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 3e995faf9efa..2636655ac180 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -27,6 +27,8 @@ 
 #define KFUNC_DECL_TAG		"bpf_kfunc"
 #define FASTCALL_DECL_TAG	"bpf_fastcall"
 
+#define MAX_ROOT_IDS		16
+
 static const char * const btf_kind_str[NR_BTF_KINDS] = {
 	[BTF_KIND_UNKN]		= "UNKNOWN",
 	[BTF_KIND_INT]		= "INT",
@@ -880,7 +882,8 @@  static int do_dump(int argc, char **argv)
 {
 	bool dump_c = false, sort_dump_c = true;
 	struct btf *btf = NULL, *base = NULL;
-	__u32 root_type_ids[2];
+	__u32 root_type_ids[MAX_ROOT_IDS];
+	bool have_id_filtering;
 	int root_type_cnt = 0;
 	__u32 btf_id = -1;
 	const char *src;
@@ -974,6 +977,8 @@  static int do_dump(int argc, char **argv)
 		goto done;
 	}
 
+	have_id_filtering = !!root_type_cnt;
+
 	while (argc) {
 		if (is_prefix(*argv, "format")) {
 			NEXT_ARG();
@@ -993,6 +998,36 @@  static int do_dump(int argc, char **argv)
 				goto done;
 			}
 			NEXT_ARG();
+		} else if (is_prefix(*argv, "root_id")) {
+			__u32 root_id;
+			char *end;
+
+			if (have_id_filtering) {
+				p_err("cannot use root_id with other type filtering");
+				err = -EINVAL;
+				goto done;
+			} else if (root_type_cnt == MAX_ROOT_IDS) {
+				p_err("only %d root_id are supported", MAX_ROOT_IDS);
+				err = -E2BIG;
+				goto done;
+			}
+
+			NEXT_ARG();
+			root_id = strtoul(*argv, &end, 0);
+			if (*end) {
+				err = -1;
+				p_err("can't parse %s as root ID", *argv);
+				goto done;
+			}
+			for (i = 0; i < root_type_cnt; i++) {
+				if (root_type_ids[i] == root_id) {
+					err = -EINVAL;
+					p_err("duplicate root_id %d supplied", root_id);
+					goto done;
+				}
+			}
+			root_type_ids[root_type_cnt++] = root_id;
+			NEXT_ARG();
 		} else if (is_prefix(*argv, "unsorted")) {
 			sort_dump_c = false;
 			NEXT_ARG();
@@ -1403,7 +1438,7 @@  static int do_help(int argc, char **argv)
 
 	fprintf(stderr,
 		"Usage: %1$s %2$s { show | list } [id BTF_ID]\n"
-		"       %1$s %2$s dump BTF_SRC [format FORMAT]\n"
+		"       %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
 		"       %1$s %2$s help\n"
 		"\n"
 		"       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"