diff mbox series

[v4,bpf-next,10/17] bpftool: add BTF dump "format meta" to dump header/metadata

Message ID 20231112124834.388735-11-alan.maguire@oracle.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series Add kind layout, CRCs to BTF | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-16 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-16 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-16 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-16 / veristat
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-2 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-4 fail Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-5 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 fail 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-9 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-15 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-16 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 fail Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 fail 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-20 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-21 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-22 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 fail Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-llvm-16 / build / build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-25 fail Logs for x86_64-llvm-16 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 fail Logs for x86_64-llvm-16 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 fail Logs for x86_64-llvm-16 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-16 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-16 / veristat
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-10 fail Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
netdev/series_format fail Series longer than 15 patches (and no cover letter)
netdev/tree_selection success Clearly marked for bpf-next
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: 8 this patch: 8
netdev/cc_maintainers success CCed 13 of 13 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
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: 8 this patch: 8
netdev/checkpatch warning WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 94 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

Commit Message

Alan Maguire Nov. 12, 2023, 12:48 p.m. UTC
Provide a way to dump BTF metadata info via bpftool; this
consists of BTF size, header fields and kind layout info
(if available); for example

$ bpftool btf dump file vmlinux format meta
size 5161076
magic 0xeb9f
version 1
flags 0x1
hdr_len 40
type_len 3036368
type_off 0
str_len 2124588
str_off 3036368
kind_layout_len 80
kind_layout_off 5160956
crc 0x64af901b
base_crc 0x0
kind 0    UNKNOWN    flags 0x0    info_sz 0    elem_sz 0
kind 1    INT        flags 0x0    info_sz 0    elem_sz 0
kind 2    PTR        flags 0x0    info_sz 0    elem_sz 0
kind 3    ARRAY      flags 0x0    info_sz 0    elem_sz 0
kind 4    STRUCT     flags 0x35   info_sz 0    elem_sz 0
...

JSON output is also supported:

$ bpftool -j btf dump file vmlinux format meta
{"size":5161076,"header":{"magic":60319,"version":1,"flags":1,"hdr_len":40,"type_len":3036368,"type_off":0,"str_len":2124588,"str_off":3036368,"kind_layout_len":80,"kind_layout_offset":5160956,"crc":1689227291,"base_crc":0},"kind_layouts":[{"kind":0,"name":"UNKNOWN","flags":0,"info_sz":0,"elem_sz":0},{"kind":1,"name":"INT","flags":0,"info_sz":0,"elem_sz":0},{"kind":2,"name":"PTR","flags":0,"info_sz":0,"elem_sz":0},{"kind":3,"name":"ARRAY","flags":0,"info_sz":0,"elem_sz":0},{"kind":4,"name":"STRUCT","flags":53,"info_sz":0,"elem_sz":0},{"kind":5,"name":"UNION","flags":0,"info_sz":0,"elem_sz":0},{"kind":6,"name":"ENUM","flags":60319,"info_sz":1,"elem_sz":1},{"kind":7,"name":"FWD","flags":40,"info_sz":0,"elem_sz":0},{"kind":8,"name":"TYPEDEF","flags":0,"info_sz":0,"elem_sz":0},{"kind":9,"name":"VOLATILE","flags":0,"info_sz":0,"elem_sz":0},{"kind":10,"name":"CONST","flags":0,"info_sz":0,"elem_sz":0},{"kind":11,"name":"RESTRICT","flags":1,"info_sz":0,"elem_sz":0},{"kind":12,"name":"FUNC","flags":0,"info_sz":0,"elem_sz":0},{"kind":13,"name":"FUNC_PROTO","flags":80,"info_sz":0,"elem_sz":0},{"kind":14,"name":"VAR","flags":0,"info_sz":0,"elem_sz":0},{"kind":15,"name":"DATASEC","flags":0,"info_sz":0,"elem_sz":0},{"kind":16,"name":"FLOAT","flags":53,"info_sz":0,"elem_sz":0},{"kind":17,"name":"DECL_TAG","flags":0,"info_sz":0,"elem_sz":0},{"kind":18,"name":"TYPE_TAG","flags":11441,"info_sz":3,"elem_sz":0},{"kind":19,"name":"ENUM64","flags":0,"info_sz":0,"elem_sz":0}]}

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/bpf/bpftool/bash-completion/bpftool |  2 +-
 tools/bpf/bpftool/btf.c                   | 91 ++++++++++++++++++++++-
 2 files changed, 90 insertions(+), 3 deletions(-)

Comments

Quentin Monnet Nov. 14, 2023, 5:10 a.m. UTC | #1
2023-11-12 12:49 UTC+0000 ~ Alan Maguire <alan.maguire@oracle.com>
> Provide a way to dump BTF metadata info via bpftool; this
> consists of BTF size, header fields and kind layout info
> (if available); for example
> 
> $ bpftool btf dump file vmlinux format meta
> size 5161076
> magic 0xeb9f
> version 1
> flags 0x1
> hdr_len 40
> type_len 3036368
> type_off 0
> str_len 2124588
> str_off 3036368
> kind_layout_len 80
> kind_layout_off 5160956
> crc 0x64af901b
> base_crc 0x0
> kind 0    UNKNOWN    flags 0x0    info_sz 0    elem_sz 0
> kind 1    INT        flags 0x0    info_sz 0    elem_sz 0
> kind 2    PTR        flags 0x0    info_sz 0    elem_sz 0
> kind 3    ARRAY      flags 0x0    info_sz 0    elem_sz 0
> kind 4    STRUCT     flags 0x35   info_sz 0    elem_sz 0
> ...
> 
> JSON output is also supported:
> 
> $ bpftool -j btf dump file vmlinux format meta
> {"size":5161076,"header":{"magic":60319,"version":1,"flags":1,"hdr_len":40,"type_len":3036368,"type_off":0,"str_len":2124588,"str_off":3036368,"kind_layout_len":80,"kind_layout_offset":5160956,"crc":1689227291,"base_crc":0},"kind_layouts":[{"kind":0,"name":"UNKNOWN","flags":0,"info_sz":0,"elem_sz":0},{"kind":1,"name":"INT","flags":0,"info_sz":0,"elem_sz":0},{"kind":2,"name":"PTR","flags":0,"info_sz":0,"elem_sz":0},{"kind":3,"name":"ARRAY","flags":0,"info_sz":0,"elem_sz":0},{"kind":4,"name":"STRUCT","flags":53,"info_sz":0,"elem_sz":0},{"kind":5,"name":"UNION","flags":0,"info_sz":0,"elem_sz":0},{"kind":6,"name":"ENUM","flags":60319,"info_sz":1,"elem_sz":1},{"kind":7,"name":"FWD","flags":40,"info_sz":0,"elem_sz":0},{"kind":8,"name":"TYPEDEF","flags":0,"info_sz":0,"elem_sz":0},{"kind":9,"name":"VOLATILE","flags":0,"info_sz":0,"elem_sz":0},{"kind":10,"name":"CONST","flags":0,"info_sz":0,"elem_sz":0},{"kind":11,"name":"RESTRICT","flags":1,"info_sz":0,"elem_sz":0},{"kind":12,"name":"FUNC","flags":0,"info_sz":0,"elem_sz":0},{"kind":13,"name":"FUNC_PROTO","flags":80,"info_sz":0,"elem_sz":0},{"kind":14,"name":"VAR","flags":0,"info_sz":0,"elem_sz":0},{"kind":15,"name":"DATASEC","flags":0,"info_sz":0,"elem_sz":0},{"kind":16,"name":"FLOAT","flags":53,"info_sz":0,"elem_sz":0},{"kind":17,"name":"DECL_TAG","flags":0,"info_sz":0,"elem_sz":0},{"kind":18,"name":"TYPE_TAG","flags":11441,"info_sz":3,"elem_sz":0},{"kind":19,"name":"ENUM64","flags":0,"info_sz":0,"elem_sz":0}]}
> 
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>  tools/bpf/bpftool/bash-completion/bpftool |  2 +-
>  tools/bpf/bpftool/btf.c                   | 91 ++++++++++++++++++++++-
>  2 files changed, 90 insertions(+), 3 deletions(-)
> 
> diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
> index 6e4f7ce6bc01..157c3afd8247 100644
> --- a/tools/bpf/bpftool/bash-completion/bpftool
> +++ b/tools/bpf/bpftool/bash-completion/bpftool
> @@ -937,7 +937,7 @@ _bpftool()
>                              return 0
>                              ;;
>                          format)
> -                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
> +                            COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
>                              ;;
>                          *)
>                              # emit extra options
> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
> index 91fcb75babe3..208f3a587534 100644
> --- a/tools/bpf/bpftool/btf.c
> +++ b/tools/bpf/bpftool/btf.c
> @@ -504,6 +504,88 @@ static int dump_btf_c(const struct btf *btf,
>  	return err;
>  }
>  
> +static int dump_btf_meta(const struct btf *btf)
> +{
> +	const struct btf_header *hdr;
> +	const struct btf_kind_layout *k;
> +	__u8 i, nr_kinds = 0;
> +	const void *data;
> +	__u32 data_sz;
> +
> +	data = btf__raw_data(btf, &data_sz);
> +	if (!data)
> +		return -ENOMEM;
> +	hdr = data;
> +	if (json_output) {
> +		jsonw_start_object(json_wtr);   /* btf metadata object */

Nit: Please make sure to be consistent when aligning these comments:
there are several occurrences with spaces (here three spaces), several
ones with tabs. For these, I'd prefer tabs to align the start and end
comments for a given object/array, although I don't really using a
single space as well as long as we keep it consistent.

> +		jsonw_uint_field(json_wtr, "size", data_sz);
> +		jsonw_name(json_wtr, "header");
> +		jsonw_start_object(json_wtr);	/* btf header object */
> +		jsonw_uint_field(json_wtr, "magic", hdr->magic);
> +		jsonw_uint_field(json_wtr, "version", hdr->version);
> +		jsonw_uint_field(json_wtr, "flags", hdr->flags);
> +		jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
> +		jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
> +		jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
> +		jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
> +		jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
> +	} else {
> +		printf("size %-10d\n", data_sz);
> +		printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10d\n",
> +		       hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
> +		printf("type_len %-10d\ntype_off %-10d\n", hdr->type_len, hdr->type_off);
> +		printf("str_len %-10d\nstr_off %-10d\n", hdr->str_len, hdr->str_off);
> +	}
> +
> +	if (hdr->hdr_len < sizeof(struct btf_header)) {
> +		if (json_output) {
> +			jsonw_end_object(json_wtr); /* header object */
> +			jsonw_end_object(json_wtr); /* metadata object */

Similarly, can you please keep consistent comment strings? "metadata
object" here vs. "end metadata" below.

> +		}
> +		return 0;
> +	}
> +	if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
> +		k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
> +		nr_kinds = hdr->kind_layout_len / sizeof(*k);
> +	}
> +	if (json_output) {
> +		jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
> +		jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
> +		jsonw_uint_field(json_wtr, "crc", hdr->crc);
> +		jsonw_uint_field(json_wtr, "base_crc", hdr->base_crc);
> +		jsonw_end_object(json_wtr); /* end header object */
> +
> +		if (nr_kinds > 0) {
> +			jsonw_name(json_wtr, "kind_layouts");
> +			jsonw_start_array(json_wtr);
> +			for (i = 0; i < nr_kinds; i++) {
> +				jsonw_start_object(json_wtr);
> +				jsonw_uint_field(json_wtr, "kind", i);
> +				if (i < NR_BTF_KINDS)
> +					jsonw_string_field(json_wtr, "name", btf_kind_str[i]);

I prefer to avoid conditional fields in JSON, especially in an array
it's easier to process the JSON if all items have the same structure.
Would it make sense to keep the "name" field, but use jsonw_null() (or
"UNKNOWN") for the value when there's no name to print?

Thanks,
Quentin
Alan Maguire Nov. 15, 2023, 8:45 a.m. UTC | #2
On 14/11/2023 05:10, Quentin Monnet wrote:
> 2023-11-12 12:49 UTC+0000 ~ Alan Maguire <alan.maguire@oracle.com>
>> Provide a way to dump BTF metadata info via bpftool; this
>> consists of BTF size, header fields and kind layout info
>> (if available); for example
>>
>> $ bpftool btf dump file vmlinux format meta
>> size 5161076
>> magic 0xeb9f
>> version 1
>> flags 0x1
>> hdr_len 40
>> type_len 3036368
>> type_off 0
>> str_len 2124588
>> str_off 3036368
>> kind_layout_len 80
>> kind_layout_off 5160956
>> crc 0x64af901b
>> base_crc 0x0
>> kind 0    UNKNOWN    flags 0x0    info_sz 0    elem_sz 0
>> kind 1    INT        flags 0x0    info_sz 0    elem_sz 0
>> kind 2    PTR        flags 0x0    info_sz 0    elem_sz 0
>> kind 3    ARRAY      flags 0x0    info_sz 0    elem_sz 0
>> kind 4    STRUCT     flags 0x35   info_sz 0    elem_sz 0
>> ...
>>
>> JSON output is also supported:
>>
>> $ bpftool -j btf dump file vmlinux format meta
>> {"size":5161076,"header":{"magic":60319,"version":1,"flags":1,"hdr_len":40,"type_len":3036368,"type_off":0,"str_len":2124588,"str_off":3036368,"kind_layout_len":80,"kind_layout_offset":5160956,"crc":1689227291,"base_crc":0},"kind_layouts":[{"kind":0,"name":"UNKNOWN","flags":0,"info_sz":0,"elem_sz":0},{"kind":1,"name":"INT","flags":0,"info_sz":0,"elem_sz":0},{"kind":2,"name":"PTR","flags":0,"info_sz":0,"elem_sz":0},{"kind":3,"name":"ARRAY","flags":0,"info_sz":0,"elem_sz":0},{"kind":4,"name":"STRUCT","flags":53,"info_sz":0,"elem_sz":0},{"kind":5,"name":"UNION","flags":0,"info_sz":0,"elem_sz":0},{"kind":6,"name":"ENUM","flags":60319,"info_sz":1,"elem_sz":1},{"kind":7,"name":"FWD","flags":40,"info_sz":0,"elem_sz":0},{"kind":8,"name":"TYPEDEF","flags":0,"info_sz":0,"elem_sz":0},{"kind":9,"name":"VOLATILE","flags":0,"info_sz":0,"elem_sz":0},{"kind":10,"name":"CONST","flags":0,"info_sz":0,"elem_sz":0},{"kind":11,"name":"RESTRICT","flags":1,"info_sz":0,"elem_sz":0},{"kind":12,"name":"FUNC","flags":0,"info_sz":0,"elem_sz":0},{"kind":13,"name":"FUNC_PROTO","flags":80,"info_sz":0,"elem_sz":0},{"kind":14,"name":"VAR","flags":0,"info_sz":0,"elem_sz":0},{"kind":15,"name":"DATASEC","flags":0,"info_sz":0,"elem_sz":0},{"kind":16,"name":"FLOAT","flags":53,"info_sz":0,"elem_sz":0},{"kind":17,"name":"DECL_TAG","flags":0,"info_sz":0,"elem_sz":0},{"kind":18,"name":"TYPE_TAG","flags":11441,"info_sz":3,"elem_sz":0},{"kind":19,"name":"ENUM64","flags":0,"info_sz":0,"elem_sz":0}]}
>>
>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>> ---
>>  tools/bpf/bpftool/bash-completion/bpftool |  2 +-
>>  tools/bpf/bpftool/btf.c                   | 91 ++++++++++++++++++++++-
>>  2 files changed, 90 insertions(+), 3 deletions(-)
>>
>> diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
>> index 6e4f7ce6bc01..157c3afd8247 100644
>> --- a/tools/bpf/bpftool/bash-completion/bpftool
>> +++ b/tools/bpf/bpftool/bash-completion/bpftool
>> @@ -937,7 +937,7 @@ _bpftool()
>>                              return 0
>>                              ;;
>>                          format)
>> -                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
>> +                            COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
>>                              ;;
>>                          *)
>>                              # emit extra options
>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
>> index 91fcb75babe3..208f3a587534 100644
>> --- a/tools/bpf/bpftool/btf.c
>> +++ b/tools/bpf/bpftool/btf.c
>> @@ -504,6 +504,88 @@ static int dump_btf_c(const struct btf *btf,
>>  	return err;
>>  }
>>  
>> +static int dump_btf_meta(const struct btf *btf)
>> +{
>> +	const struct btf_header *hdr;
>> +	const struct btf_kind_layout *k;
>> +	__u8 i, nr_kinds = 0;
>> +	const void *data;
>> +	__u32 data_sz;
>> +
>> +	data = btf__raw_data(btf, &data_sz);
>> +	if (!data)
>> +		return -ENOMEM;
>> +	hdr = data;
>> +	if (json_output) {
>> +		jsonw_start_object(json_wtr);   /* btf metadata object */
> 
> Nit: Please make sure to be consistent when aligning these comments:
> there are several occurrences with spaces (here three spaces), several
> ones with tabs. For these, I'd prefer tabs to align the start and end
> comments for a given object/array, although I don't really using a
> single space as well as long as we keep it consistent.
> 
>> +		jsonw_uint_field(json_wtr, "size", data_sz);
>> +		jsonw_name(json_wtr, "header");
>> +		jsonw_start_object(json_wtr);	/* btf header object */
>> +		jsonw_uint_field(json_wtr, "magic", hdr->magic);
>> +		jsonw_uint_field(json_wtr, "version", hdr->version);
>> +		jsonw_uint_field(json_wtr, "flags", hdr->flags);
>> +		jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
>> +		jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
>> +		jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
>> +		jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
>> +		jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
>> +	} else {
>> +		printf("size %-10d\n", data_sz);
>> +		printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10d\n",
>> +		       hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
>> +		printf("type_len %-10d\ntype_off %-10d\n", hdr->type_len, hdr->type_off);
>> +		printf("str_len %-10d\nstr_off %-10d\n", hdr->str_len, hdr->str_off);
>> +	}
>> +
>> +	if (hdr->hdr_len < sizeof(struct btf_header)) {
>> +		if (json_output) {
>> +			jsonw_end_object(json_wtr); /* header object */
>> +			jsonw_end_object(json_wtr); /* metadata object */
> 
> Similarly, can you please keep consistent comment strings? "metadata
> object" here vs. "end metadata" below.
> 

Sure, I'll fix indent consistency/naming and the docs issue in the
next revision. Thanks!

>> +		}
>> +		return 0;
>> +	}
>> +	if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
>> +		k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
>> +		nr_kinds = hdr->kind_layout_len / sizeof(*k);
>> +	}
>> +	if (json_output) {
>> +		jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
>> +		jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
>> +		jsonw_uint_field(json_wtr, "crc", hdr->crc);
>> +		jsonw_uint_field(json_wtr, "base_crc", hdr->base_crc);
>> +		jsonw_end_object(json_wtr); /* end header object */
>> +
>> +		if (nr_kinds > 0) {
>> +			jsonw_name(json_wtr, "kind_layouts");
>> +			jsonw_start_array(json_wtr);
>> +			for (i = 0; i < nr_kinds; i++) {
>> +				jsonw_start_object(json_wtr);
>> +				jsonw_uint_field(json_wtr, "kind", i);
>> +				if (i < NR_BTF_KINDS)
>> +					jsonw_string_field(json_wtr, "name", btf_kind_str[i]);
> 
> I prefer to avoid conditional fields in JSON, especially in an array
> it's easier to process the JSON if all items have the same structure.
> Would it make sense to keep the "name" field, but use jsonw_null() (or
> "UNKNOWN") for the value when there's no name to print?
>

The only thing about UNKNOWN is that there is a BTF_KIND_UNKN that
is displayed as UNKNOWN; what about "?" to be consistent with the
non-json output (or if there's another option of course, we could
use that for both)? Thanks!

Alan
Quentin Monnet Nov. 15, 2023, 2:51 p.m. UTC | #3
On 15 November 2023 03:45:41 GMT-05:00, Alan Maguire <alan.maguire@oracle.com> wrote:
>On 14/11/2023 05:10, Quentin Monnet wrote:
>> 2023-11-12 12:49 UTC+0000 ~ Alan Maguire <alan.maguire@oracle.com>
>>> Provide a way to dump BTF metadata info via bpftool; this
>>> consists of BTF size, header fields and kind layout info
>>> (if available); for example
>>>
>>> $ bpftool btf dump file vmlinux format meta
>>> size 5161076
>>> magic 0xeb9f
>>> version 1
>>> flags 0x1
>>> hdr_len 40
>>> type_len 3036368
>>> type_off 0
>>> str_len 2124588
>>> str_off 3036368
>>> kind_layout_len 80
>>> kind_layout_off 5160956
>>> crc 0x64af901b
>>> base_crc 0x0
>>> kind 0    UNKNOWN    flags 0x0    info_sz 0    elem_sz 0
>>> kind 1    INT        flags 0x0    info_sz 0    elem_sz 0
>>> kind 2    PTR        flags 0x0    info_sz 0    elem_sz 0
>>> kind 3    ARRAY      flags 0x0    info_sz 0    elem_sz 0
>>> kind 4    STRUCT     flags 0x35   info_sz 0    elem_sz 0
>>> ...
>>>
>>> JSON output is also supported:
>>>
>>> $ bpftool -j btf dump file vmlinux format meta
>>> {"size":5161076,"header":{"magic":60319,"version":1,"flags":1,"hdr_len":40,"type_len":3036368,"type_off":0,"str_len":2124588,"str_off":3036368,"kind_layout_len":80,"kind_layout_offset":5160956,"crc":1689227291,"base_crc":0},"kind_layouts":[{"kind":0,"name":"UNKNOWN","flags":0,"info_sz":0,"elem_sz":0},{"kind":1,"name":"INT","flags":0,"info_sz":0,"elem_sz":0},{"kind":2,"name":"PTR","flags":0,"info_sz":0,"elem_sz":0},{"kind":3,"name":"ARRAY","flags":0,"info_sz":0,"elem_sz":0},{"kind":4,"name":"STRUCT","flags":53,"info_sz":0,"elem_sz":0},{"kind":5,"name":"UNION","flags":0,"info_sz":0,"elem_sz":0},{"kind":6,"name":"ENUM","flags":60319,"info_sz":1,"elem_sz":1},{"kind":7,"name":"FWD","flags":40,"info_sz":0,"elem_sz":0},{"kind":8,"name":"TYPEDEF","flags":0,"info_sz":0,"elem_sz":0},{"kind":9,"name":"VOLATILE","flags":0,"info_sz":0,"elem_sz":0},{"kind":10,"name":"CONST","flags":0,"info_sz":0,"elem_sz":0},{"kind":11,"name":"RESTRICT","flags":1,"info_sz":0,"elem_sz":0},{"kind":12,"name":"FUNC","flags":0,"info_sz":0,"elem_sz":0},{"kind":13,"name":"FUNC_PROTO","flags":80,"info_sz":0,"elem_sz":0},{"kind":14,"name":"VAR","flags":0,"info_sz":0,"elem_sz":0},{"kind":15,"name":"DATASEC","flags":0,"info_sz":0,"elem_sz":0},{"kind":16,"name":"FLOAT","flags":53,"info_sz":0,"elem_sz":0},{"kind":17,"name":"DECL_TAG","flags":0,"info_sz":0,"elem_sz":0},{"kind":18,"name":"TYPE_TAG","flags":11441,"info_sz":3,"elem_sz":0},{"kind":19,"name":"ENUM64","flags":0,"info_sz":0,"elem_sz":0}]}
>>>
>>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>>> ---
>>>  tools/bpf/bpftool/bash-completion/bpftool |  2 +-
>>>  tools/bpf/bpftool/btf.c                   | 91 ++++++++++++++++++++++-
>>>  2 files changed, 90 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
>>> index 6e4f7ce6bc01..157c3afd8247 100644
>>> --- a/tools/bpf/bpftool/bash-completion/bpftool
>>> +++ b/tools/bpf/bpftool/bash-completion/bpftool
>>> @@ -937,7 +937,7 @@ _bpftool()
>>>                              return 0
>>>                              ;;
>>>                          format)
>>> -                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
>>> +                            COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
>>>                              ;;
>>>                          *)
>>>                              # emit extra options
>>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
>>> index 91fcb75babe3..208f3a587534 100644
>>> --- a/tools/bpf/bpftool/btf.c
>>> +++ b/tools/bpf/bpftool/btf.c
>>> @@ -504,6 +504,88 @@ static int dump_btf_c(const struct btf *btf,
>>>  	return err;
>>>  }
>>>  
>>> +static int dump_btf_meta(const struct btf *btf)
>>> +{
>>> +	const struct btf_header *hdr;
>>> +	const struct btf_kind_layout *k;
>>> +	__u8 i, nr_kinds = 0;
>>> +	const void *data;
>>> +	__u32 data_sz;
>>> +
>>> +	data = btf__raw_data(btf, &data_sz);
>>> +	if (!data)
>>> +		return -ENOMEM;
>>> +	hdr = data;
>>> +	if (json_output) {
>>> +		jsonw_start_object(json_wtr);   /* btf metadata object */
>> 
>> Nit: Please make sure to be consistent when aligning these comments:
>> there are several occurrences with spaces (here three spaces), several
>> ones with tabs. For these, I'd prefer tabs to align the start and end
>> comments for a given object/array, although I don't really using a
>> single space as well as long as we keep it consistent.
>> 
>>> +		jsonw_uint_field(json_wtr, "size", data_sz);
>>> +		jsonw_name(json_wtr, "header");
>>> +		jsonw_start_object(json_wtr);	/* btf header object */
>>> +		jsonw_uint_field(json_wtr, "magic", hdr->magic);
>>> +		jsonw_uint_field(json_wtr, "version", hdr->version);
>>> +		jsonw_uint_field(json_wtr, "flags", hdr->flags);
>>> +		jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
>>> +		jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
>>> +		jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
>>> +		jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
>>> +		jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
>>> +	} else {
>>> +		printf("size %-10d\n", data_sz);
>>> +		printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10d\n",
>>> +		       hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
>>> +		printf("type_len %-10d\ntype_off %-10d\n", hdr->type_len, hdr->type_off);
>>> +		printf("str_len %-10d\nstr_off %-10d\n", hdr->str_len, hdr->str_off);
>>> +	}
>>> +
>>> +	if (hdr->hdr_len < sizeof(struct btf_header)) {
>>> +		if (json_output) {
>>> +			jsonw_end_object(json_wtr); /* header object */
>>> +			jsonw_end_object(json_wtr); /* metadata object */
>> 
>> Similarly, can you please keep consistent comment strings? "metadata
>> object" here vs. "end metadata" below.
>> 
>
>Sure, I'll fix indent consistency/naming and the docs issue in the
>next revision. Thanks!
>
>>> +		}
>>> +		return 0;
>>> +	}
>>> +	if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
>>> +		k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
>>> +		nr_kinds = hdr->kind_layout_len / sizeof(*k);
>>> +	}
>>> +	if (json_output) {
>>> +		jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
>>> +		jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
>>> +		jsonw_uint_field(json_wtr, "crc", hdr->crc);
>>> +		jsonw_uint_field(json_wtr, "base_crc", hdr->base_crc);
>>> +		jsonw_end_object(json_wtr); /* end header object */
>>> +
>>> +		if (nr_kinds > 0) {
>>> +			jsonw_name(json_wtr, "kind_layouts");
>>> +			jsonw_start_array(json_wtr);
>>> +			for (i = 0; i < nr_kinds; i++) {
>>> +				jsonw_start_object(json_wtr);
>>> +				jsonw_uint_field(json_wtr, "kind", i);
>>> +				if (i < NR_BTF_KINDS)
>>> +					jsonw_string_field(json_wtr, "name", btf_kind_str[i]);
>> 
>> I prefer to avoid conditional fields in JSON, especially in an array
>> it's easier to process the JSON if all items have the same structure.
>> Would it make sense to keep the "name" field, but use jsonw_null() (or
>> "UNKNOWN") for the value when there's no name to print?
>>
>
>The only thing about UNKNOWN is that there is a BTF_KIND_UNKN that
>is displayed as UNKNOWN; what about "?" to be consistent with the
>non-json output (or if there's another option of course, we could
>use that for both)? Thanks!

Right, sorry, I realised just after sending my message.
In that case we could just use a 'null' value in JSON with jsonw_null()? The object '{"name": null}' is valid. The question mark is another possibility but requires comparing strings when parsing the JSON.

Thanks,
Quentin
Alan Maguire Nov. 16, 2023, 9:16 a.m. UTC | #4
On 15/11/2023 14:51, Quentin Monnet wrote:
> 
> 
> On 15 November 2023 03:45:41 GMT-05:00, Alan Maguire <alan.maguire@oracle.com> wrote:
>> On 14/11/2023 05:10, Quentin Monnet wrote:
>>> 2023-11-12 12:49 UTC+0000 ~ Alan Maguire <alan.maguire@oracle.com>
>>>> Provide a way to dump BTF metadata info via bpftool; this
>>>> consists of BTF size, header fields and kind layout info
>>>> (if available); for example
>>>>
>>>> $ bpftool btf dump file vmlinux format meta
>>>> size 5161076
>>>> magic 0xeb9f
>>>> version 1
>>>> flags 0x1
>>>> hdr_len 40
>>>> type_len 3036368
>>>> type_off 0
>>>> str_len 2124588
>>>> str_off 3036368
>>>> kind_layout_len 80
>>>> kind_layout_off 5160956
>>>> crc 0x64af901b
>>>> base_crc 0x0
>>>> kind 0    UNKNOWN    flags 0x0    info_sz 0    elem_sz 0
>>>> kind 1    INT        flags 0x0    info_sz 0    elem_sz 0
>>>> kind 2    PTR        flags 0x0    info_sz 0    elem_sz 0
>>>> kind 3    ARRAY      flags 0x0    info_sz 0    elem_sz 0
>>>> kind 4    STRUCT     flags 0x35   info_sz 0    elem_sz 0
>>>> ...
>>>>
>>>> JSON output is also supported:
>>>>
>>>> $ bpftool -j btf dump file vmlinux format meta
>>>> {"size":5161076,"header":{"magic":60319,"version":1,"flags":1,"hdr_len":40,"type_len":3036368,"type_off":0,"str_len":2124588,"str_off":3036368,"kind_layout_len":80,"kind_layout_offset":5160956,"crc":1689227291,"base_crc":0},"kind_layouts":[{"kind":0,"name":"UNKNOWN","flags":0,"info_sz":0,"elem_sz":0},{"kind":1,"name":"INT","flags":0,"info_sz":0,"elem_sz":0},{"kind":2,"name":"PTR","flags":0,"info_sz":0,"elem_sz":0},{"kind":3,"name":"ARRAY","flags":0,"info_sz":0,"elem_sz":0},{"kind":4,"name":"STRUCT","flags":53,"info_sz":0,"elem_sz":0},{"kind":5,"name":"UNION","flags":0,"info_sz":0,"elem_sz":0},{"kind":6,"name":"ENUM","flags":60319,"info_sz":1,"elem_sz":1},{"kind":7,"name":"FWD","flags":40,"info_sz":0,"elem_sz":0},{"kind":8,"name":"TYPEDEF","flags":0,"info_sz":0,"elem_sz":0},{"kind":9,"name":"VOLATILE","flags":0,"info_sz":0,"elem_sz":0},{"kind":10,"name":"CONST","flags":0,"info_sz":0,"elem_sz":0},{"kind":11,"name":"RESTRICT","flags":1,"info_sz":0,"elem_sz":0},{"kind":12,"name":"FUNC","flags":0,"info_sz":0,"elem_sz":0},{"kind":13,"name":"FUNC_PROTO","flags":80,"info_sz":0,"elem_sz":0},{"kind":14,"name":"VAR","flags":0,"info_sz":0,"elem_sz":0},{"kind":15,"name":"DATASEC","flags":0,"info_sz":0,"elem_sz":0},{"kind":16,"name":"FLOAT","flags":53,"info_sz":0,"elem_sz":0},{"kind":17,"name":"DECL_TAG","flags":0,"info_sz":0,"elem_sz":0},{"kind":18,"name":"TYPE_TAG","flags":11441,"info_sz":3,"elem_sz":0},{"kind":19,"name":"ENUM64","flags":0,"info_sz":0,"elem_sz":0}]}
>>>>
>>>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
>>>> ---
>>>>  tools/bpf/bpftool/bash-completion/bpftool |  2 +-
>>>>  tools/bpf/bpftool/btf.c                   | 91 ++++++++++++++++++++++-
>>>>  2 files changed, 90 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
>>>> index 6e4f7ce6bc01..157c3afd8247 100644
>>>> --- a/tools/bpf/bpftool/bash-completion/bpftool
>>>> +++ b/tools/bpf/bpftool/bash-completion/bpftool
>>>> @@ -937,7 +937,7 @@ _bpftool()
>>>>                              return 0
>>>>                              ;;
>>>>                          format)
>>>> -                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
>>>> +                            COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
>>>>                              ;;
>>>>                          *)
>>>>                              # emit extra options
>>>> diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
>>>> index 91fcb75babe3..208f3a587534 100644
>>>> --- a/tools/bpf/bpftool/btf.c
>>>> +++ b/tools/bpf/bpftool/btf.c
>>>> @@ -504,6 +504,88 @@ static int dump_btf_c(const struct btf *btf,
>>>>  	return err;
>>>>  }
>>>>  
>>>> +static int dump_btf_meta(const struct btf *btf)
>>>> +{
>>>> +	const struct btf_header *hdr;
>>>> +	const struct btf_kind_layout *k;
>>>> +	__u8 i, nr_kinds = 0;
>>>> +	const void *data;
>>>> +	__u32 data_sz;
>>>> +
>>>> +	data = btf__raw_data(btf, &data_sz);
>>>> +	if (!data)
>>>> +		return -ENOMEM;
>>>> +	hdr = data;
>>>> +	if (json_output) {
>>>> +		jsonw_start_object(json_wtr);   /* btf metadata object */
>>>
>>> Nit: Please make sure to be consistent when aligning these comments:
>>> there are several occurrences with spaces (here three spaces), several
>>> ones with tabs. For these, I'd prefer tabs to align the start and end
>>> comments for a given object/array, although I don't really using a
>>> single space as well as long as we keep it consistent.
>>>
>>>> +		jsonw_uint_field(json_wtr, "size", data_sz);
>>>> +		jsonw_name(json_wtr, "header");
>>>> +		jsonw_start_object(json_wtr);	/* btf header object */
>>>> +		jsonw_uint_field(json_wtr, "magic", hdr->magic);
>>>> +		jsonw_uint_field(json_wtr, "version", hdr->version);
>>>> +		jsonw_uint_field(json_wtr, "flags", hdr->flags);
>>>> +		jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
>>>> +		jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
>>>> +		jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
>>>> +		jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
>>>> +		jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
>>>> +	} else {
>>>> +		printf("size %-10d\n", data_sz);
>>>> +		printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10d\n",
>>>> +		       hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
>>>> +		printf("type_len %-10d\ntype_off %-10d\n", hdr->type_len, hdr->type_off);
>>>> +		printf("str_len %-10d\nstr_off %-10d\n", hdr->str_len, hdr->str_off);
>>>> +	}
>>>> +
>>>> +	if (hdr->hdr_len < sizeof(struct btf_header)) {
>>>> +		if (json_output) {
>>>> +			jsonw_end_object(json_wtr); /* header object */
>>>> +			jsonw_end_object(json_wtr); /* metadata object */
>>>
>>> Similarly, can you please keep consistent comment strings? "metadata
>>> object" here vs. "end metadata" below.
>>>
>>
>> Sure, I'll fix indent consistency/naming and the docs issue in the
>> next revision. Thanks!
>>
>>>> +		}
>>>> +		return 0;
>>>> +	}
>>>> +	if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
>>>> +		k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
>>>> +		nr_kinds = hdr->kind_layout_len / sizeof(*k);
>>>> +	}
>>>> +	if (json_output) {
>>>> +		jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
>>>> +		jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
>>>> +		jsonw_uint_field(json_wtr, "crc", hdr->crc);
>>>> +		jsonw_uint_field(json_wtr, "base_crc", hdr->base_crc);
>>>> +		jsonw_end_object(json_wtr); /* end header object */
>>>> +
>>>> +		if (nr_kinds > 0) {
>>>> +			jsonw_name(json_wtr, "kind_layouts");
>>>> +			jsonw_start_array(json_wtr);
>>>> +			for (i = 0; i < nr_kinds; i++) {
>>>> +				jsonw_start_object(json_wtr);
>>>> +				jsonw_uint_field(json_wtr, "kind", i);
>>>> +				if (i < NR_BTF_KINDS)
>>>> +					jsonw_string_field(json_wtr, "name", btf_kind_str[i]);
>>>
>>> I prefer to avoid conditional fields in JSON, especially in an array
>>> it's easier to process the JSON if all items have the same structure.
>>> Would it make sense to keep the "name" field, but use jsonw_null() (or
>>> "UNKNOWN") for the value when there's no name to print?
>>>
>>
>> The only thing about UNKNOWN is that there is a BTF_KIND_UNKN that
>> is displayed as UNKNOWN; what about "?" to be consistent with the
>> non-json output (or if there's another option of course, we could
>> use that for both)? Thanks!
> 
> Right, sorry, I realised just after sending my message.
> In that case we could just use a 'null' value in JSON with jsonw_null()? The object '{"name": null}' is valid. The question mark is another possibility but requires comparing strings when parsing the JSON.
>

Great idea! I'll use jsonw_null() for the next version.

I think given the fact we're not sure about the way forward on CRCs and
standalone BTF I'll probably just separate out the kind layout stuff +
bpftool dump meta parts since I _think_ at this point those pieces are
relatively uncontroversial.

Thanks again for the reviews Quentin!

Alan

> Thanks,
> Quentin
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool
index 6e4f7ce6bc01..157c3afd8247 100644
--- a/tools/bpf/bpftool/bash-completion/bpftool
+++ b/tools/bpf/bpftool/bash-completion/bpftool
@@ -937,7 +937,7 @@  _bpftool()
                             return 0
                             ;;
                         format)
-                            COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) )
+                            COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) )
                             ;;
                         *)
                             # emit extra options
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index 91fcb75babe3..208f3a587534 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -504,6 +504,88 @@  static int dump_btf_c(const struct btf *btf,
 	return err;
 }
 
+static int dump_btf_meta(const struct btf *btf)
+{
+	const struct btf_header *hdr;
+	const struct btf_kind_layout *k;
+	__u8 i, nr_kinds = 0;
+	const void *data;
+	__u32 data_sz;
+
+	data = btf__raw_data(btf, &data_sz);
+	if (!data)
+		return -ENOMEM;
+	hdr = data;
+	if (json_output) {
+		jsonw_start_object(json_wtr);   /* btf metadata object */
+		jsonw_uint_field(json_wtr, "size", data_sz);
+		jsonw_name(json_wtr, "header");
+		jsonw_start_object(json_wtr);	/* btf header object */
+		jsonw_uint_field(json_wtr, "magic", hdr->magic);
+		jsonw_uint_field(json_wtr, "version", hdr->version);
+		jsonw_uint_field(json_wtr, "flags", hdr->flags);
+		jsonw_uint_field(json_wtr, "hdr_len", hdr->hdr_len);
+		jsonw_uint_field(json_wtr, "type_len", hdr->type_len);
+		jsonw_uint_field(json_wtr, "type_off", hdr->type_off);
+		jsonw_uint_field(json_wtr, "str_len", hdr->str_len);
+		jsonw_uint_field(json_wtr, "str_off", hdr->str_off);
+	} else {
+		printf("size %-10d\n", data_sz);
+		printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10d\n",
+		       hdr->magic, hdr->version, hdr->flags, hdr->hdr_len);
+		printf("type_len %-10d\ntype_off %-10d\n", hdr->type_len, hdr->type_off);
+		printf("str_len %-10d\nstr_off %-10d\n", hdr->str_len, hdr->str_off);
+	}
+
+	if (hdr->hdr_len < sizeof(struct btf_header)) {
+		if (json_output) {
+			jsonw_end_object(json_wtr); /* header object */
+			jsonw_end_object(json_wtr); /* metadata object */
+		}
+		return 0;
+	}
+	if (hdr->kind_layout_len > 0 && hdr->kind_layout_off > 0) {
+		k = (void *)hdr + hdr->hdr_len + hdr->kind_layout_off;
+		nr_kinds = hdr->kind_layout_len / sizeof(*k);
+	}
+	if (json_output) {
+		jsonw_uint_field(json_wtr, "kind_layout_len", hdr->kind_layout_len);
+		jsonw_uint_field(json_wtr, "kind_layout_offset", hdr->kind_layout_off);
+		jsonw_uint_field(json_wtr, "crc", hdr->crc);
+		jsonw_uint_field(json_wtr, "base_crc", hdr->base_crc);
+		jsonw_end_object(json_wtr); /* end header object */
+
+		if (nr_kinds > 0) {
+			jsonw_name(json_wtr, "kind_layouts");
+			jsonw_start_array(json_wtr);
+			for (i = 0; i < nr_kinds; i++) {
+				jsonw_start_object(json_wtr);
+				jsonw_uint_field(json_wtr, "kind", i);
+				if (i < NR_BTF_KINDS)
+					jsonw_string_field(json_wtr, "name", btf_kind_str[i]);
+				jsonw_uint_field(json_wtr, "flags", k[i].flags);
+				jsonw_uint_field(json_wtr, "info_sz", k[i].info_sz);
+				jsonw_uint_field(json_wtr, "elem_sz", k[i].elem_sz);
+				jsonw_end_object(json_wtr);
+			}
+			jsonw_end_array(json_wtr);
+		}
+		jsonw_end_object(json_wtr);	/* end metadata */
+	} else {
+		printf("kind_layout_len %-10d\nkind_layout_off %-10d\n",
+		       hdr->kind_layout_len, hdr->kind_layout_off);
+		printf("crc 0x%-10x\nbase_crc 0x%-10x\n",
+		       hdr->crc, hdr->base_crc);
+		for (i = 0; i < nr_kinds; i++) {
+			printf("kind %-4d %-10s flags 0x%-4x info_sz %-4d elem_sz %-4d\n",
+			       i, i < NR_BTF_KINDS ? btf_kind_str[i] : "?",
+			       k[i].flags, k[i].info_sz, k[i].elem_sz);
+		}
+	}
+
+	return 0;
+}
+
 static const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux";
 
 static struct btf *get_vmlinux_btf_from_sysfs(void)
@@ -553,6 +635,7 @@  static int do_dump(int argc, char **argv)
 	__u32 root_type_ids[2];
 	int root_type_cnt = 0;
 	bool dump_c = false;
+	bool dump_meta = false;
 	__u32 btf_id = -1;
 	const char *src;
 	int fd = -1;
@@ -654,10 +737,12 @@  static int do_dump(int argc, char **argv)
 			}
 			if (strcmp(*argv, "c") == 0) {
 				dump_c = true;
+			} else if (is_prefix(*argv, "meta")) {
+				dump_meta = true;
 			} else if (strcmp(*argv, "raw") == 0) {
 				dump_c = false;
 			} else {
-				p_err("unrecognized format specifier: '%s', possible values: raw, c",
+				p_err("unrecognized format specifier: '%s', possible values: raw, c, meta",
 				      *argv);
 				err = -EINVAL;
 				goto done;
@@ -692,6 +777,8 @@  static int do_dump(int argc, char **argv)
 			goto done;
 		}
 		err = dump_btf_c(btf, root_type_ids, root_type_cnt);
+	} else if (dump_meta) {
+		err = dump_btf_meta(btf);
 	} else {
 		err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
 	}
@@ -1063,7 +1150,7 @@  static int do_help(int argc, char **argv)
 		"       %1$s %2$s help\n"
 		"\n"
 		"       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
-		"       FORMAT  := { raw | c }\n"
+		"       FORMAT  := { raw | c | meta }\n"
 		"       " HELP_SPEC_MAP "\n"
 		"       " HELP_SPEC_PROGRAM "\n"
 		"       " HELP_SPEC_OPTIONS " |\n"