bpf: btf: fix struct/union/fwd types with kind_flag
Subsystems (6)
v3 cover bpf: btf: fix struct/union/fwd types with kind_flag
Commit 69b693f0aefa ("bpf: btf: Introduce BPF Type Format (BTF)") introduced BTF, a debug info format for BTF.
The original design has a couple of issues though.
First, the bitfield size is only encoded in int type.
If the struct member bitfield type is enum, pahole ([1])
or llvm is forced to replace enum with int type. As a result, the original type information gets lost.
Second, the original BTF design does not envision the possibility of BTF=>header_file conversion ([2]), hence does not encode "struct" or "union" info for a forward type. Such information is necessary to convert BTF to a header file.
This patch set fixed the issue by introducing kind_flag, using one bit in type->info. When kind_flag, the struct/union btf_member->offset will encode both bitfield_size and bit_offset, covering both int and enum base types. The kind_flag is also used to indicate whether the forward type is a union (when set) or a struct.
Patch #1 refactors function btf_int_bits_seq_show() so Patch #2 can reuse part of the function.
Patch #2 implemented kind_flag support for struct/union/fwd types. Patch #3 added kind_flag support for cgroup local storage map pretty print. Patch #4 syncs kernel uapi btf.h to tools directory.
Patch #5 added unit tests for kind_flag.
Patch #6 added tests for kernel bpffs based pretty print with kind_flag. Patch #7 refactors function btf_dumper_int_bits() so Patch #8 can reuse part of the function.
Patch #8 added bpftool support of pretty print with kind_flag set.
[1] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?id=b18354f64cc215368c3bc0df4a7e5341c55c378c
[2] https://lwn.net/SubscriberLink/773198/fe3074838f5c3f26/
Change logs:
v2 -> v3:
. Relocated comments about bitfield_size/bit_offset interpretation
of the "offset" field right before the "offset" struct member.
. Added missing byte alignment checking for non-bitfield enum
member of a struct with kind_flag set.
. Added two test cases in unit tests for struct type, kind_flag set,
non-bitfield int/enum member, not-byte aligned bit offsets.
. Added comments to help understand there is no overflow for
total_bits_offset in bpftool function btf_dumper_int_bits().
. Added explanation of typedef type dumping fix in Patch #8 commit
message.
v1 -> v2:
. If kind_flag is set for a structure, ensure an int member,
whether it is a bitfield or not, is a regular int type.
. Added support so cgroup local storage map pretty print
works with kind_flag.
Yonghong Song (8):
bpf: btf: refactor btf_int_bits_seq_show()
bpf: btf: fix struct/union/fwd types with kind_flag
bpf: enable cgroup local storage map pretty print with kind_flag tools/bpf: sync btf.h header from kernel to tools tools/bpf: add test_btf unit tests for kind_flag tools/bpf: test kernel bpffs map pretty print with struct kind_flag
tools: bpftool: refactor btf_dumper_int_bits()
tools: bpftool: support pretty print with kind_flag set
include/linux/btf.h | 5 +- include/uapi/linux/btf.h | 20 +- kernel/bpf/btf.c | 352 +++++++++++-- kernel/bpf/local_storage.c | 17 +- tools/bpf/bpftool/btf_dumper.c | 61 ++- tools/include/uapi/linux/btf.h | 20 +- tools/testing/selftests/bpf/test_btf.c | 664 ++++++++++++++++++++++++- 7 files changed, 1047 insertions(+), 92 deletions(-)
Signature · 1 line
2.17.1
PATCH v3 1/8 bpf: btf: refactor btf_int_bits_seq_show()
Refactor function btf_int_bits_seq_show() by creating function btf_bitfield_seq_show() which has no dependence on btf and btf_type. The function btf_bitfield_seq_show() will be in later patch to directly dump bitfield member values.
--- kernel/bpf/btf.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-)
kernel/bpf/btf.c +21 -14
@@ -1068,26 +1068,16 @@ static void btf_int_log(struct btf_verifier_env *env, btf_int_encoding_str(BTF_INT_ENCODING(int_data))); } -static void btf_int_bits_seq_show (const struct btf *btf, -const struct btf_type *t, -void *data,u8 bits_offset, -struct seq_file *m) +static void btf_bitfield_seq_show (void *data,u8 bits_offset, +u8 nr_bits,struct seq_file *m) {u16 left_shift_bits, right_shift_bits; -u32 int_data =btf_type_int (t); -u8 nr_bits =BTF_INT_BITS (int_data); -u8 total_bits_offset;u8 nr_copy_bytes;u8 nr_copy_bits;u64 print_num; -/* -* bits_offset is at most 7. -* BTF_INT_OFFSET() cannot exceed 64 bits. -*/ - total_bits_offset = bits_offset +BTF_INT_OFFSET (int_data); - data +=BITS_ROUNDDOWN_BYTES (total_bits_offset); - bits_offset =BITS_PER_BYTE_MASKED (total_bits_offset); + data +=BITS_ROUNDDOWN_BYTES (bits_offset); + bits_offset =BITS_PER_BYTE_MASKED (bits_offset); nr_copy_bits = nr_bits + bits_offset; nr_copy_bytes =BITS_ROUNDUP_BYTES (nr_copy_bits);
@@ -1107,6 +1097,23 @@ static void btf_int_bits_seq_show(const struct btf *btf,seq_printf (m,"0x%llx" , print_num); } +static void btf_int_bits_seq_show (const struct btf *btf, +const struct btf_type *t, +void *data,u8 bits_offset, +struct seq_file *m) +{ +u32 int_data =btf_type_int (t); +u8 nr_bits =BTF_INT_BITS (int_data); +u8 total_bits_offset; + +/* +* bits_offset is at most 7. +* BTF_INT_OFFSET() cannot exceed 64 bits. +*/ + total_bits_offset = bits_offset +BTF_INT_OFFSET (int_data); +btf_bitfield_seq_show (data, total_bits_offset, nr_bits, m); +} +static void btf_int_seq_show (const struct btf *btf,const struct btf_type *t,u32 type_id,void *data,u8 bits_offset,struct seq_file *m)
Signature · 1 line
2.17.1
PATCH v3 2/8 bpf: btf: fix struct/union/fwd types with kind_flag
In reply to Yonghong SongThis patch fixed two issues with BTF. One is related to struct/union bitfield encoding and the other is related to forward type.
Issue #1 and solution:
======================
Current btf encoding of bitfield follows what pahole generates. For each bitfield, pahole will duplicate the type chain and put the bitfield size at the final int or enum type. Since the BTF enum type cannot encode bit size, pahole workarounds the issue by generating
an int type whenever the enum bit size is not 32.
For example,
-bash-4.4$ cat t.c
typedef int ___int;
enum A { A1, A2, A3 };
struct t {
int a[5];
___int b:4;
volatile enum A c:4;
} g;
-bash-4.4$ gcc -c -O2 -g t.c
The current kernel supports the following BTF encoding:
$ pahole -JV t.o
[1] TYPEDEF ___int type_id=2
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
[3] ENUM A size=4 vlen=3
A1 val=0
A2 val=1
A3 val=2
[4] STRUCT t size=24 vlen=3
a type_id=5 bits_offset=0
b type_id=9 bits_offset=160
c type_id=11 bits_offset=164
[5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
[6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
[7] VOLATILE (anon) type_id=3
[8] INT int size=1 bit_offset=0 nr_bits=4 encoding=(none)
[9] TYPEDEF ___int type_id=8
[10] INT (anon) size=1 bit_offset=0 nr_bits=4 encoding=SIGNED
[11] VOLATILE (anon) type_id=10Two issues are in the above:
. by changing enum type to int, we lost the original
type information and this will not be ideal later when we try to convert BTF to a header file.
. the type duplication for bitfields will cause
BTF bloat. Duplicated types cannot be deduplicated later if the bitfield size is different.
To fix this issue, this patch implemented a compatible change for BTF struct type encoding:
. the bit 31 of struct_type->info, previously reserved,
now is used to indicate whether bitfield_size is encoded in btf_member or not.
. if bit 31 of struct_type->info is set,
btf_member->offset will encode like:
bit 0 - 23: bit offset
bit 24 - 31: bitfield size
if bit 31 is not set, the old behavior is preserved:
bit 0 - 31: bit offset
So if the struct contains a bit field, the maximum bit offset will be reduced to (2^24 - 1) instead of MAX_UINT. The maximum bitfield size will be 256 which is enough for today as maximum bitfield in compiler can be 128 where int128 type is supported.
This kernel patch intends to support the new BTF encoding:
$ pahole -JV t.o
[1] TYPEDEF ___int type_id=2
[2] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
[3] ENUM A size=4 vlen=3
A1 val=0
A2 val=1
A3 val=2
[4] STRUCT t kind_flag=1 size=24 vlen=3
a type_id=5 bitfield_size=0 bits_offset=0
b type_id=1 bitfield_size=4 bits_offset=160
c type_id=7 bitfield_size=4 bits_offset=164
[5] ARRAY (anon) type_id=2 index_type_id=2 nr_elems=5
[6] INT sizetype size=8 bit_offset=0 nr_bits=64 encoding=(none)
[7] VOLATILE (anon) type_id=3Issue #2 and solution:
======================
Current forward type in BTF does not specify whether the original type is struct or union. This will not work for type pretty print and BTF-to-header-file conversion as struct/union must be specified.
$ cat tt.c
struct t;
union u;
int foo(struct t *t, union u *u) { return 0; }
$ gcc -c -g -O2 tt.c
$ pahole -JV tt.o
[1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED
[2] FWD t type_id=0
[3] PTR (anon) type_id=2
[4] FWD u type_id=0
[5] PTR (anon) type_id=4To fix this issue, similar to issue #1, type->info bit 31 is used. If the bit is set, it is union type. Otherwise, it is a struct type.
$ pahole -JV tt.o [1] INT int size=4 bit_offset=0 nr_bits=32 encoding=SIGNED [2] FWD t kind_flag=0 type_id=0 [3] PTR (anon) kind_flag=0 type_id=2 [4] FWD u kind_flag=1 type_id=0 [5] PTR (anon) kind_flag=0 type_id=4
Pahole/LLVM change:
===================
The new kind_flag functionality has been implemented in pahole and llvm:
https://github.com/yonghong-song/pahole/tree/bitfield
https://github.com/yonghong-song/llvm/tree/bitfield
Note that pahole hasn't implemented func/func_proto kind and .BTF.ext. So to print function signature with bpftool, the llvm compiler should be used.
--- include/uapi/linux/btf.h | 20 ++- kernel/bpf/btf.c | 280 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 278 insertions(+), 22 deletions(-)
include/uapi/linux/btf.h +18 -2
@@ -34,7 +34,9 @@ struct btf_type {* bits 0-15: vlen (e.g. # of struct's members) * bits 16-23: unused * bits 24-27: kind (e.g. int, ptr, array...etc) -* bits 28-31: unused +* bits 28-30: unused +* bit 31: kind_flag, currently used by +* struct, union and fwd */ __u32 info;/* "size" is used by INT, ENUM, STRUCT and UNION.
@@ -52,6 +54,7 @@ struct btf_type {#define BTF_INFO_KIND (info) (((info) >> 24) & 0x0f)#define BTF_INFO_VLEN (info) ((info) & 0xffff) +#define BTF_INFO_KFLAG (info) ((info) >> 31)#define BTF_KIND_UNKN 0/* Unknown */ #define BTF_KIND_INT 1/* Integer */
@@ -110,9 +113,22 @@ struct btf_array {struct btf_member {__u32 name_off;__u32 type; -__u32 offset;/* offset in bits */ +/* If the type info kind_flag is set, the btf_member offset +* contains both member bitfield size and bit offset. The +* bitfield size is set for bitfield members. If the type +* info kind_flag is not set, the offset contains only bit +* offset. +*/ +__u32 offset; }; +/* If the struct/union type info kind_flag is set, the +* following two macros are used to access bitfield_size +* and bit_offset from btf_member.offset. +*/ +#define BTF_MEMBER_BITFIELD_SIZE (val) ((val) >> 24) +#define BTF_MEMBER_BIT_OFFSET (val) ((val) & 0xffffff) +/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". * The exact number of btf_param is stored in the vlen (of the * info in "struct btf_type").
kernel/bpf/btf.c +260 -20
476 lines not on this page · raw message
Signature · 1 line
2.17.1
PATCH v3 3/8 bpf: enable cgroup local storage map pretty print with kind_flag
In reply to Yonghong SongCommit 970289fc0a83 ("bpf: add bpffs pretty print for cgroup local storage maps") added bpffs pretty print for cgroup local storage maps. The commit worked for struct without kind_flag set.
This patch refactored and made pretty print also work with kind_flag set for the struct.
--- include/linux/btf.h | 5 ++++- kernel/bpf/btf.c | 37 ++++++++++++++++++++++++++++--------- kernel/bpf/local_storage.c | 17 ++++------------- 3 files changed, 36 insertions(+), 23 deletions(-)
include/linux/btf.h +4 -1
@@ -7,6 +7,7 @@#include <linux/types.h> struct btf ; +struct btf_member ;struct btf_type ;union bpf_attr ;
@@ -46,7 +47,9 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj, struct seq_file *m);int btf_get_fd_by_id (u32 id);u32 btf_id (const struct btf *btf); -bool btf_type_is_reg_int (const struct btf_type *t,u32 expected_size); +bool btf_member_is_reg_int (const struct btf *btf,const struct btf_type *s, +const struct btf_member *m, +u32 expected_offset,u32 expected_size);#ifdef CONFIG_BPF_SYSCALL const struct btf_type *btf_type_by_id (const struct btf *btf,u32 type_id);
kernel/bpf/btf.c +28 -9
@@ -546,22 +546,41 @@ static bool btf_type_int_is_regular(const struct btf_type *t) }/* -* Check that given type is a regular int and has the expected size. +* Check that given struct member is a regular int with expected +* offset and size. */ -bool btf_type_is_reg_int (const struct btf_type *t,u32 expected_size) +bool btf_member_is_reg_int (const struct btf *btf,const struct btf_type *s, +const struct btf_member *m, +u32 expected_offset,u32 expected_size) { -u8 nr_bits, nr_bytes; -u32 int_data; +const struct btf_type *t; +u32 id, int_data; +u8 nr_bits; -if (!btf_type_is_int (t)) + id = m->type; + t =btf_type_id_size (btf, &id,NULL ); +if (!t || !btf_type_is_int (t))return false; int_data =btf_type_int (t); nr_bits =BTF_INT_BITS (int_data); - nr_bytes =BITS_ROUNDUP_BYTES (nr_bits); -if (BITS_PER_BYTE_MASKED (nr_bits) || -BTF_INT_OFFSET (int_data) || - nr_bytes != expected_size) +if (btf_type_kflag (s)) { +u32 bitfield_size =BTF_MEMBER_BITFIELD_SIZE (m->offset); +u32 bit_offset =BTF_MEMBER_BIT_OFFSET (m->offset); + +/* if kflag set, int should be a regular int and +* bit offset should be at byte boundary. +*/ +return !bitfield_size && +BITS_ROUNDUP_BYTES (bit_offset) == expected_offset && +BITS_ROUNDUP_BYTES (nr_bits) == expected_size; + } + +if (BTF_INT_OFFSET (int_data) || +BITS_PER_BYTE_MASKED (m->offset) || +BITS_ROUNDUP_BYTES (m->offset) != expected_offset || +BITS_PER_BYTE_MASKED (nr_bits) || +BITS_ROUNDUP_BYTES (nr_bits) != expected_size)return false;return true;
kernel/bpf/local_storage.c +4 -13
@@ -315,9 +315,8 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,const struct btf_type *key_type, const struct btf_type *value_type) { - const struct btf_type *t; struct btf_member *m; -u32 id, size; +u32 offset, size;/* Key is expected to be of struct bpf_cgroup_storage_key type, * which is:
@@ -338,25 +337,17 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,* The first field must be a 64 bit integer at 0 offset. */ m = (struct btf_member *)(key_type +1 ); -if (m->offset) -return -EINVAL ; - id = m->type; - t =btf_type_id_size (btf, &id,NULL ); size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, cgroup_inode_id); -if (!t || !btf_type_is_reg_int (t, size)) +if (!btf_member_is_reg_int (btf, key_type, m,0 , size))return -EINVAL ;/* * The second field must be a 32 bit integer at 64 bit offset. */ m++; -if (m->offset != offsetof(struct bpf_cgroup_storage_key , attach_type) * -BITS_PER_BYTE ) -return -EINVAL ; - id = m->type; - t =btf_type_id_size (btf, &id,NULL ); + offset = offsetof(struct bpf_cgroup_storage_key , attach_type); size = FIELD_SIZEOF(struct bpf_cgroup_storage_key, attach_type); -if (!t || !btf_type_is_reg_int (t, size)) +if (!btf_member_is_reg_int (btf, key_type, m, offset, size))return -EINVAL ;return 0 ;
Signature · 1 line
2.17.1
PATCH v3 4/8 tools/bpf: sync btf.h header from kernel to tools
In reply to Yonghong SongSync include/uapi/linux/btf.h to tools/include/uapi/linux/btf.h.
--- tools/include/uapi/linux/btf.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
tools/include/uapi/linux/btf.h +18 -2
@@ -34,7 +34,9 @@ struct btf_type {* bits 0-15: vlen (e.g. # of struct's members) * bits 16-23: unused * bits 24-27: kind (e.g. int, ptr, array...etc) -* bits 28-31: unused +* bits 28-30: unused +* bit 31: kind_flag, currently used by +* struct, union and fwd */ __u32 info;/* "size" is used by INT, ENUM, STRUCT and UNION.
@@ -52,6 +54,7 @@ struct btf_type {#define BTF_INFO_KIND (info) (((info) >> 24) & 0x0f)#define BTF_INFO_VLEN (info) ((info) & 0xffff) +#define BTF_INFO_KFLAG (info) ((info) >> 31)#define BTF_KIND_UNKN 0/* Unknown */ #define BTF_KIND_INT 1/* Integer */
@@ -110,9 +113,22 @@ struct btf_array {struct btf_member {__u32 name_off;__u32 type; -__u32 offset;/* offset in bits */ +/* If the type info kind_flag is set, the btf_member offset +* contains both member bitfield size and bit offset. The +* bitfield size is set for bitfield members. If the type +* info kind_flag is not set, the offset contains only bit +* offset. +*/ +__u32 offset; }; +/* If the struct/union type info kind_flag is set, the +* following two macros are used to access bitfield_size +* and bit_offset from btf_member.offset. +*/ +#define BTF_MEMBER_BITFIELD_SIZE (val) ((val) >> 24) +#define BTF_MEMBER_BIT_OFFSET (val) ((val) & 0xffffff) +/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param". * The exact number of btf_param is stored in the vlen (of the * info in "struct btf_type").
Signature · 1 line
2.17.1
PATCH v3 5/8 tools/bpf: add test_btf unit tests for kind_flag
In reply to Yonghong SongThis patch added unit tests for different types handling type->info.kind_flag. The following new tests are added:
$ test_btf ... BTF raw test[82] (invalid int kind_flag): OK BTF raw test[83] (invalid ptr kind_flag): OK BTF raw test[84] (invalid array kind_flag): OK BTF raw test[85] (invalid enum kind_flag): OK BTF raw test[86] (valid fwd kind_flag): OK BTF raw test[87] (invalid typedef kind_flag): OK BTF raw test[88] (invalid volatile kind_flag): OK BTF raw test[89] (invalid const kind_flag): OK BTF raw test[90] (invalid restrict kind_flag): OK BTF raw test[91] (invalid func kind_flag): OK BTF raw test[92] (invalid func_proto kind_flag): OK BTF raw test[93] (valid struct kind_flag, bitfield_size = 0): OK BTF raw test[94] (valid struct kind_flag, int member, bitfield_size != 0): OK BTF raw test[95] (valid union kind_flag, int member, bitfield_size != 0): OK BTF raw test[96] (valid struct kind_flag, enum member, bitfield_size != 0): OK BTF raw test[97] (valid union kind_flag, enum member, bitfield_size != 0): OK BTF raw test[98] (valid struct kind_flag, typedef member, bitfield_size != 0): OK BTF raw test[99] (valid union kind_flag, typedef member, bitfield_size != 0): OK BTF raw test[100] (invalid struct type, bitfield_size greater than struct size): OK BTF raw test[101] (invalid struct type, kind_flag bitfield base_type int not regular): OK BTF raw test[102] (invalid struct type, kind_flag base_type int not regular): OK BTF raw test[103] (invalid union type, bitfield_size greater than struct size): OK ... PASS:122 SKIP:0 FAIL:0
The second parameter name of macro
BTF_INFO_ENC(kind, root, vlen)
in selftests test_btf.c is also renamed from "root" to "kind_flag". Note that before this patch "root" is not used and always 0.
--- tools/testing/selftests/bpf/test_btf.c | 496 ++++++++++++++++++++++++- 1 file changed, 494 insertions(+), 2 deletions(-)
tools/testing/selftests/bpf/test_btf.c +494 -2
@@ -65,8 +65,8 @@ static int __base_pr(const char *format, ...)return err; } -#define BTF_INFO_ENC (kind, root, vlen) \ - ((!!(root) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) +#define BTF_INFO_ENC (kind, kind_flag, vlen) \ + ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))#define BTF_TYPE_ENC (name, info, size_or_type) \ (name), (info), (size_or_type)
@@ -86,6 +86,8 @@ static int __base_pr(const char *format, ...)#define BTF_MEMBER_ENC (name, type, bits_offset) \ (name), (type), (bits_offset)#define BTF_ENUM_ENC (name, val) (name), (val) +#define BTF_MEMBER_OFFSET (bitfield_size, bits_offset) \ + ((bitfield_size) << 24 | (bits_offset))#define BTF_TYPEDEF_ENC (name, type) \ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0), type)
@@ -2215,6 +2217,496 @@ static struct btf_raw_test raw_tests[] = { .err_str = "Invalid type_id", }, +{ + .descr = "invalid int kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_INT, 1, 0), 4), /* [2] */ + BTF_INT_ENC(0, 0, 32), + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "int_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid ptr kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 1, 0), 1), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "ptr_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid array kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ARRAY, 1, 0), 0), /* [2] */ + BTF_ARRAY_ENC(1, 1, 1), + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "array_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid enum kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 1, 1), 4), /* [2] */ + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "enum_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "valid fwd kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(NAME_TBD, + BTF_INFO_ENC(BTF_KIND_FWD, 1, 0), 0), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0A"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "fwd_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "invalid typedef kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(NAME_TBD, + BTF_INFO_ENC(BTF_KIND_TYPEDEF, 1, 0), 1), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0A"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "typedef_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid volatile kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 1, 0), 1), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "volatile_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid const kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 1, 0), 1), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "const_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid restrict kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_RESTRICT, 1, 0), 1), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "restrict_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid func kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 0), 0), /* [2] */ + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 1, 0), 2), /* [3] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0A"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "func_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "invalid func_proto kind_flag", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 1, 0), 0), /* [2] */ + BTF_END_RAW, + }, + BTF_STR_SEC(""), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "func_proto_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid btf_info kind_flag", +}, + +{ + .descr = "valid struct, kind_flag, bitfield_size = 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 8), /* [2] */ + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(0, 0)), + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(0, 32)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "valid struct, kind_flag, int member, bitfield_size != 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4), /* [2] */ + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 0)), + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 4)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "valid union, kind_flag, int member, bitfield_size != 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 4), /* [2] */ + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 0)), + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(4, 0)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "union_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "valid struct, kind_flag, enum member, bitfield_size != 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), /* [2] */ + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),/* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 0)), + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 4)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B\0C"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "valid union, kind_flag, enum member, bitfield_size != 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), /* [2] */ + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 4), /* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 0)), + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(4, 0)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B\0C"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "union_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "valid struct, kind_flag, typedef member, bitfield_size != 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), /* [2] */ + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4),/* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 4, BTF_MEMBER_OFFSET(4, 0)), + BTF_MEMBER_ENC(NAME_TBD, 5, BTF_MEMBER_OFFSET(4, 4)), + BTF_TYPEDEF_ENC(NAME_TBD, 1), /* [4] */ + BTF_TYPEDEF_ENC(NAME_TBD, 2), /* [5] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B\0C\0D\0E"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "valid union, kind_flag, typedef member, bitfield_size != 0", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), /* [2] */ + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 4), /* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 4, BTF_MEMBER_OFFSET(4, 0)), + BTF_MEMBER_ENC(NAME_TBD, 5, BTF_MEMBER_OFFSET(4, 0)), + BTF_TYPEDEF_ENC(NAME_TBD, 1), /* [4] */ + BTF_TYPEDEF_ENC(NAME_TBD, 2), /* [5] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B\0C\0D\0E"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "union_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, +}, + +{ + .descr = "invalid struct, kind_flag, bitfield_size greater than struct size", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4), /* [2] */ + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(20, 0)), + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(20, 20)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Member exceeds struct_size", +}, + +{ + .descr = "invalid struct, kind_flag, bitfield base_type int not regular", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 20, 4), /* [2] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4), /* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(20, 0)), + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(20, 20)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid member base type", +}, + +{ + .descr = "invalid struct, kind_flag, base_type int not regular", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 12, 4), /* [2] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 4), /* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(8, 0)), + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(8, 8)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid member base type", +}, + +{ + .descr = "invalid union, kind_flag, bitfield_size greater than struct size", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 1, 2), 2), /* [2] */ + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(8, 0)), + BTF_MEMBER_ENC(NAME_TBD, 1, BTF_MEMBER_OFFSET(20, 0)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "union_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Member exceeds struct_size", +}, + +{ + .descr = "invalid struct, kind_flag, int member, bitfield_size = 0, wrong byte alignment", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 12), /* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 0)), + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 36)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid member offset", +}, + +{ + .descr = "invalid struct, kind_flag, enum member, bitfield_size = 0, wrong byte alignment", + .raw_types = { + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [2] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4), /* [2] */ + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 2), 12), /* [3] */ + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 0)), + BTF_MEMBER_ENC(NAME_TBD, 2, BTF_MEMBER_OFFSET(0, 36)), + BTF_END_RAW, + }, + BTF_STR_SEC("\0A\0B\0C"), + .map_type = BPF_MAP_TYPE_ARRAY, + .map_name = "struct_type_check_btf", + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 4, + .btf_load_err = true, + .err_str = "Invalid member offset", +}, + }; /* struct btf_raw_test raw_tests[] */ static const char *get_next_str(const char *start, const char *end)
Signature · 1 line
2.17.1
PATCH v3 6/8 tools/bpf: test kernel bpffs map pretty print with struct kind_flag
In reply to Yonghong SongThe new tests are added to test bpffs map pretty print in kernel with kind_flag for structure type.
$ test_btf -p ...... BTF pretty print array(#1)......OK BTF pretty print array(#2)......OK PASS:8 SKIP:0 FAIL:0
--- tools/testing/selftests/bpf/test_btf.c | 168 ++++++++++++++++++++++--- 1 file changed, 154 insertions(+), 14 deletions(-)
tools/testing/selftests/bpf/test_btf.c +154 -14
@@ -3528,7 +3528,8 @@ struct pprint_mapv { } aenum; }; -static struct btf_raw_test pprint_test_template = { +static struct btf_raw_test pprint_test_template[] = { +{ .raw_types = {/* unsighed char */ /* [1] */ BTF_TYPE_INT_ENC (NAME_TBD ,0 ,0 ,8 ,1 ),
@@ -3578,13 +3579,140 @@ static struct btf_raw_test pprint_test_template = { BTF_MEMBER_ENC(NAME_TBD, 15, 192), /* aenum */ BTF_END_RAW, }, - .str_sec = "\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum", - .str_sec_size = sizeof("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"), + BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"), + .key_size = sizeof(unsigned int), + .value_size = sizeof(struct pprint_mapv), + .key_type_id = 3, /* unsigned int */ + .value_type_id = 16, /* struct pprint_mapv */ + .max_entries = 128 * 1024, +}, + +{ + /* this type will have the same type as the + * first .raw_types definition, but struct type will + * be encoded with kind_flag set. + */ + .raw_types = { + /* unsighed char */ /* [1] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1), +/* unsigned short */ /* [2] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2), +/* unsigned int */ /* [3] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4), +/* int */ /* [4] */ + BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4), +/* unsigned long long */ /* [5] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8), + BTF_TYPE_INT_ENC(0, 0, 0, 32, 4), /* [6] */ + BTF_TYPE_INT_ENC(0, 0, 0, 32, 4), /* [7] */ +/* uint8_t[8] */ /* [8] */ + BTF_TYPE_ARRAY_ENC(9, 1, 8), +/* typedef unsigned char uint8_t */ /* [9] */ + BTF_TYPEDEF_ENC(NAME_TBD, 1), +/* typedef unsigned short uint16_t */ /* [10] */ + BTF_TYPEDEF_ENC(NAME_TBD, 2), +/* typedef unsigned int uint32_t */ /* [11] */ + BTF_TYPEDEF_ENC(NAME_TBD, 3), +/* typedef int int32_t */ /* [12] */ + BTF_TYPEDEF_ENC(NAME_TBD, 4), +/* typedef unsigned long long uint64_t *//* [13] */ + BTF_TYPEDEF_ENC(NAME_TBD, 5), +/* union (anon) */ /* [14] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8), + BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */ + BTF_MEMBER_ENC(NAME_TBD, 8, 0), /* uint8_t ui8a[8]; */ +/* enum (anon) */ /* [15] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4), + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_ENUM_ENC(NAME_TBD, 1), + BTF_ENUM_ENC(NAME_TBD, 2), + BTF_ENUM_ENC(NAME_TBD, 3), +/* struct pprint_mapv */ /* [16] */ + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 8), 32), + BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)), /* uint32_t ui32 */ + BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)), /* uint16_t ui16 */ + BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)), /* int32_t si32 */ + BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 96)), /* unused_bits2a */ + BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)), /* bits28 */ + BTF_MEMBER_ENC(NAME_TBD, 6, BTF_MEMBER_OFFSET(2, 126)), /* unused_bits2b */ + BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)), /* union (anon) */ + BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)), /* aenum */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum"), .key_size = sizeof(unsigned int), .value_size = sizeof(struct pprint_mapv), .key_type_id = 3, /* unsigned int */ .value_type_id = 16, /* struct pprint_mapv */ .max_entries = 128 * 1024, +}, + +{ + /* this type will have the same layout as the + * first .raw_types definition. The struct type will + * be encoded with kind_flag set, bitfield members + * are added typedef/const/volatile, and bitfield members + * will have both int and enum types. + */ + .raw_types = { + /* unsighed char */ /* [1] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 8, 1), +/* unsigned short */ /* [2] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 16, 2), +/* unsigned int */ /* [3] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 32, 4), +/* int */ /* [4] */ + BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4), +/* unsigned long long */ /* [5] */ + BTF_TYPE_INT_ENC(NAME_TBD, 0, 0, 64, 8), + BTF_TYPE_INT_ENC(0, 0, 0, 32, 4), /* [6] */ + BTF_TYPE_INT_ENC(0, 0, 0, 32, 4), /* [7] */ +/* uint8_t[8] */ /* [8] */ + BTF_TYPE_ARRAY_ENC(9, 1, 8), +/* typedef unsigned char uint8_t */ /* [9] */ + BTF_TYPEDEF_ENC(NAME_TBD, 1), +/* typedef unsigned short uint16_t */ /* [10] */ + BTF_TYPEDEF_ENC(NAME_TBD, 2), +/* typedef unsigned int uint32_t */ /* [11] */ + BTF_TYPEDEF_ENC(NAME_TBD, 3), +/* typedef int int32_t */ /* [12] */ + BTF_TYPEDEF_ENC(NAME_TBD, 4), +/* typedef unsigned long long uint64_t *//* [13] */ + BTF_TYPEDEF_ENC(NAME_TBD, 5), +/* union (anon) */ /* [14] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_UNION, 0, 2), 8), + BTF_MEMBER_ENC(NAME_TBD, 13, 0),/* uint64_t ui64; */ + BTF_MEMBER_ENC(NAME_TBD, 8, 0), /* uint8_t ui8a[8]; */ +/* enum (anon) */ /* [15] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 0, 4), 4), + BTF_ENUM_ENC(NAME_TBD, 0), + BTF_ENUM_ENC(NAME_TBD, 1), + BTF_ENUM_ENC(NAME_TBD, 2), + BTF_ENUM_ENC(NAME_TBD, 3), +/* struct pprint_mapv */ /* [16] */ + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, 8), 32), + BTF_MEMBER_ENC(NAME_TBD, 11, BTF_MEMBER_OFFSET(0, 0)), /* uint32_t ui32 */ + BTF_MEMBER_ENC(NAME_TBD, 10, BTF_MEMBER_OFFSET(0, 32)), /* uint16_t ui16 */ + BTF_MEMBER_ENC(NAME_TBD, 12, BTF_MEMBER_OFFSET(0, 64)), /* int32_t si32 */ + BTF_MEMBER_ENC(NAME_TBD, 17, BTF_MEMBER_OFFSET(2, 96)), /* unused_bits2a */ + BTF_MEMBER_ENC(NAME_TBD, 7, BTF_MEMBER_OFFSET(28, 98)), /* bits28 */ + BTF_MEMBER_ENC(NAME_TBD, 19, BTF_MEMBER_OFFSET(2, 126)),/* unused_bits2b */ + BTF_MEMBER_ENC(0, 14, BTF_MEMBER_OFFSET(0, 128)), /* union (anon) */ + BTF_MEMBER_ENC(NAME_TBD, 15, BTF_MEMBER_OFFSET(0, 192)), /* aenum */ +/* typedef unsigned int ___int */ /* [17] */ + BTF_TYPEDEF_ENC(NAME_TBD, 18), + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_VOLATILE, 0, 0), 6), /* [18] */ + BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 15), /* [19] */ + BTF_END_RAW, + }, + BTF_STR_SEC("\0unsigned char\0unsigned short\0unsigned int\0int\0unsigned long long\0uint8_t\0uint16_t\0uint32_t\0int32_t\0uint64_t\0ui64\0ui8a\0ENUM_ZERO\0ENUM_ONE\0ENUM_TWO\0ENUM_THREE\0pprint_mapv\0ui32\0ui16\0si32\0unused_bits2a\0bits28\0unused_bits2b\0aenum\0___int"), + .key_size = sizeof(unsigned int), + .value_size = sizeof(struct pprint_mapv), + .key_type_id = 3, /* unsigned int */ + .value_type_id = 16, /* struct pprint_mapv */ + .max_entries = 128 * 1024, +}, + }; static struct btf_pprint_test_meta {
@@ -3687,9 +3815,9 @@ static int check_line(const char *expected_line, int nexpected_line, } -static int do_test_pprint (void ) +static int do_test_pprint (int test_num) { -const struct btf_raw_test *test = &pprint_test_template; +const struct btf_raw_test *test = &pprint_test_template[test_num];struct bpf_create_map_attr create_attr = {};bool ordered_map, lossless_map, percpu_map;int err, ret, num_cpus, rounded_value_size;
@@ -3705,7 +3833,7 @@ static int do_test_pprint(void)uint8_t *raw_btf;ssize_t nread; -fprintf (stderr,"%s......" , test->descr); +fprintf (stderr,"%s(#%d)......" , test->descr, test_num); raw_btf =btf_raw_create (&hdr_tmpl, test->raw_types, test->str_sec, test->str_sec_size, &raw_btf_size,NULL );
@@ -3898,15 +4026,27 @@ static int test_pprint(void)unsigned int i;int err =0 ; +/* test various maps with the first test template */ for (i =0 ; i <ARRAY_SIZE (pprint_tests_meta); i++) { - pprint_test_template.descr = pprint_tests_meta[i].descr; - pprint_test_template.map_type = pprint_tests_meta[i].map_type; - pprint_test_template.map_name = pprint_tests_meta[i].map_name; - pprint_test_template.ordered_map = pprint_tests_meta[i].ordered_map; - pprint_test_template.lossless_map = pprint_tests_meta[i].lossless_map; - pprint_test_template.percpu_map = pprint_tests_meta[i].percpu_map; - - err |=count_result (do_test_pprint ()); + pprint_test_template[0 ].descr = pprint_tests_meta[i].descr; + pprint_test_template[0 ].map_type = pprint_tests_meta[i].map_type; + pprint_test_template[0 ].map_name = pprint_tests_meta[i].map_name; + pprint_test_template[0 ].ordered_map = pprint_tests_meta[i].ordered_map; + pprint_test_template[0 ].lossless_map = pprint_tests_meta[i].lossless_map; + pprint_test_template[0 ].percpu_map = pprint_tests_meta[i].percpu_map; + + err |=count_result (do_test_pprint (0 )); + } + +/* test rest test templates with the first map */ +for (i =1 ; i <ARRAY_SIZE (pprint_test_template); i++) { + pprint_test_template[i].descr = pprint_tests_meta[0 ].descr; + pprint_test_template[i].map_type = pprint_tests_meta[0 ].map_type; + pprint_test_template[i].map_name = pprint_tests_meta[0 ].map_name; + pprint_test_template[i].ordered_map = pprint_tests_meta[0 ].ordered_map; + pprint_test_template[i].lossless_map = pprint_tests_meta[0 ].lossless_map; + pprint_test_template[i].percpu_map = pprint_tests_meta[0 ].percpu_map; + err |=count_result (do_test_pprint (i)); }return err;
Signature · 1 line
2.17.1
PATCH v3 7/8 tools: bpftool: refactor btf_dumper_int_bits()
In reply to Yonghong SongThe core dump funcitonality in btf_dumper_int_bits() is refactored into a separate function btf_dumper_bitfield() which will be used by the next patch.
--- tools/bpf/bpftool/btf_dumper.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-)
tools/bpf/bpftool/btf_dumper.c +19 -6
@@ -73,20 +73,17 @@ static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,return ret; } -static void btf_dumper_int_bits (__u32 int_type,__u8 bit_offset, +static void btf_dumper_bitfield (__u32 nr_bits,__u8 bit_offset,const void *data,json_writer_t *jw,bool is_plain_text) {int left_shift_bits, right_shift_bits; -int nr_bits =BTF_INT_BITS (int_type); -int total_bits_offset;int bytes_to_copy;int bits_to_copy;__u64 print_num; - total_bits_offset = bit_offset +BTF_INT_OFFSET (int_type); - data +=BITS_ROUNDDOWN_BYTES (total_bits_offset); - bit_offset =BITS_PER_BYTE_MASKED (total_bits_offset); + data +=BITS_ROUNDDOWN_BYTES (bit_offset); + bit_offset =BITS_PER_BYTE_MASKED (bit_offset); bits_to_copy = bit_offset + nr_bits; bytes_to_copy =BITS_ROUNDUP_BYTES (bits_to_copy);
@@ -109,6 +106,22 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,jsonw_printf (jw,"%llu" , print_num); } + +static void btf_dumper_int_bits (__u32 int_type,__u8 bit_offset, +const void *data,json_writer_t *jw, +bool is_plain_text) +{ +int nr_bits =BTF_INT_BITS (int_type); +int total_bits_offset; + +/* bits_offset is at most 7. +* BTF_INT_OFFSET() cannot exceed 64 bits. +*/ + total_bits_offset = bit_offset +BTF_INT_OFFSET (int_type); +btf_dumper_bitfield (nr_bits, total_bits_offset, data, jw, + is_plain_text); +} +static int btf_dumper_int (const struct btf_type *t,__u8 bit_offset,const void *data,json_writer_t *jw,bool is_plain_text)
Signature · 1 line
2.17.1
PATCH v3 8/8 tools: bpftool: support pretty print with kind_flag set
In reply to Yonghong SongThe following example shows map pretty print with structures which include bitfield members.
enum A { A1, A2, A3, A4, A5 };
typedef enum A ___A;
struct tmp_t {
char a1:4;
int a2:4;
int :4;
__u32 a3:4;
int b;
___A b1:4;
enum A b2:4;
};
struct bpf_map_def SEC("maps") tmpmap = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(__u32),
.value_size = sizeof(struct tmp_t),
.max_entries = 1,
};
BPF_ANNOTATE_KV_PAIR(tmpmap, int, struct tmp_t);and the following map update in the bpf program:
key = 0;
struct tmp_t t = {};
t.a1 = 2;
t.a2 = 4;
t.a3 = 6;
t.b = 7;
t.b1 = 8;
t.b2 = 10;
bpf_map_update_elem(&tmpmap, &key, &t, 0);With this patch, I am able to print out the map values correctly with this patch:
bpftool map dump id 187
[{
"key": 0,
"value": {
"a1": 0x2,
"a2": 0x4,
"a3": 0x6,
"b": 7,
"b1": 0x8,
"b2": 0xa
}
}]
Previously, if a function prototype argument has a typedef type, the prototype is not printed since
function __btf_dumper_type_only() bailed out with error if the type is a typedef. This commit corrected this behavior by printing out typedef properly.
The following example shows forward type and typedef type can be properly printed in function prototype with modified test_btf_haskv.c.
struct t;
union u;
__attribute__((noinline))
static int test_long_fname_1(struct dummy_tracepoint_args *arg,
struct t *p1, union u *p2,
__u32 unused)
...
int _dummy_tracepoint(struct dummy_tracepoint_args *arg) {
return test_long_fname_1(arg, 0, 0, 0);
} $ bpftool p d xlated id 24
...
int test_long_fname_1(struct dummy_tracepoint_args * arg,
struct t * p1, union u * p2,
__u32 unused)
...--- tools/bpf/bpftool/btf_dumper.c | 36 +++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-)
tools/bpf/bpftool/btf_dumper.c +27 -9
@@ -193,6 +193,7 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,const struct btf_type *t;struct btf_member *m;const void *data_off; +int kind_flag;int ret =0 ;int i, vlen;
@@ -200,18 +201,32 @@ static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,if (!t)return -EINVAL ; + kind_flag =BTF_INFO_KFLAG (t->info); vlen =BTF_INFO_VLEN (t->info);jsonw_start_object (d->jw); m = (struct btf_member *)(t +1 );for (i =0 ; i < vlen; i++) { - data_off = data +BITS_ROUNDDOWN_BYTES (m[i].offset); +__u32 bit_offset = m[i].offset; +__u32 bitfield_size =0 ; + +if (kind_flag) { + bitfield_size =BTF_MEMBER_BITFIELD_SIZE (bit_offset); + bit_offset =BTF_MEMBER_BIT_OFFSET (bit_offset); + } +jsonw_name (d->jw,btf__name_by_offset (d->btf, m[i].name_off)); - ret =btf_dumper_do_type (d, m[i].type, -BITS_PER_BYTE_MASKED (m[i].offset), - data_off); -if (ret) -break ; +if (bitfield_size) { +btf_dumper_bitfield (bitfield_size, bit_offset, + data, d->jw, d->is_plain_text); + }else { + data_off = data +BITS_ROUNDDOWN_BYTES (bit_offset); + ret =btf_dumper_do_type (d, m[i].type, +BITS_PER_BYTE_MASKED (bit_offset), + data_off); +if (ret) +break ; + } }jsonw_end_object (d->jw);
@@ -298,6 +313,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,switch (BTF_INFO_KIND (t->info)) {case BTF_KIND_INT : +case BTF_KIND_TYPEDEF :BTF_PRINT_ARG ("%s " ,btf__name_by_offset (btf, t->name_off));break ;case BTF_KIND_STRUCT :
@@ -321,10 +337,11 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,BTF_PRINT_TYPE (t->type);BTF_PRINT_ARG ("* " );break ; -case BTF_KIND_UNKN :case BTF_KIND_FWD : -case BTF_KIND_TYPEDEF : -return -1 ; +BTF_PRINT_ARG ("%s %s " , +BTF_INFO_KFLAG (t->info) ?"union" :"struct" , +btf__name_by_offset (btf, t->name_off)); +break ;case BTF_KIND_VOLATILE :BTF_PRINT_ARG ("volatile " );BTF_PRINT_TYPE (t->type);
@@ -348,6 +365,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,if (pos ==-1 )return -1 ;break ; +case BTF_KIND_UNKN :default :return -1 ; }
Signature · 1 line
2.17.1
Quoted 63 lines · Yonghong Song
Commit
69b693f0aefa("bpf: btf: Introduce BPF Type Format (BTF)") introduced BTF, a debug info format for BTF.The original design has a couple of issues though.
First, the bitfield size is only encoded in int type.
If the struct member bitfield type is enum, pahole ([1])
or llvm is forced to replace enum with int type. As a result, the original type information gets lost.Second, the original BTF design does not envision the possibility of BTF=>header_file conversion ([2]), hence does not encode "struct" or "union" info for a forward type. Such information is necessary to convert BTF to a header file.
This patch set fixed the issue by introducing
kind_flag, using one bit intype->info. Whenkind_flag, the struct/unionbtf_member->offsetwill encode bothbitfield_sizeandbit_offset, covering both int and enum base types. Thekind_flagis also used to indicate whether the forward type is a union (when set) or a struct.Patch #1 refactors function
btf_int_bits_seq_show()so Patch #2 can reuse part of the function.
Patch #2 implementedkind_flagsupport for struct/union/fwd types. Patch #3 addedkind_flagsupport for cgroup local storage map pretty print. Patch #4 syncs kernel uapibtf.hto tools directory.
Patch #5 added unit tests forkind_flag.
Patch #6 added tests for kernel bpffs based pretty print withkind_flag. Patch #7 refactors functionbtf_dumper_int_bits()so Patch #8 can reuse part of the function.
Patch #8 added bpftool support of pretty print withkind_flagset.[1] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/commit/?id=b18354f64cc215368c3bc0df4a7e5341c55c378c
[2] https://lwn.net/SubscriberLink/773198/fe3074838f5c3f26/Change logs:
v2 -> v3:
. Relocated comments about bitfield_size/bit_offset interpretation
of the "offset" field right before the "offset" struct member.
. Added missing byte alignment checking for non-bitfield enum
member of a struct withkind_flagset.
. Added two test cases in unit tests for struct type,kind_flagset,
non-bitfield int/enum member, not-byte aligned bit offsets.
. Added comments to help understand there is no overflow for
total_bits_offsetin bpftool functionbtf_dumper_int_bits().
. Added explanation of typedef type dumping fix in Patch #8 commit
message.v1 -> v2:
. Ifkind_flagis set for a structure, ensure an int member,
whether it is a bitfield or not, is a regular int type.
. Added support so cgroup local storage map pretty print
works withkind_flag.Yonghong Song (8):
bpf: btf: refactorbtf_int_bits_seq_show()
bpf: btf: fix struct/union/fwd types withkind_flag
bpf: enable cgroup local storage map pretty print withkind_flagtools/bpf: syncbtf.hheader from kernel to toolstools/bpf: addtest_btfunit tests forkind_flagtools/bpf: test kernel bpffs map pretty print with structkind_flag
tools: bpftool: refactorbtf_dumper_int_bits()
tools: bpftool: support pretty print withkind_flagset
Quoted 9 lines
include/linux/btf.h | 5 +- include/uapi/linux/btf.h | 20 +- kernel/bpf/btf.c | 352 +++++++++++-- kernel/bpf/local_storage.c | 17 +- tools/bpf/bpftool/btf_dumper.c | 61 ++- tools/include/uapi/linux/btf.h | 20 +- tools/testing/selftests/bpf/test_btf.c | 664 ++++++++++++++++++++++++- 7 files changed, 1047 insertions(+), 92 deletions(-)
Applied to bpf-next, thanks!