2025-05-28 10:57 UTC+0100 ~ Alan Maguire <alan.maguire@xxxxxxxxxx> > 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 5169836 > magic 0xeb9f > version 1 > flags 0x1 > hdr_len 40 > type_len 3041436 > type_off 0 > str_len 2128279 > str_off 3041436 > kind_layout_len 80 > kind_layout_off 5169716 > kind 0 UNKNOWN flags 0x0 info_sz 0 elem_sz 0 > kind 1 INT flags 0x0 info_sz 4 elem_sz 0 > kind 2 PTR flags 0x0 info_sz 0 elem_sz 0 > kind 3 ARRAY flags 0x0 info_sz 12 elem_sz 0 > kind 4 STRUCT flags 0x0 info_sz 0 elem_sz 12 > ... > > JSON output is also supported: > > $ bpftool -j btf dump file vmlinux format meta | jq Just so that you know, we have "bpftool -p" (which is the short version for "bpftool --json --pretty") that will prettify the output without requiring you to pipe to jq. > { > "size": 5169836, > "header": { > "magic": 60319, > "version": 1, > "flags": 1, > "hdr_len": 40, > "type_len": 3041436, > "type_off": 0, > "str_len": 2128279, > "str_off": 3041436, > "kind_layout_len": 80, > "kind_layout_offset": 5169716, > }, > "kind_layouts": [ > { > "kind": 0, > "name": "UNKNOWN", > "flags": 0, > "info_sz": 0, > "elem_sz": 0 > }, > { > "kind": 1, > "name": "INT", > "flags": 0, > "info_sz": 4, > "elem_sz": 0 > }, > ... > > Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> > --- > tools/bpf/bpftool/bash-completion/bpftool | 2 +- > tools/bpf/bpftool/btf.c | 90 ++++++++++++++++++++++- > 2 files changed, 88 insertions(+), 4 deletions(-) > > diff --git a/tools/bpf/bpftool/bash-completion/bpftool b/tools/bpf/bpftool/bash-completion/bpftool > index 1ce409a6cbd9..8accc9e153a7 100644 > --- a/tools/bpf/bpftool/bash-completion/bpftool > +++ b/tools/bpf/bpftool/bash-completion/bpftool > @@ -928,7 +928,7 @@ _bpftool() > return 0 > ;; > format) > - COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) ) > + COMPREPLY=( $( compgen -W "c raw meta" -- "$cur" ) ) > ;; > root_id) > return 0; Thanks for the bash completion; could you also please update bpftool's documentation? The bpftool-btf.rst page (two locations: the "BTF COMMANDS" summary at the top, and the command description - you could even add the example from your commit description as an example at the bottom of the page, if you'd like). > diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c > index 6b14cbfa58aa..686608fb7b6c 100644 > --- a/tools/bpf/bpftool/btf.c > +++ b/tools/bpf/bpftool/btf.c > @@ -835,6 +835,86 @@ 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); /* metadata object */ > + jsonw_uint_field(json_wtr, "size", data_sz); > + jsonw_name(json_wtr, "header"); > + jsonw_start_object(json_wtr); /* 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 %-10u\n", data_sz); > + printf("magic 0x%-10x\nversion %-10d\nflags 0x%-10x\nhdr_len %-10u\n", > + hdr->magic, hdr->version, hdr->flags, hdr->hdr_len); > + printf("type_len %-10u\ntype_off %-10u\n", hdr->type_len, hdr->type_off); > + printf("str_len %-10u\nstr_off %-10u\n", hdr->str_len, hdr->str_off); > + } > + > + if (hdr->hdr_len < sizeof(struct btf_header)) { > + if (json_output) { > + jsonw_end_object(json_wtr); /* end header object */ > + jsonw_end_object(json_wtr); /* end 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_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]); > + else > + jsonw_null_field(json_wtr, "name"); Thanks! > + 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 object */ Why not have the fields unconditionally in JSON? ... "kind_layout_len": 0, "kind_layout_offset": 0, }, "kind_layouts": [ ] This would allow tools to know explicitely that there's no kind layout info, or to loop on kind_layouts without having to check for existence first. If the fields are not set, I find it always difficult to know if there's no kind info or if bpftool didn't support dumping the kind info (in that case this concern doesn't really apply because we add support for kind info dump and the "meta" format at the same time, but users don't necessarily know that). Thanks, Quentin