diff mbox series

[v2,bpf-next,03/13] selftests/bpf: test distilled base, split BTF generation

Message ID 20240424154806.3417662-4-alan.maguire@oracle.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series bpf: support resilient split BTF | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 949 this patch: 949
netdev/build_tools 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: 955 this patch: 955
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
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: 961 this patch: 961
netdev/checkpatch warning CHECK: Lines should not end with a '(' WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-9 fail Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-13 fail Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 fail Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-6 fail Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 fail Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 fail Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 fail Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-26 fail Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 fail Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 fail Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-41 fail Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-21 fail Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 fail Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 fail Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-31 fail Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 fail Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 fail Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 fail Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 fail Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 fail Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18

Commit Message

Alan Maguire April 24, 2024, 3:47 p.m. UTC
Test generation of split+distilled base BTF, ensuring that

- base BTF STRUCTs which are embedded in split BTF structs are
  represented as 0-member sized structs, allowing size checking
- FWDs are used in place of full named struct/union declarations
- FWDs are used in place of full named enum declarations
- anonymous struct/unions are represented in full
- anonymous enums are represented in full
- types unreferenced from split BTF are not present in distilled
  base BTF

Also test that with vmlinux BTF and split BTF based upon it,
we only represent needed base types referenced from split BTF
in distilled base.

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

Comments

Eduard Zingerman April 30, 2024, 11:50 p.m. UTC | #1
On Wed, 2024-04-24 at 16:47 +0100, Alan Maguire wrote:

[...]

> +static void test_distilled_base(void)
> +{

[...]

> +	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
> +		       "distilled_base") ||
> +	    !ASSERT_OK_PTR(btf3, "distilled_base") ||
> +	    !ASSERT_OK_PTR(btf4, "distilled_split"))
> +		goto cleanup;

Maybe also assert the value of btf4->start_id?
Otherwise look good.

> +
> +	VALIDATE_RAW_BTF(
> +		btf4,
> +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
> +		"[2] FWD 's1' fwd_kind=struct",
> +		"[3] STRUCT '(anon)' size=12 vlen=2\n"
> +		"\t'f1' type_id=1 bits_offset=0\n"
> +		"\t'f2' type_id=2 bits_offset=32",
> +		"[4] FWD 'u1' fwd_kind=union",
> +		"[5] UNION '(anon)' size=4 vlen=1\n"
> +		"\t'f1' type_id=1 bits_offset=0",
> +		"[6] ENUM 'e1' encoding=UNSIGNED size=4 vlen=0",
> +		"[7] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
> +		"\t'av1' val=2",
> +		"[8] ENUM64 'e641' encoding=SIGNED size=8 vlen=0",
> +		"[9] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
> +		"\t'v1' val=1025",
> +		"[10] STRUCT 'embedded' size=4 vlen=0",
> +		"[11] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
> +		"\t'p1' type_id=1",
> +		"[12] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3",
> +		"[13] PTR '(anon)' type_id=2",
> +		"[14] PTR '(anon)' type_id=3",
> +		"[15] CONST '(anon)' type_id=4",
> +		"[16] RESTRICT '(anon)' type_id=5",
> +		"[17] VOLATILE '(anon)' type_id=6",
> +		"[18] TYPEDEF 'et' type_id=7",
> +		"[19] CONST '(anon)' type_id=8",
> +		"[20] PTR '(anon)' type_id=9",
> +		"[21] STRUCT 'with_embedded' size=4 vlen=1\n"
> +		"\t'f1' type_id=10 bits_offset=0",
> +		"[22] FUNC 'fn' type_id=11 linkage=static",
> +		"[23] TYPEDEF 'arraytype' type_id=12");
> +
> +cleanup:
> +	btf__free(btf4);
> +	btf__free(btf3);
> +	btf__free(btf2);
> +	btf__free(btf1);
> +}

[...]
Eduard Zingerman April 30, 2024, 11:55 p.m. UTC | #2
On Wed, 2024-04-24 at 16:47 +0100, Alan Maguire wrote:


[...]

> +static void test_distilled_base(void)
> +{
> 

[...]

> +
> +	VALIDATE_RAW_BTF(
> +		btf1,
> +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
> +		"[2] PTR '(anon)' type_id=1",
> +		"[3] STRUCT 's1' size=8 vlen=1\n"
> +		"\t'f1' type_id=2 bits_offset=0",
> +		"[4] STRUCT '(anon)' size=12 vlen=2\n"
> +		"\t'f1' type_id=1 bits_offset=0\n"
> +		"\t'f2' type_id=3 bits_offset=32",
> +		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
> +		"[6] UNION 'u1' size=12 vlen=2\n"
> +		"\t'f1' type_id=1 bits_offset=0\n"
> +		"\t'f2' type_id=2 bits_offset=0",
> +		"[7] UNION '(anon)' size=4 vlen=1\n"
> +		"\t'f1' type_id=1 bits_offset=0",
> +		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n"
> +		"\t'v1' val=1",
> +		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
> +		"\t'av1' val=2",
> +		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n"
> +		"\t'v1' val=1024",
> +		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
> +		"\t'v1' val=1025",
> +		"[12] STRUCT 'unneeded' size=4 vlen=1\n"
> +		"\t'f1' type_id=1 bits_offset=0",
> +		"[13] STRUCT 'embedded' size=4 vlen=1\n"
> +		"\t'f1' type_id=1 bits_offset=0",
> +		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
> +		"\t'p1' type_id=1",
> +		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3");

Sorry, one more thing,
maybe add a a FUNC_PROTO referencing a struct and refer to this proto from btf2?
To check that FUNC_PROTOs are visited as appropriate.

> +
> +	btf2 = btf__new_empty_split(btf1);
> +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
> +		goto cleanup;
> +

[...]
Alan Maguire May 1, 2024, 5:31 p.m. UTC | #3
On 01/05/2024 00:55, Eduard Zingerman wrote:
> On Wed, 2024-04-24 at 16:47 +0100, Alan Maguire wrote:
> 
> 
> [...]
> 
>> +static void test_distilled_base(void)
>> +{
>>
> 
> [...]
> 
>> +
>> +	VALIDATE_RAW_BTF(
>> +		btf1,
>> +		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
>> +		"[2] PTR '(anon)' type_id=1",
>> +		"[3] STRUCT 's1' size=8 vlen=1\n"
>> +		"\t'f1' type_id=2 bits_offset=0",
>> +		"[4] STRUCT '(anon)' size=12 vlen=2\n"
>> +		"\t'f1' type_id=1 bits_offset=0\n"
>> +		"\t'f2' type_id=3 bits_offset=32",
>> +		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
>> +		"[6] UNION 'u1' size=12 vlen=2\n"
>> +		"\t'f1' type_id=1 bits_offset=0\n"
>> +		"\t'f2' type_id=2 bits_offset=0",
>> +		"[7] UNION '(anon)' size=4 vlen=1\n"
>> +		"\t'f1' type_id=1 bits_offset=0",
>> +		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n"
>> +		"\t'v1' val=1",
>> +		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
>> +		"\t'av1' val=2",
>> +		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n"
>> +		"\t'v1' val=1024",
>> +		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
>> +		"\t'v1' val=1025",
>> +		"[12] STRUCT 'unneeded' size=4 vlen=1\n"
>> +		"\t'f1' type_id=1 bits_offset=0",
>> +		"[13] STRUCT 'embedded' size=4 vlen=1\n"
>> +		"\t'f1' type_id=1 bits_offset=0",
>> +		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
>> +		"\t'p1' type_id=1",
>> +		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3");
> 
> Sorry, one more thing,
> maybe add a a FUNC_PROTO referencing a struct and refer to this proto from btf2?
> To check that FUNC_PROTOs are visited as appropriate.
>

good idea, I'll add this. the test will need to be reworked anyway since
ref types etc will move to split BTF.

>> +
>> +	btf2 = btf__new_empty_split(btf1);
>> +	if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
>> +		goto cleanup;
>> +
> 
> [...]
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/prog_tests/btf_distill.c b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
new file mode 100644
index 000000000000..aae9aef68bd6
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_distill.c
@@ -0,0 +1,253 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024, Oracle and/or its affiliates. */
+
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include "btf_helpers.h"
+
+/* Fabricate base, split BTF with references to base types needed; then create
+ * split BTF with distilled base BTF and ensure expectations are met:
+ *  - only referenced base types from split BTF are present
+ *  - struct/union/enum are represented as FWDs unless anonymous, when they
+ *    are represented in full, or if embedded in a split BTF struct, in which
+ *    case they are represented by a STRUCT with specified size and vlen=0.
+ */
+static void test_distilled_base(void)
+{
+	struct btf *btf1 = NULL, *btf2 = NULL, *btf3 = NULL, *btf4 = NULL;
+
+	btf1 = btf__new_empty();
+	if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
+		return;
+
+	btf__add_int(btf1, "int", 4, BTF_INT_SIGNED);	/* [1] int */
+	btf__add_ptr(btf1, 1);				/* [2] ptr to int */
+	btf__add_struct(btf1, "s1", 8);			/* [3] struct s1 { */
+	btf__add_field(btf1, "f1", 2, 0, 0);		/*      int *f1; */
+							/* } */
+	btf__add_struct(btf1, "", 12);			/* [4] struct { */
+	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */
+	btf__add_field(btf1, "f2", 3, 32, 0);		/*	struct s1 f2; */
+							/* } */
+	btf__add_int(btf1, "unsigned int", 4, 0);	/* [5] unsigned int */
+	btf__add_union(btf1, "u1", 12);			/* [6] union u1 { */
+	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */
+	btf__add_field(btf1, "f2", 2, 0, 0);		/*	int *f2; */
+							/* } */
+	btf__add_union(btf1, "", 4);			/* [7] union { */
+	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */
+							/* } */
+	btf__add_enum(btf1, "e1", 4);			/* [8] enum e1 { */
+	btf__add_enum_value(btf1, "v1", 1);		/*	v1 = 1; */
+							/* } */
+	btf__add_enum(btf1, "", 4);			/* [9] enum { */
+	btf__add_enum_value(btf1, "av1", 2);		/*	av1 = 2; */
+							/* } */
+	btf__add_enum64(btf1, "e641", 8, true);		/* [10] enum64 { */
+	btf__add_enum64_value(btf1, "v1", 1024);	/*	v1 = 1024; */
+							/* } */
+	btf__add_enum64(btf1, "", 8, true);		/* [11] enum64 { */
+	btf__add_enum64_value(btf1, "v1", 1025);	/*	v1 = 1025; */
+							/* } */
+	btf__add_struct(btf1, "unneeded", 4);		/* [12] struct unneeded { */
+	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */
+							/* } */
+	btf__add_struct(btf1, "embedded", 4);		/* [13] struct embedded { */
+	btf__add_field(btf1, "f1", 1, 0, 0);		/*	int f1; */
+							/* } */
+	btf__add_func_proto(btf1, 1);			/* [14] int (*)(int *p1); */
+	btf__add_func_param(btf1, "p1", 1);
+
+	btf__add_array(btf1, 1, 1, 3);			/* [15] int [3]; */
+
+	VALIDATE_RAW_BTF(
+		btf1,
+		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+		"[2] PTR '(anon)' type_id=1",
+		"[3] STRUCT 's1' size=8 vlen=1\n"
+		"\t'f1' type_id=2 bits_offset=0",
+		"[4] STRUCT '(anon)' size=12 vlen=2\n"
+		"\t'f1' type_id=1 bits_offset=0\n"
+		"\t'f2' type_id=3 bits_offset=32",
+		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
+		"[6] UNION 'u1' size=12 vlen=2\n"
+		"\t'f1' type_id=1 bits_offset=0\n"
+		"\t'f2' type_id=2 bits_offset=0",
+		"[7] UNION '(anon)' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n"
+		"\t'v1' val=1",
+		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
+		"\t'av1' val=2",
+		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n"
+		"\t'v1' val=1024",
+		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
+		"\t'v1' val=1025",
+		"[12] STRUCT 'unneeded' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[13] STRUCT 'embedded' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+		"\t'p1' type_id=1",
+		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3");
+
+	btf2 = btf__new_empty_split(btf1);
+	if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
+		goto cleanup;
+
+	btf__add_ptr(btf2, 3);				/* [16] ptr to struct s1 */
+	/* add ptr to struct anon */
+	btf__add_ptr(btf2, 4);				/* [17] ptr to struct (anon) */
+	btf__add_const(btf2, 6);			/* [18] const union u1 */
+	btf__add_restrict(btf2, 7);			/* [19] restrict union (anon) */
+	btf__add_volatile(btf2, 8);			/* [20] volatile enum e1 */
+	btf__add_typedef(btf2, "et", 9);		/* [21] typedef enum (anon) */
+	btf__add_const(btf2, 10);			/* [22] const enum64 e641 */
+	btf__add_ptr(btf2, 11);				/* [23] restrict enum64 (anon) */
+	btf__add_struct(btf2, "with_embedded", 4);	/* [24] struct with_embedded { */
+	btf__add_field(btf2, "f1", 13, 0, 0);		/*	struct embedded f1; */
+							/* } */
+	btf__add_func(btf2, "fn", BTF_FUNC_STATIC, 14);	/* [25] int fn(int p1); */
+	btf__add_typedef(btf2, "arraytype", 15);	/* [26] typedef int[3] foo; */
+
+	VALIDATE_RAW_BTF(
+		btf2,
+		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+		"[2] PTR '(anon)' type_id=1",
+		"[3] STRUCT 's1' size=8 vlen=1\n"
+		"\t'f1' type_id=2 bits_offset=0",
+		"[4] STRUCT '(anon)' size=12 vlen=2\n"
+		"\t'f1' type_id=1 bits_offset=0\n"
+		"\t'f2' type_id=3 bits_offset=32",
+		"[5] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)",
+		"[6] UNION 'u1' size=12 vlen=2\n"
+		"\t'f1' type_id=1 bits_offset=0\n"
+		"\t'f2' type_id=2 bits_offset=0",
+		"[7] UNION '(anon)' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[8] ENUM 'e1' encoding=UNSIGNED size=4 vlen=1\n"
+		"\t'v1' val=1",
+		"[9] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
+		"\t'av1' val=2",
+		"[10] ENUM64 'e641' encoding=SIGNED size=8 vlen=1\n"
+		"\t'v1' val=1024",
+		"[11] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
+		"\t'v1' val=1025",
+		"[12] STRUCT 'unneeded' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[13] STRUCT 'embedded' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[14] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+		"\t'p1' type_id=1",
+		"[15] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3",
+		"[16] PTR '(anon)' type_id=3",
+		"[17] PTR '(anon)' type_id=4",
+		"[18] CONST '(anon)' type_id=6",
+		"[19] RESTRICT '(anon)' type_id=7",
+		"[20] VOLATILE '(anon)' type_id=8",
+		"[21] TYPEDEF 'et' type_id=9",
+		"[22] CONST '(anon)' type_id=10",
+		"[23] PTR '(anon)' type_id=11",
+		"[24] STRUCT 'with_embedded' size=4 vlen=1\n"
+		"\t'f1' type_id=13 bits_offset=0",
+		"[25] FUNC 'fn' type_id=14 linkage=static",
+		"[26] TYPEDEF 'arraytype' type_id=15");
+
+	if (!ASSERT_EQ(0, btf__distill_base(btf2, &btf3, &btf4),
+		       "distilled_base") ||
+	    !ASSERT_OK_PTR(btf3, "distilled_base") ||
+	    !ASSERT_OK_PTR(btf4, "distilled_split"))
+		goto cleanup;
+
+	VALIDATE_RAW_BTF(
+		btf4,
+		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+		"[2] FWD 's1' fwd_kind=struct",
+		"[3] STRUCT '(anon)' size=12 vlen=2\n"
+		"\t'f1' type_id=1 bits_offset=0\n"
+		"\t'f2' type_id=2 bits_offset=32",
+		"[4] FWD 'u1' fwd_kind=union",
+		"[5] UNION '(anon)' size=4 vlen=1\n"
+		"\t'f1' type_id=1 bits_offset=0",
+		"[6] ENUM 'e1' encoding=UNSIGNED size=4 vlen=0",
+		"[7] ENUM '(anon)' encoding=UNSIGNED size=4 vlen=1\n"
+		"\t'av1' val=2",
+		"[8] ENUM64 'e641' encoding=SIGNED size=8 vlen=0",
+		"[9] ENUM64 '(anon)' encoding=SIGNED size=8 vlen=1\n"
+		"\t'v1' val=1025",
+		"[10] STRUCT 'embedded' size=4 vlen=0",
+		"[11] FUNC_PROTO '(anon)' ret_type_id=1 vlen=1\n"
+		"\t'p1' type_id=1",
+		"[12] ARRAY '(anon)' type_id=1 index_type_id=1 nr_elems=3",
+		"[13] PTR '(anon)' type_id=2",
+		"[14] PTR '(anon)' type_id=3",
+		"[15] CONST '(anon)' type_id=4",
+		"[16] RESTRICT '(anon)' type_id=5",
+		"[17] VOLATILE '(anon)' type_id=6",
+		"[18] TYPEDEF 'et' type_id=7",
+		"[19] CONST '(anon)' type_id=8",
+		"[20] PTR '(anon)' type_id=9",
+		"[21] STRUCT 'with_embedded' size=4 vlen=1\n"
+		"\t'f1' type_id=10 bits_offset=0",
+		"[22] FUNC 'fn' type_id=11 linkage=static",
+		"[23] TYPEDEF 'arraytype' type_id=12");
+
+cleanup:
+	btf__free(btf4);
+	btf__free(btf3);
+	btf__free(btf2);
+	btf__free(btf1);
+}
+
+/* create split reference BTF from vmlinux + split BTF with a few type references;
+ * ensure the resultant split reference BTF is as expected, containing only types
+ * needed to disambiguate references from split BTF.
+ */
+static void test_distilled_base_vmlinux(void)
+{
+	struct btf *split_btf = NULL, *vmlinux_btf = btf__load_vmlinux_btf();
+	struct btf *split_dist = NULL, *base_dist = NULL;
+	__s32 int_id, sk_buff_id;
+
+	if (!ASSERT_OK_PTR(vmlinux_btf, "load_vmlinux"))
+		return;
+	int_id = btf__find_by_name_kind(vmlinux_btf, "int", BTF_KIND_INT);
+	if (!ASSERT_GT(int_id, 0, "find_int"))
+		goto cleanup;
+	sk_buff_id = btf__find_by_name_kind(vmlinux_btf, "sk_buff", BTF_KIND_STRUCT);
+	if (!ASSERT_GT(sk_buff_id, 0, "find_sk_buff_id"))
+		goto cleanup;
+	split_btf = btf__new_empty_split(vmlinux_btf);
+	if (!ASSERT_OK_PTR(split_btf, "new_split"))
+		goto cleanup;
+	btf__add_typedef(split_btf, "myint", int_id);
+	btf__add_ptr(split_btf, sk_buff_id);
+
+	if (!ASSERT_EQ(btf__distill_base(split_btf, &base_dist, &split_dist), 0,
+		       "distill_vmlinux_base"))
+		goto cleanup;
+
+	if (!ASSERT_OK_PTR(split_dist, "split_distilled") ||
+	    !ASSERT_OK_PTR(base_dist, "base_dist"))
+		goto cleanup;
+	VALIDATE_RAW_BTF(
+		split_dist,
+		"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
+		"[2] FWD 'sk_buff' fwd_kind=struct",
+		"[3] TYPEDEF 'myint' type_id=1",
+		"[4] PTR '(anon)' type_id=2");
+
+cleanup:
+	btf__free(split_dist);
+	btf__free(base_dist);
+	btf__free(split_btf);
+	btf__free(vmlinux_btf);
+}
+
+void test_btf_distill(void)
+{
+	if (test__start_subtest("distilled_base"))
+		test_distilled_base();
+	if (test__start_subtest("distilled_base_vmlinux"))
+		test_distilled_base_vmlinux();
+}