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 <mauricio@xxxxxxxxxx> Signed-off-by: Rafael David Tinoco <rafael.tinoco@xxxxxxxxxxx> Signed-off-by: Lorenzo Fontana <lorenzo.fontana@xxxxxxxxxx> Signed-off-by: Leonardo Di Donato <leonardo.didonato@xxxxxxxxxx> --- 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