[PATCH bpf-next v5 9/9] selftest/bpf: Implement tests for bpftool gen min_core_btf
flat view
STALE1714d
From: Mauricio Vásquez <hidden>
Date: 2022-01-28 22:33:55
Also in:
bpf
Subsystem:
bpf [general] (safe dynamic programs and tools), bpf [selftests] (test runners & infrastructure), kernel selftest framework, the rest · Maintainers:
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi, Shuah Khan, Shuah Khan, Linus Torvalds
This commit implements some integration tests for "BTFGen". The goal of such tests is to verify that the generated BTF file contains the expected types. Signed-off-by: Mauricio Vásquez <redacted> Signed-off-by: Rafael David Tinoco <redacted> Signed-off-by: Lorenzo Fontana <redacted> Signed-off-by: Leonardo Di Donato <redacted> --- tools/testing/selftests/bpf/.gitignore | 1 + tools/testing/selftests/bpf/Makefile | 4 +- .../selftests/bpf/progs/btfgen_btf_source.c | 12 + .../bpf/progs/btfgen_primitives_array.c | 39 +++ .../bpf/progs/btfgen_primitives_struct.c | 40 +++ .../bpf/progs/btfgen_primitives_struct2.c | 44 ++++ .../bpf/progs/btfgen_primitives_union.c | 32 +++ tools/testing/selftests/bpf/test_bpftool.c | 228 ++++++++++++++++++ 8 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/progs/btfgen_btf_source.c create mode 100644 tools/testing/selftests/bpf/progs/btfgen_primitives_array.c create mode 100644 tools/testing/selftests/bpf/progs/btfgen_primitives_struct.c create mode 100644 tools/testing/selftests/bpf/progs/btfgen_primitives_struct2.c create mode 100644 tools/testing/selftests/bpf/progs/btfgen_primitives_union.c create mode 100644 tools/testing/selftests/bpf/test_bpftool.c
diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 1dad8d617da8..308cd5b9cfc4 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore@@ -41,3 +41,4 @@ test_cpp *.tmp xdpxceiver xdp_redirect_multi +test_bpftool
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 945f92d71db3..afc9bff6545d 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile@@ -38,7 +38,8 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_sockmap get_cgroup_id_user \ test_cgroup_storage \ test_tcpnotify_user test_sysctl \ - test_progs-no_alu32 + test_progs-no_alu32 \ + test_bpftool # Also test bpf-gcc, if present ifneq ($(BPF_GCC),)
@@ -212,6 +213,7 @@ $(OUTPUT)/xdping: $(TESTING_HELPERS) $(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS) $(OUTPUT)/test_maps: $(TESTING_HELPERS) $(OUTPUT)/test_verifier: $(TESTING_HELPERS) +$(OUTPUT)/test_bpftool: BPFTOOL ?= $(DEFAULT_BPFTOOL) $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \
diff --git a/tools/testing/selftests/bpf/progs/btfgen_btf_source.c b/tools/testing/selftests/bpf/progs/btfgen_btf_source.c
new file mode 100644
index 000000000000..772d5d1e2788
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btfgen_btf_source.c@@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "core_reloc_types.h" + +// structure made of primitive types +void f0(struct core_reloc_primitives x) {} + +// union made of primite types +void f1(union a_union x) {} + +// arrays +void f2(struct core_reloc_arrays x) {}
diff --git a/tools/testing/selftests/bpf/progs/btfgen_primitives_array.c b/tools/testing/selftests/bpf/progs/btfgen_primitives_array.c
new file mode 100644
index 000000000000..aa514d1bcd96
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btfgen_primitives_array.c@@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bpf.h> +#include <stdint.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> + +char _license[] SEC("license") = "GPL"; + +struct { + char in[256]; + char out[256]; +} data = {}; + +struct core_reloc_arrays_substruct { + int c; + int d; +}; + +struct core_reloc_arrays { + int a[5]; + char b[2][3][4]; + struct core_reloc_arrays_substruct c[3]; + struct core_reloc_arrays_substruct d[1][2]; + struct core_reloc_arrays_substruct f[][2]; +}; +#define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src) + +SEC("raw_tracepoint/sys_enter") +int test_btfgen_primitives(void *ctx) +{ + struct core_reloc_arrays *in = (void *)&data.in; + struct core_reloc_arrays *out = (void *)&data.out; + + if (CORE_READ(&out->a[0], &in->a[0])) + return 1; + + return 0; +}
diff --git a/tools/testing/selftests/bpf/progs/btfgen_primitives_struct.c b/tools/testing/selftests/bpf/progs/btfgen_primitives_struct.c
new file mode 100644
index 000000000000..b761d020fa4d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btfgen_primitives_struct.c@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bpf.h> +#include <stdint.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> + +char _license[] SEC("license") = "GPL"; + +struct { + char in[256]; + char out[256]; +} data = {}; + +enum core_reloc_primitives_enum { + A = 0, + B = 1, +}; + +struct core_reloc_primitives { + char a; + int b; + enum core_reloc_primitives_enum c; + void *d; + int (*f)(const char *); +}; + +#define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src) + +SEC("raw_tracepoint/sys_enter") +int test_btfgen_primitives(void *ctx) +{ + struct core_reloc_primitives *in = (void *)&data.in; + struct core_reloc_primitives *out = (void *)&data.out; + + if (CORE_READ(&out->a, &in->a)) + return 1; + + return 0; +}
diff --git a/tools/testing/selftests/bpf/progs/btfgen_primitives_struct2.c b/tools/testing/selftests/bpf/progs/btfgen_primitives_struct2.c
new file mode 100644
index 000000000000..fe67c9a24a7f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btfgen_primitives_struct2.c@@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* This is almost the same as btfgen_primitives_struct.c but in this one + * a different field is accessed + */ + +#include <linux/bpf.h> +#include <stdint.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> + +char _license[] SEC("license") = "GPL"; + +struct { + char in[256]; + char out[256]; +} data = {}; + +enum core_reloc_primitives_enum { + A = 0, + B = 1, +}; + +struct core_reloc_primitives { + char a; + int b; + enum core_reloc_primitives_enum c; + void *d; + int (*f)(const char *); +}; + +#define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src) + +SEC("raw_tracepoint/sys_enter") +int test_btfgen_primitives(void *ctx) +{ + struct core_reloc_primitives *in = (void *)&data.in; + struct core_reloc_primitives *out = (void *)&data.out; + + if (CORE_READ(&out->b, &in->b)) + return 1; + + return 0; +}
diff --git a/tools/testing/selftests/bpf/progs/btfgen_primitives_union.c b/tools/testing/selftests/bpf/progs/btfgen_primitives_union.c
new file mode 100644
index 000000000000..d4690c9e963c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btfgen_primitives_union.c@@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bpf.h> +#include <stdint.h> +#include <bpf/bpf_helpers.h> +#include <bpf/bpf_core_read.h> + +char _license[] SEC("license") = "GPL"; + +struct { + char in[256]; + char out[256]; +} data = {}; + +union a_union { + int y; + int z; +}; + +#define CORE_READ(dst, src) bpf_core_read(dst, sizeof(*(dst)), src) + +SEC("raw_tracepoint/sys_enter") +int test_btfgen_primitives(void *ctx) +{ + union a_union *in = (void *)&data.in; + union a_union *out = (void *)&data.out; + + if (CORE_READ(&out->y, &in->y)) + return 1; + + return 0; +}
diff --git a/tools/testing/selftests/bpf/test_bpftool.c b/tools/testing/selftests/bpf/test_bpftool.c
new file mode 100644
index 000000000000..ca7facc582d5
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_bpftool.c@@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <linux/limits.h> + +#include <bpf/libbpf.h> +#include <bpf/btf.h> + +#include "bpf_util.h" + +static int run_btfgen(const char *src_btf, const char *dst_btf, const char *objspaths[]) +{ + char command[4096]; + int ret, i, n; + + n = snprintf(command, sizeof(command), + "./tools/build/bpftool/bpftool gen min_core_btf %s %s", src_btf, dst_btf); + assert(n >= 0 && n < sizeof(command)); + + for (i = 0; objspaths[i] != NULL; i++) { + assert(sizeof(command) - strlen(command) > strlen(objspaths[i]) + 1); + strcat(command, " "); + strcat(command, objspaths[i]); + } + + printf("Executing bpftool: %s\n", command); + printf("---\n"); + ret = system(command); + printf("---\n"); + return ret; +} + +struct btfgen_test { + const char *descr; + const char *src_btf; + const char *bpfobj[16]; + void (*run_test)(struct btf *btf); +}; + +static void check_btfgen_primitive_struct(struct btf *btf) +{ + struct btf_member *members; + const struct btf_type *t; + int id; + + assert(btf__type_cnt(btf) == 3); + + id = btf__find_by_name_kind(btf, "core_reloc_primitives", BTF_KIND_STRUCT); + assert(id > 0); + + t = btf__type_by_id(btf, id); + assert(btf_vlen(t) == 1); + + members = btf_members(t); + + id = btf__find_by_name_kind(btf, "char", BTF_KIND_INT); + assert(id > 0); + + /* the type of the struct member must be the char */ + assert(members[0].type == id); +} + +static void check_btfgen_primitive_union(struct btf *btf) +{ + struct btf_member *members; + const struct btf_type *t; + int id; + + /* void, a_union and int*/ + assert(btf__type_cnt(btf) == 3); + + id = btf__find_by_name_kind(btf, "a_union", BTF_KIND_UNION); + assert(id > 0); + + t = btf__type_by_id(btf, id); + assert(btf_vlen(t) == 1); + + members = btf_members(t); + + id = btf__find_by_name_kind(btf, "int", BTF_KIND_INT); + assert(id > 0); + + /* the type of the union member must be the integer */ + assert(members[0].type == id); +} + +static void check_btfgen_primitive_array(struct btf *btf) +{ + int array_id, array_type_id, array_index_type_id; + struct btf_array *array; + + /* void, struct, array, int (array index type) and int (array type) */ + assert(btf__type_cnt(btf) == 5); + + array_id = btf__find_by_name_kind(btf, "", BTF_KIND_ARRAY); + assert(array_id > 0); + + array = btf_array(btf__type_by_id(btf, array_id)); + + array_type_id = btf__find_by_name_kind(btf, "int", BTF_KIND_INT); + assert(array_type_id > 0); + + array_index_type_id = btf__find_by_name_kind(btf, "__ARRAY_SIZE_TYPE__", BTF_KIND_INT); + assert(array_index_type_id > 0); + + /* check that array types are the correct ones */ + assert(array->type == array_type_id); + assert(array->index_type == array_index_type_id); +} + +/* If there are relocations in two different BPF objects involving + * different members of the same struct, then the generated BTF should + * contain a single instance of such struct with both fields. + */ +static void check_btfgen_primitive_structs_different_objects(struct btf *btf) +{ + struct btf_member *members; + const struct btf_type *t; + int struct_id, char_id, int_id; + + /* void, struct, int and char */ + assert(btf__type_cnt(btf) == 4); + + struct_id = btf__find_by_name_kind(btf, "core_reloc_primitives", BTF_KIND_STRUCT); + assert(struct_id > 0); + + t = btf__type_by_id(btf, struct_id); + assert(btf_vlen(t) == 2); + + members = btf_members(t); + + char_id = btf__find_by_name_kind(btf, "char", BTF_KIND_INT); + assert(char_id > 0); + + int_id = btf__find_by_name_kind(btf, "int", BTF_KIND_INT); + assert(int_id > 0); + + for (int i = 0; i < btf_vlen(t); i++) { + const char *name = btf__str_by_offset(btf, members[i].name_off); + + if (!strcmp("a", name)) + assert(members[i].type == char_id); + else if (!strcmp("b", name)) + assert(members[i].type == int_id); + } +} + +static struct btfgen_test btfgen_tests[] = { + { + "primitive struct", + "btfgen_btf_source.o", + { + "btfgen_primitives_struct.o", + }, + check_btfgen_primitive_struct, + }, + { + "primitive union", + "btfgen_btf_source.o", + { + "btfgen_primitives_union.o", + }, + check_btfgen_primitive_union, + }, + { + "primitive array", + "btfgen_btf_source.o", + { + "btfgen_primitives_array.o", + }, + check_btfgen_primitive_array, + }, + { + "primitive structs in different objects", + "btfgen_btf_source.o", + { + "btfgen_primitives_struct.o", + "btfgen_primitives_struct2.o", + }, + check_btfgen_primitive_structs_different_objects, + }, +}; + +void test_gen_min_core_btf(void) +{ + char target_path[PATH_MAX]; + struct btfgen_test *test; + struct btf *dst_btf; + int ret; + + for (int i = 0; i < ARRAY_SIZE(btfgen_tests); i++) { + char dir_path[] = "/tmp/btfgen-XXXXXX"; + + test = &btfgen_tests[i]; + + printf("Running %s\n", test->descr); + + mkdtemp(dir_path); + + snprintf(target_path, sizeof(target_path), "%s/foo.btf", dir_path); + + ret = run_btfgen(test->src_btf, target_path, test->bpfobj); + assert(ret == 0); + + dst_btf = btf__parse(target_path, NULL); + assert(dst_btf != NULL); + + test->run_test(dst_btf); + + printf("Test %s: PASS\n", test->descr); + } + + printf("%s: PASS\n", __func__); +} + +int main(void) +{ + libbpf_set_strict_mode(LIBBPF_STRICT_ALL); + + test_gen_min_core_btf(); + + printf("test_bpftool: OK\n"); + + return 0; +}
--
2.25.1