Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'bpftool-btf-support-dumping-a-single-type-from-file'

Daniel Xu says:

====================
bpftool: btf: Support dumping a single type from file

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 single type by
providing an optional root_id argument.

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

=== Changelog ===

Changes in v5:
* Update bash-completion to support repeating root_id
* Update man page to mention root_id NAND map key/value/kv/all

Changes in v4:
* Support multiple instances of root_id

Changes in v3:
* Make `root_id` a top level btf-dump argument rather than attached to `file`
* Update bash completion script
* Refactor root_type_ids checking to after btf handle creation
* Update help messages and fix existing man page inconsistency

Changes in v2:
* Add early error check for invalid BTF ID
====================

Link: https://patch.msgid.link/cover.1734119028.git.dxu@dxuuu.xyz
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+62 -5
+7 -2
tools/bpf/bpftool/Documentation/bpftool-btf.rst
··· 24 24 ============= 25 25 26 26 | **bpftool** **btf** { **show** | **list** } [**id** *BTF_ID*] 27 - | **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] 27 + | **bpftool** **btf dump** *BTF_SRC* [**format** *FORMAT*] [**root_id** *ROOT_ID*] 28 28 | **bpftool** **btf help** 29 29 | 30 30 | *BTF_SRC* := { **id** *BTF_ID* | **prog** *PROG* | **map** *MAP* [{**key** | **value** | **kv** | **all**}] | **file** *FILE* } ··· 43 43 that hold open file descriptors (FDs) against BTF objects. On such kernels 44 44 bpftool will automatically emit this information as well. 45 45 46 - bpftool btf dump *BTF_SRC* 46 + bpftool btf dump *BTF_SRC* [format *FORMAT*] [root_id *ROOT_ID*] 47 47 Dump BTF entries from a given *BTF_SRC*. 48 48 49 49 When **id** is specified, BTF object with that ID will be loaded and all ··· 66 66 (**raw**) or C-syntax (**c**) output formats are supported. With C-style 67 67 formatting, the output is sorted by default. Use the **unsorted** option 68 68 to avoid sorting the output. 69 + 70 + **root_id** option can be used to filter a dump to a single type and all 71 + its dependent types. It cannot be used with any other types of filtering 72 + (such as the "key", "value", or "kv" arguments when dumping BTF for a map). 73 + It can be passed multiple times to dump multiple types. 69 74 70 75 bpftool btf help 71 76 Print short help message.
+6 -1
tools/bpf/bpftool/bash-completion/bpftool
··· 930 930 format) 931 931 COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) ) 932 932 ;; 933 + root_id) 934 + return 0; 935 + ;; 933 936 c) 934 - COMPREPLY=( $( compgen -W "unsorted" -- "$cur" ) ) 937 + COMPREPLY=( $( compgen -W "unsorted root_id" -- "$cur" ) ) 935 938 ;; 936 939 *) 937 940 # emit extra options 938 941 case ${words[3]} in 939 942 id|file) 943 + COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) ) 940 944 _bpftool_once_attr 'format' 941 945 ;; 942 946 map|prog) 943 947 if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; then 944 948 COMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) ) 945 949 fi 950 + COMPREPLY=( $( compgen -W "root_id" -- "$cur" ) ) 946 951 _bpftool_once_attr 'format' 947 952 ;; 948 953 *)
+49 -2
tools/bpf/bpftool/btf.c
··· 27 27 #define KFUNC_DECL_TAG "bpf_kfunc" 28 28 #define FASTCALL_DECL_TAG "bpf_fastcall" 29 29 30 + #define MAX_ROOT_IDS 16 31 + 30 32 static const char * const btf_kind_str[NR_BTF_KINDS] = { 31 33 [BTF_KIND_UNKN] = "UNKNOWN", 32 34 [BTF_KIND_INT] = "INT", ··· 882 880 { 883 881 bool dump_c = false, sort_dump_c = true; 884 882 struct btf *btf = NULL, *base = NULL; 885 - __u32 root_type_ids[2]; 883 + __u32 root_type_ids[MAX_ROOT_IDS]; 884 + bool have_id_filtering; 886 885 int root_type_cnt = 0; 887 886 __u32 btf_id = -1; 888 887 const char *src; 889 888 int fd = -1; 890 889 int err = 0; 890 + int i; 891 891 892 892 if (!REQ_ARGS(2)) { 893 893 usage(); ··· 977 973 goto done; 978 974 } 979 975 976 + have_id_filtering = !!root_type_cnt; 977 + 980 978 while (argc) { 981 979 if (is_prefix(*argv, "format")) { 982 980 NEXT_ARG(); ··· 997 991 err = -EINVAL; 998 992 goto done; 999 993 } 994 + NEXT_ARG(); 995 + } else if (is_prefix(*argv, "root_id")) { 996 + __u32 root_id; 997 + char *end; 998 + 999 + if (have_id_filtering) { 1000 + p_err("cannot use root_id with other type filtering"); 1001 + err = -EINVAL; 1002 + goto done; 1003 + } else if (root_type_cnt == MAX_ROOT_IDS) { 1004 + p_err("only %d root_id are supported", MAX_ROOT_IDS); 1005 + err = -E2BIG; 1006 + goto done; 1007 + } 1008 + 1009 + NEXT_ARG(); 1010 + root_id = strtoul(*argv, &end, 0); 1011 + if (*end) { 1012 + err = -1; 1013 + p_err("can't parse %s as root ID", *argv); 1014 + goto done; 1015 + } 1016 + for (i = 0; i < root_type_cnt; i++) { 1017 + if (root_type_ids[i] == root_id) { 1018 + err = -EINVAL; 1019 + p_err("duplicate root_id %d supplied", root_id); 1020 + goto done; 1021 + } 1022 + } 1023 + root_type_ids[root_type_cnt++] = root_id; 1000 1024 NEXT_ARG(); 1001 1025 } else if (is_prefix(*argv, "unsorted")) { 1002 1026 sort_dump_c = false; ··· 1049 1013 if (!btf) { 1050 1014 err = -errno; 1051 1015 p_err("get btf by id (%u): %s", btf_id, strerror(errno)); 1016 + goto done; 1017 + } 1018 + } 1019 + 1020 + /* Invalid root IDs causes half emitted boilerplate and then unclean 1021 + * exit. It's an ugly user experience, so handle common error here. 1022 + */ 1023 + for (i = 0; i < root_type_cnt; i++) { 1024 + if (root_type_ids[i] >= btf__type_cnt(btf)) { 1025 + err = -EINVAL; 1026 + p_err("invalid root ID: %u", root_type_ids[i]); 1052 1027 goto done; 1053 1028 } 1054 1029 } ··· 1438 1391 1439 1392 fprintf(stderr, 1440 1393 "Usage: %1$s %2$s { show | list } [id BTF_ID]\n" 1441 - " %1$s %2$s dump BTF_SRC [format FORMAT]\n" 1394 + " %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n" 1442 1395 " %1$s %2$s help\n" 1443 1396 "\n" 1444 1397 " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"