diff mbox series

[RFC,bpf-next,5/5] selftests/bpf: test kind encoding/decoding

Message ID 1669225312-28949-6-git-send-email-alan.maguire@oracle.com (mailing list archive)
State RFC
Delegated to: BPF
Headers show
Series bpf: making BTF self-describing | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR pending PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-7 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
netdev/tree_selection success Clearly marked for bpf-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 2 maintainers not CCed: linux-kselftest@vger.kernel.org shuah@kernel.org
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning CHECK: Concatenated strings should use spaces between elements WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-9 pending Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 pending Logs for test_maps on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-11 pending Logs for test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 pending Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 pending Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-14 pending Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 pending Logs for test_progs on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-16 pending Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-17 pending Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 pending Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-19 pending Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 pending Logs for test_progs_no_alu32 on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-21 pending Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-22 pending Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 pending Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-24 pending Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 pending Logs for test_progs_no_alu32_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 pending Logs for test_progs_no_alu32_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-27 pending Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 pending Logs for test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 pending Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 pending Logs for test_progs_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-31 pending Logs for test_progs_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-32 pending Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 pending Logs for test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-34 pending Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 pending Logs for test_verifier on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-36 pending Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-37 pending Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 pending Logs for test_verifier on x86_64 with llvm-16

Commit Message

Alan Maguire Nov. 23, 2022, 5:41 p.m. UTC
verify btf__add_kinds() adds kind encodings for all kinds supported,
and after adding kind-related types for unknown kinds, ensure that
parsing uses this info when those kinds are encountered rather than
giving up.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/testing/selftests/bpf/prog_tests/btf_kind.c | 234 ++++++++++++++++++++++
 1 file changed, 234 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_kind.c
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/prog_tests/btf_kind.c b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
new file mode 100644
index 0000000..e0865ab
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
@@ -0,0 +1,234 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022, Oracle and/or its affiliates. */
+
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include <bpf/libbpf.h>
+
+/* verify kind encoding exists for each kind; ensure it consists of a
+ * typedef __BTF_KIND_<num> pointing at a struct __BTF_KIND_<name>,
+ * and the latter has a struct btf_type field and an optional metadata
+ * field.
+ */
+void test_btf_kind_encoding(struct btf *btf)
+{
+	int type_cnt;
+	__u16 i;
+
+	type_cnt = btf__type_cnt(btf);
+	if (!ASSERT_GT(type_cnt, 0, "check_type_cnt"))
+		return;
+
+	for (i = 0; i <= BTF_KIND_MAX; i++) {
+		const struct btf_type *t, *mt;
+		const struct btf_array *a;
+		struct btf_member *m;
+		char name[64];
+		__s32 tid;
+
+		snprintf(name, sizeof(name), BTF_KIND_PFX"%u", i);
+
+		tid = btf__find_by_name(btf, name);
+
+		if (!ASSERT_GT(tid, 0, "find_typedef"))
+			continue;
+
+		t = btf__type_by_id(btf, tid);
+		if (!ASSERT_OK_PTR(t, "typedef_ptr"))
+			continue;
+		t = btf__type_by_id(btf, t->type);
+		if (!ASSERT_OK_PTR(t, "struct_ptr"))
+			continue;
+		if (!ASSERT_EQ(strncmp(btf__name_by_offset(btf, t->name_off),
+				       BTF_KIND_PFX,
+				       strlen(BTF_KIND_PFX)),
+			      0, "check_struct_name"))
+			continue;
+		if (!ASSERT_GT(btf_vlen(t), 0, "check_struct_vlen"))
+			continue;
+		m = btf_members(t);
+		mt = btf__type_by_id(btf, m->type);
+		if (!ASSERT_EQ(btf_kind(mt), BTF_KIND_STRUCT, "member_kind"))
+			continue;
+		if (!ASSERT_EQ(strcmp(btf__name_by_offset(btf, mt->name_off),
+				      "__btf_type"), 0, "member_type_name"))
+			continue;
+		if (btf_vlen(t) == 1)
+			continue;
+		m++;
+		mt = btf__type_by_id(btf, m->type);
+		if (!ASSERT_EQ(btf_kind(mt), BTF_KIND_ARRAY, "member_kind"))
+			continue;
+		a = btf_array(mt);
+		mt = btf__type_by_id(btf, a->type);
+		if (!ASSERT_EQ(strncmp(btf__name_by_offset(btf, mt->name_off),
+				       BTF_KIND_META_PFX,
+				       strlen(BTF_KIND_META_PFX)), 0,
+			       "check_meta_name"))
+			continue;
+	}
+}
+
+static __s32 add_kind(struct btf *btf, __u16 unrec_kind, int nr_meta, int meta_size)
+{
+	__s32 btf_type_id, id, struct_id, array_id;
+	char name[64];
+	int ret;
+
+	/* fabricate unrecognized kind definitions that will be used
+	 * when we add a type using the unrecognized kind later.
+	 */
+	btf_type_id = btf__find_by_name_kind(btf, "__btf_type", BTF_KIND_STRUCT);
+	if (!ASSERT_GT(btf_type_id, 0, "check_btf_type_id"))
+		return btf_type_id;
+
+	if (meta_size > 0) {
+		__s32 __u32_id;
+
+		__u32_id = btf__find_by_name(btf, "__u32");
+		if (!ASSERT_GT(__u32_id, 0, "find_u32"))
+			return __u32_id;
+
+		array_id = btf__add_array(btf, __u32_id, btf_type_id, nr_meta);
+		if (!ASSERT_GT(array_id, 0, "btf__add_array"))
+			return array_id;
+	}
+
+	snprintf(name, sizeof(name), BTF_KIND_PFX "UNREC%d", unrec_kind);
+	struct_id = btf__add_struct(btf, name, sizeof(struct btf_type) + (nr_meta * meta_size));
+	if (!ASSERT_GT(struct_id, 0, "check_struct_id"))
+		return struct_id;
+
+	ret = btf__add_field(btf, "type", btf_type_id, 0, 0);
+	if (!ASSERT_EQ(ret, 0, "btf__add_field"))
+		return ret;
+	if (meta_size > 0) {
+		ret = btf__add_field(btf, "meta", array_id, 96, 0);
+		if (!ASSERT_EQ(ret, 0, "btf__add_field_meta"))
+			return ret;
+	}
+	snprintf(name, sizeof(name), BTF_KIND_PFX "%u", unrec_kind);
+	id = btf__add_typedef(btf, name, struct_id);
+	if (!ASSERT_GT(id, 0, "btf__add_typedef"))
+		return id;
+
+	return btf_type_id;
+}
+
+/* fabricate unrecognized kinds at BTF_KIND_MAX + 1/2/3, and after adding
+ * the appropriate struct/typedefs to the BTF such that it recognizes
+ * these kinds, ensure that parsing of BTF containing the unrecognized kinds
+ * can succeed.
+ */
+void test_btf_kind_decoding(struct btf *btf)
+{
+	const struct btf_type *old_unrecs[6];
+	__s32 id, btf_type_id, unrec_ids[6];
+	__u16 unrec_kind = BTF_KIND_MAX + 1;
+	struct btf_type *new_unrecs[6];
+	const void *raw_data;
+	struct btf *raw_btf;
+	char btf_path[64];
+	__u32 raw_size;
+	int i, fd;
+
+	/* fabricate unrecognized kind definitions that will be used
+	 * when we add a type using the unrecognized kind later.
+	 *
+	 * Kinds are created with
+	 * - no metadata;
+	 * - a single metadata object
+	 * - a vlen-determined number of metadata objects.
+	 */
+	btf_type_id = add_kind(btf, unrec_kind, 0, 0);
+	if (!ASSERT_GT(btf_type_id, 0, "add_kind1"))
+		return;
+	if (!ASSERT_GT(add_kind(btf, unrec_kind + 1, 1, sizeof(struct btf_type)), 0, "add_kind2"))
+		return;
+	if (!ASSERT_GT(add_kind(btf, unrec_kind + 2, 0, sizeof(struct btf_type)), 0, "add_kind3"))
+		return;
+
+	/* now create our types with unrecognized kinds by adding typedef kinds
+	 * and overwriting them with our unrecognized kind values.
+	 */
+	for (i = 0; i < ARRAY_SIZE(unrec_ids); i++) {
+		char name[64];
+
+		snprintf(name, sizeof(name), "unrec_kind%d", i);
+		unrec_ids[i] = btf__add_typedef(btf, name, btf_type_id);
+		if (!ASSERT_GT(unrec_ids[i], 0, "btf__add_typedef"))
+			return;
+		old_unrecs[i] = btf__type_by_id(btf, unrec_ids[i]);
+		if (!ASSERT_OK_PTR(old_unrecs[i], "check_unrec_ptr"))
+			return;
+		new_unrecs[i] = (struct btf_type *)old_unrecs[i];
+	}
+
+	/* add an id after it that we will look up to verify we can parse
+	 * beyond unrecognized kinds.
+	 */
+	id = btf__add_typedef(btf, "test_lookup", btf_type_id);
+	if (!ASSERT_GT(id, 0, "add_test_lookup_id"))
+		return;
+	/* ...but because we converted two BTF types into metadata, the id
+	 * for lookup after modification will be two less.
+	 */
+	id -= 2;
+
+	/* now modify typedefs added above to become unrecognized kinds */
+	new_unrecs[0]->info = (unrec_kind << 24);
+
+	/* unrecognized kind with 1 metadata object */
+	new_unrecs[1]->info = ((unrec_kind + 1) << 24);
+
+	/* unrecognized kind with vlen-determined number of metadata objects; vlen == 1 here */
+	new_unrecs[3]->info = ((unrec_kind + 2) << 24) | 0x1;
+
+	/* having another instance of the unrecognized kind allows us to test
+	 * the caching codepath; we store unrecognized kind data the
+	 * first time we encounter one to avoid the cost of typedef/struct
+	 * lookups each time an unrecognized kind is seen.
+	 */
+	new_unrecs[5]->info = (unrec_kind << 24);
+	/* now write our BTF to a raw file, ready for parsing. */
+	snprintf(btf_path, sizeof(btf_path), "/tmp/btf_kind.%d", getpid());
+	raw_data = btf__raw_data(btf, &raw_size);
+	if (!ASSERT_OK_PTR(raw_data, "check_raw_data"))
+		return;
+	fd = open(btf_path, O_WRONLY | O_CREAT);
+	write(fd, raw_data, raw_size);
+	close(fd);
+
+	/* verify parsing succeeds, and that we can read type info past
+	 * the unrecognized kind.
+	 */
+	raw_btf = btf__parse_raw(btf_path);
+	if (!ASSERT_OK_PTR(raw_btf, "btf__parse_raw"))
+		goto unlink;
+	ASSERT_EQ(btf__find_by_name_kind(raw_btf, "test_lookup",
+					 BTF_KIND_TYPEDEF), id,
+		  "verify_id_lookup");
+
+	/* finally, verify the kernel can handle unrecognized kinds. */
+	ASSERT_EQ(btf__load_into_kernel(raw_btf), 0, "btf_load_into_kernel");
+unlink:
+	unlink(btf_path);
+}
+
+void test_btf_kind(void)
+{
+	struct btf *btf = btf__new_empty();
+
+	if (!ASSERT_OK_PTR(btf, "btf_new"))
+		return;
+
+	if (!ASSERT_OK(btf__add_kinds(btf), "btf__add_kinds"))
+		goto cleanup;
+
+	if (test__start_subtest("btf_kind_encoding"))
+		test_btf_kind_encoding(btf);
+	if (test__start_subtest("btf_kind_decoding"))
+		test_btf_kind_decoding(btf);
+cleanup:
+	btf__free(btf);
+}