[RFC PATCH v3 2/3] selftests/bpf: add tests for BTF type permutation

Donglin Peng posted 3 patches 3 months, 2 weeks ago
[RFC PATCH v3 2/3] selftests/bpf: add tests for BTF type permutation
Posted by Donglin Peng 3 months, 2 weeks ago
Verify that BTF type permutation functionality works correctly.

Cc: Eduard Zingerman <eddyz87@gmail.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: Alan Maguire <alan.maguire@oracle.com>
Cc: Song Liu <song@kernel.org>
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
---
 tools/testing/selftests/bpf/prog_tests/btf.c | 109 ++++++++++++++++---
 1 file changed, 94 insertions(+), 15 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c
index 8a9ba4292109..0688449613d4 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf.c
@@ -6935,14 +6935,18 @@ struct btf_raw_data {
 	__u32 str_sec_size;
 };
 
-struct btf_dedup_test {
+struct btf_dedup_permute_test {
 	const char *descr;
 	struct btf_raw_data input;
 	struct btf_raw_data expect;
-	struct btf_dedup_opts opts;
+	bool permute;
+	struct btf_dedup_opts dedup_opts;
+	struct btf_permute_opts permute_opts;
 };
 
-static struct btf_dedup_test dedup_tests[] = {
+static __u32 permute_ids_sort_by_kind_name[] = {3, 4, 5, 8, 11, 14, 6, 9, 12, 15, 7, 10, 13, 16, 1, 2};
+
+static struct btf_dedup_permute_test dedup_permute_tests[] = {
 
 {
 	.descr = "dedup: unused strings filtering",
@@ -7105,7 +7109,7 @@ static struct btf_dedup_test dedup_tests[] = {
 		},
 		BTF_STR_SEC("\0s\0x"),
 	},
-	.opts = {
+	.dedup_opts = {
 		.force_collisions = true, /* force hash collisions */
 	},
 },
@@ -7151,7 +7155,7 @@ static struct btf_dedup_test dedup_tests[] = {
 		},
 		BTF_STR_SEC("\0s\0x"),
 	},
-	.opts = {
+	.dedup_opts = {
 		.force_collisions = true, /* force hash collisions */
 	},
 },
@@ -7354,7 +7358,7 @@ static struct btf_dedup_test dedup_tests[] = {
 		},
 		BTF_STR_SEC("\0.bss\0t"),
 	},
-	.opts = {
+	.dedup_opts = {
 		.force_collisions = true
 	},
 },
@@ -8022,6 +8026,72 @@ static struct btf_dedup_test dedup_tests[] = {
 		BTF_STR_SEC("\0foo\0x\0y\0foo_ptr"),
 	},
 },
+{
+	.descr = "permute: func/func_param/struct/struct_member tags",
+	.input = {
+		.raw_types = {
+			/* int */
+			BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),	/* [1] */
+			/* void f(int a1, int a2) */
+			BTF_FUNC_PROTO_ENC(0, 2),			/* [2] */
+				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(1), 1),
+				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1),
+			BTF_FUNC_ENC(NAME_NTH(3), 2),			/* [3] */
+			/* struct t {int m1; int m2;} */
+			BTF_STRUCT_ENC(NAME_NTH(4), 2, 8),		/* [4] */
+				BTF_MEMBER_ENC(NAME_NTH(5), 1, 0),
+				BTF_MEMBER_ENC(NAME_NTH(6), 1, 32),
+			/* tag -> f: tag1, tag2, tag3 */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 3, -1),		/* [5] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 3, -1),		/* [6] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 3, -1),		/* [7] */
+			/* tag -> f/a2: tag1, tag2, tag3 */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 3, 1),		/* [8] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 3, 1),		/* [9] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 3, 1),		/* [10] */
+			/* tag -> t: tag1, tag2, tag3 */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 4, -1),		/* [11] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 4, -1),		/* [12] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 4, -1),		/* [13] */
+			/* tag -> t/m2: tag1, tag3 */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 4, 1),		/* [14] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 4, 1),		/* [15] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 4, 1),		/* [16] */
+			BTF_END_RAW,
+		},
+		BTF_STR_SEC("\0a1\0a2\0f\0t\0m1\0m2\0tag1\0tag2\0tag3"),
+	},
+	.expect = {
+		.raw_types = {
+			BTF_FUNC_ENC(NAME_NTH(3), 16),			/* [1] */
+			BTF_STRUCT_ENC(NAME_NTH(4), 2, 8),		/* [2] */
+				BTF_MEMBER_ENC(NAME_NTH(5), 15, 0),
+				BTF_MEMBER_ENC(NAME_NTH(6), 15, 32),
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 1, -1),		/* [3] */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 1,  1),		/* [4] */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 2, -1),		/* [5] */
+			BTF_DECL_TAG_ENC(NAME_NTH(7), 2,  1),		/* [6] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 1, -1),		/* [7] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 1,  1),		/* [8] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 2, -1),		/* [9] */
+			BTF_DECL_TAG_ENC(NAME_NTH(8), 2,  1),		/* [10] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 1, -1),		/* [11] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 1,  1),		/* [12] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 2, -1),		/* [13] */
+			BTF_DECL_TAG_ENC(NAME_NTH(9), 2,  1),		/* [14] */
+			BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),	/* [15] */
+			BTF_FUNC_PROTO_ENC(0, 2),			/* [16] */
+				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(1), 15),
+				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 15),
+			BTF_END_RAW,
+		},
+		BTF_STR_SEC("\0a1\0a2\0f\0t\0m1\0m2\0tag1\0tag2\0tag3"),
+	},
+	.permute = true,
+	.permute_opts = {
+		.ids = permute_ids_sort_by_kind_name,
+	},
+},
 };
 
 static int btf_type_size(const struct btf_type *t)
@@ -8078,9 +8148,9 @@ static void dump_btf_strings(const char *strs, __u32 len)
 	}
 }
 
-static void do_test_dedup(unsigned int test_num)
+static void do_test_dedup_permute(unsigned int test_num)
 {
-	struct btf_dedup_test *test = &dedup_tests[test_num - 1];
+	struct btf_dedup_permute_test *test = &dedup_permute_tests[test_num - 1];
 	__u32 test_nr_types, expect_nr_types, test_btf_size, expect_btf_size;
 	const struct btf_header *test_hdr, *expect_hdr;
 	struct btf *test_btf = NULL, *expect_btf = NULL;
@@ -8124,11 +8194,20 @@ static void do_test_dedup(unsigned int test_num)
 		goto done;
 	}
 
-	test->opts.sz = sizeof(test->opts);
-	err = btf__dedup(test_btf, &test->opts);
-	if (CHECK(err, "btf_dedup failed errno:%d", err)) {
-		err = -1;
-		goto done;
+	if (test->permute) {
+		test->permute_opts.sz = sizeof(test->permute_opts);
+		err = btf__permute(test_btf, &test->permute_opts);
+		if (CHECK(err, "btf_permute failed errno:%d", err)) {
+			err = -1;
+			goto done;
+		}
+	} else {
+		test->dedup_opts.sz = sizeof(test->dedup_opts);
+		err = btf__dedup(test_btf, &test->dedup_opts);
+		if (CHECK(err, "btf_dedup failed errno:%d", err)) {
+			err = -1;
+			goto done;
+		}
 	}
 
 	test_btf_data = btf__raw_data(test_btf, &test_btf_size);
@@ -8249,7 +8328,7 @@ void test_btf(void)
 		do_test_file(i);
 	for (i = 1; i <= ARRAY_SIZE(info_raw_tests); i++)
 		do_test_info_raw(i);
-	for (i = 1; i <= ARRAY_SIZE(dedup_tests); i++)
-		do_test_dedup(i);
+	for (i = 1; i <= ARRAY_SIZE(dedup_permute_tests); i++)
+		do_test_dedup_permute(i);
 	test_pprint();
 }
-- 
2.34.1
Re: [RFC PATCH v3 2/3] selftests/bpf: add tests for BTF type permutation
Posted by Eduard Zingerman 3 months, 1 week ago
On Mon, 2025-10-27 at 21:54 +0800, Donglin Peng wrote:
> Verify that BTF type permutation functionality works correctly.
> 
> Cc: Eduard Zingerman <eddyz87@gmail.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
> Cc: Alan Maguire <alan.maguire@oracle.com>
> Cc: Song Liu <song@kernel.org>
> Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
> ---

Do we need a test case for split btf?
We probably do, as there is index arithmetic etc.

[...]

> @@ -8022,6 +8026,72 @@ static struct btf_dedup_test dedup_tests[] = {
>  		BTF_STR_SEC("\0foo\0x\0y\0foo_ptr"),
>  	},
>  },
> +{
> +	.descr = "permute: func/func_param/struct/struct_member tags",
> +	.input = {
> +		.raw_types = {
> +			/* int */
> +			BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),	/* [1] */
> +			/* void f(int a1, int a2) */
> +			BTF_FUNC_PROTO_ENC(0, 2),			/* [2] */
> +				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(1), 1),
> +				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1),
> +			BTF_FUNC_ENC(NAME_NTH(3), 2),			/* [3] */
> +			/* struct t {int m1; int m2;} */
> +			BTF_STRUCT_ENC(NAME_NTH(4), 2, 8),		/* [4] */
> +				BTF_MEMBER_ENC(NAME_NTH(5), 1, 0),
> +				BTF_MEMBER_ENC(NAME_NTH(6), 1, 32),
> +			/* tag -> f: tag1, tag2, tag3 */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 3, -1),		/* [5] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 3, -1),		/* [6] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 3, -1),		/* [7] */
> +			/* tag -> f/a2: tag1, tag2, tag3 */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 3, 1),		/* [8] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 3, 1),		/* [9] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 3, 1),		/* [10] */
> +			/* tag -> t: tag1, tag2, tag3 */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 4, -1),		/* [11] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 4, -1),		/* [12] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 4, -1),		/* [13] */
> +			/* tag -> t/m2: tag1, tag3 */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 4, 1),		/* [14] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 4, 1),		/* [15] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 4, 1),		/* [16] */
> +			BTF_END_RAW,
> +		},
> +		BTF_STR_SEC("\0a1\0a2\0f\0t\0m1\0m2\0tag1\0tag2\0tag3"),
> +	},

Nit: I think that this test is a bit too large.
     Having fewer decl_tags would still test what we want to test.

> +	.expect = {
> +		.raw_types = {
> +			BTF_FUNC_ENC(NAME_NTH(3), 16),			/* [1] */
> +			BTF_STRUCT_ENC(NAME_NTH(4), 2, 8),		/* [2] */
> +				BTF_MEMBER_ENC(NAME_NTH(5), 15, 0),
> +				BTF_MEMBER_ENC(NAME_NTH(6), 15, 32),
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 1, -1),		/* [3] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 1,  1),		/* [4] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 2, -1),		/* [5] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(7), 2,  1),		/* [6] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 1, -1),		/* [7] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 1,  1),		/* [8] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 2, -1),		/* [9] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(8), 2,  1),		/* [10] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 1, -1),		/* [11] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 1,  1),		/* [12] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 2, -1),		/* [13] */
> +			BTF_DECL_TAG_ENC(NAME_NTH(9), 2,  1),		/* [14] */
> +			BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),	/* [15] */
> +			BTF_FUNC_PROTO_ENC(0, 2),			/* [16] */
> +				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(1), 15),
> +				BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 15),
> +			BTF_END_RAW,
> +		},
> +		BTF_STR_SEC("\0a1\0a2\0f\0t\0m1\0m2\0tag1\0tag2\0tag3"),
> +	},
> +	.permute = true,
> +	.permute_opts = {
> +		.ids = permute_ids_sort_by_kind_name,
> +	},
> +},
>  };

[...]
Re: [RFC PATCH v3 2/3] selftests/bpf: add tests for BTF type permutation
Posted by Donglin Peng 3 months, 1 week ago
On Tue, Oct 28, 2025 at 2:53 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Mon, 2025-10-27 at 21:54 +0800, Donglin Peng wrote:
> > Verify that BTF type permutation functionality works correctly.
> >
> > Cc: Eduard Zingerman <eddyz87@gmail.com>
> > Cc: Alexei Starovoitov <ast@kernel.org>
> > Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>
> > Cc: Alan Maguire <alan.maguire@oracle.com>
> > Cc: Song Liu <song@kernel.org>
> > Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
> > Signed-off-by: Donglin Peng <dolinux.peng@gmail.com>
> > ---
>
> Do we need a test case for split btf?
> We probably do, as there is index arithmetic etc.

Thanks, I will add a test case for split btf.

>
> [...]
>
> > @@ -8022,6 +8026,72 @@ static struct btf_dedup_test dedup_tests[] = {
> >               BTF_STR_SEC("\0foo\0x\0y\0foo_ptr"),
> >       },
> >  },
> > +{
> > +     .descr = "permute: func/func_param/struct/struct_member tags",
> > +     .input = {
> > +             .raw_types = {
> > +                     /* int */
> > +                     BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
> > +                     /* void f(int a1, int a2) */
> > +                     BTF_FUNC_PROTO_ENC(0, 2),                       /* [2] */
> > +                             BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(1), 1),
> > +                             BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 1),
> > +                     BTF_FUNC_ENC(NAME_NTH(3), 2),                   /* [3] */
> > +                     /* struct t {int m1; int m2;} */
> > +                     BTF_STRUCT_ENC(NAME_NTH(4), 2, 8),              /* [4] */
> > +                             BTF_MEMBER_ENC(NAME_NTH(5), 1, 0),
> > +                             BTF_MEMBER_ENC(NAME_NTH(6), 1, 32),
> > +                     /* tag -> f: tag1, tag2, tag3 */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 3, -1),           /* [5] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 3, -1),           /* [6] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 3, -1),           /* [7] */
> > +                     /* tag -> f/a2: tag1, tag2, tag3 */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 3, 1),            /* [8] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 3, 1),            /* [9] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 3, 1),            /* [10] */
> > +                     /* tag -> t: tag1, tag2, tag3 */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 4, -1),           /* [11] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 4, -1),           /* [12] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 4, -1),           /* [13] */
> > +                     /* tag -> t/m2: tag1, tag3 */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 4, 1),            /* [14] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 4, 1),            /* [15] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 4, 1),            /* [16] */
> > +                     BTF_END_RAW,
> > +             },
> > +             BTF_STR_SEC("\0a1\0a2\0f\0t\0m1\0m2\0tag1\0tag2\0tag3"),
> > +     },
>
> Nit: I think that this test is a bit too large.
>      Having fewer decl_tags would still test what we want to test.

Thanks, I agree, I will clean it up.

>
> > +     .expect = {
> > +             .raw_types = {
> > +                     BTF_FUNC_ENC(NAME_NTH(3), 16),                  /* [1] */
> > +                     BTF_STRUCT_ENC(NAME_NTH(4), 2, 8),              /* [2] */
> > +                             BTF_MEMBER_ENC(NAME_NTH(5), 15, 0),
> > +                             BTF_MEMBER_ENC(NAME_NTH(6), 15, 32),
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 1, -1),           /* [3] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 1,  1),           /* [4] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 2, -1),           /* [5] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(7), 2,  1),           /* [6] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 1, -1),           /* [7] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 1,  1),           /* [8] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 2, -1),           /* [9] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(8), 2,  1),           /* [10] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 1, -1),           /* [11] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 1,  1),           /* [12] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 2, -1),           /* [13] */
> > +                     BTF_DECL_TAG_ENC(NAME_NTH(9), 2,  1),           /* [14] */
> > +                     BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [15] */
> > +                     BTF_FUNC_PROTO_ENC(0, 2),                       /* [16] */
> > +                             BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(1), 15),
> > +                             BTF_FUNC_PROTO_ARG_ENC(NAME_NTH(2), 15),
> > +                     BTF_END_RAW,
> > +             },
> > +             BTF_STR_SEC("\0a1\0a2\0f\0t\0m1\0m2\0tag1\0tag2\0tag3"),
> > +     },
> > +     .permute = true,
> > +     .permute_opts = {
> > +             .ids = permute_ids_sort_by_kind_name,
> > +     },
> > +},
> >  };
>
> [...]