Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add a btf_dump test for type_tags

Factor out common routines handling custom BTF from
test_btf_dump_incremental. Then use them in the
test_btf_dump_type_tags.

test_btf_dump_type_tags verifies that a type tag is dumped correctly
with respect to its kflag.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/bpf/20250130201239.1429648-5-ihor.solodrai@linux.dev

authored by

Ihor Solodrai and committed by
Andrii Nakryiko
6c2d2a05 2019c583

+110 -37
+110 -37
tools/testing/selftests/bpf/prog_tests/btf_dump.c
··· 126 126 return err; 127 127 } 128 128 129 - static char *dump_buf; 130 - static size_t dump_buf_sz; 131 - static FILE *dump_buf_file; 129 + struct test_ctx { 130 + struct btf *btf; 131 + struct btf_dump *d; 132 + char *dump_buf; 133 + size_t dump_buf_sz; 134 + FILE *dump_buf_file; 135 + }; 136 + 137 + static void test_ctx__free(struct test_ctx *t) 138 + { 139 + fclose(t->dump_buf_file); 140 + free(t->dump_buf); 141 + btf_dump__free(t->d); 142 + btf__free(t->btf); 143 + } 144 + 145 + static int test_ctx__init(struct test_ctx *t) 146 + { 147 + t->dump_buf_file = open_memstream(&t->dump_buf, &t->dump_buf_sz); 148 + if (!ASSERT_OK_PTR(t->dump_buf_file, "dump_memstream")) 149 + return -1; 150 + t->btf = btf__new_empty(); 151 + if (!ASSERT_OK_PTR(t->btf, "new_empty")) 152 + goto err_out; 153 + t->d = btf_dump__new(t->btf, btf_dump_printf, t->dump_buf_file, NULL); 154 + if (!ASSERT_OK(libbpf_get_error(t->d), "btf_dump__new")) 155 + goto err_out; 156 + 157 + return 0; 158 + 159 + err_out: 160 + test_ctx__free(t); 161 + return -1; 162 + } 163 + 164 + static void test_ctx__dump_and_compare(struct test_ctx *t, 165 + const char *expected_output, 166 + const char *message) 167 + { 168 + int i, err; 169 + 170 + for (i = 1; i < btf__type_cnt(t->btf); i++) { 171 + err = btf_dump__dump_type(t->d, i); 172 + ASSERT_OK(err, "dump_type_ok"); 173 + } 174 + 175 + fflush(t->dump_buf_file); 176 + t->dump_buf[t->dump_buf_sz] = 0; /* some libc implementations don't do this */ 177 + 178 + ASSERT_STREQ(t->dump_buf, expected_output, message); 179 + } 132 180 133 181 static void test_btf_dump_incremental(void) 134 182 { 135 - struct btf *btf = NULL; 136 - struct btf_dump *d = NULL; 137 - int id, err, i; 183 + struct test_ctx t = {}; 184 + struct btf *btf; 185 + int id, err; 138 186 139 - dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz); 140 - if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream")) 187 + if (test_ctx__init(&t)) 141 188 return; 142 - btf = btf__new_empty(); 143 - if (!ASSERT_OK_PTR(btf, "new_empty")) 144 - goto err_out; 145 - d = btf_dump__new(btf, btf_dump_printf, dump_buf_file, NULL); 146 - if (!ASSERT_OK(libbpf_get_error(d), "btf_dump__new")) 147 - goto err_out; 189 + 190 + btf = t.btf; 148 191 149 192 /* First, generate BTF corresponding to the following C code: 150 193 * ··· 225 182 err = btf__add_field(btf, "x", 4, 0, 0); 226 183 ASSERT_OK(err, "field_ok"); 227 184 228 - for (i = 1; i < btf__type_cnt(btf); i++) { 229 - err = btf_dump__dump_type(d, i); 230 - ASSERT_OK(err, "dump_type_ok"); 231 - } 232 - 233 - fflush(dump_buf_file); 234 - dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ 235 - 236 - ASSERT_STREQ(dump_buf, 185 + test_ctx__dump_and_compare(&t, 237 186 "enum x;\n" 238 187 "\n" 239 188 "enum x {\n" ··· 256 221 * enum values don't conflict; 257 222 * 258 223 */ 259 - fseek(dump_buf_file, 0, SEEK_SET); 224 + fseek(t.dump_buf_file, 0, SEEK_SET); 260 225 261 226 id = btf__add_struct(btf, "s", 4); 262 227 ASSERT_EQ(id, 7, "struct_id"); ··· 267 232 err = btf__add_field(btf, "s", 6, 64, 0); 268 233 ASSERT_OK(err, "field_ok"); 269 234 270 - for (i = 1; i < btf__type_cnt(btf); i++) { 271 - err = btf_dump__dump_type(d, i); 272 - ASSERT_OK(err, "dump_type_ok"); 273 - } 274 - 275 - fflush(dump_buf_file); 276 - dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ 277 - ASSERT_STREQ(dump_buf, 235 + test_ctx__dump_and_compare(&t, 278 236 "struct s___2 {\n" 279 237 " enum x x;\n" 280 238 " enum {\n" ··· 276 248 " struct s s;\n" 277 249 "};\n\n" , "c_dump1"); 278 250 279 - err_out: 280 - fclose(dump_buf_file); 281 - free(dump_buf); 282 - btf_dump__free(d); 283 - btf__free(btf); 251 + test_ctx__free(&t); 252 + } 253 + 254 + static void test_btf_dump_type_tags(void) 255 + { 256 + struct test_ctx t = {}; 257 + struct btf *btf; 258 + int id, err; 259 + 260 + if (test_ctx__init(&t)) 261 + return; 262 + 263 + btf = t.btf; 264 + 265 + /* Generate BTF corresponding to the following C code: 266 + * 267 + * struct s { 268 + * void __attribute__((btf_type_tag(\"void_tag\"))) *p1; 269 + * void __attribute__((void_attr)) *p2; 270 + * }; 271 + * 272 + */ 273 + 274 + id = btf__add_type_tag(btf, "void_tag", 0); 275 + ASSERT_EQ(id, 1, "type_tag_id"); 276 + id = btf__add_ptr(btf, id); 277 + ASSERT_EQ(id, 2, "void_ptr_id1"); 278 + 279 + id = btf__add_type_attr(btf, "void_attr", 0); 280 + ASSERT_EQ(id, 3, "type_attr_id"); 281 + id = btf__add_ptr(btf, id); 282 + ASSERT_EQ(id, 4, "void_ptr_id2"); 283 + 284 + id = btf__add_struct(btf, "s", 8); 285 + ASSERT_EQ(id, 5, "struct_id"); 286 + err = btf__add_field(btf, "p1", 2, 0, 0); 287 + ASSERT_OK(err, "field_ok1"); 288 + err = btf__add_field(btf, "p2", 4, 0, 0); 289 + ASSERT_OK(err, "field_ok2"); 290 + 291 + test_ctx__dump_and_compare(&t, 292 + "struct s {\n" 293 + " void __attribute__((btf_type_tag(\"void_tag\"))) *p1;\n" 294 + " void __attribute__((void_attr)) *p2;\n" 295 + "};\n\n", "dump_and_compare"); 296 + 297 + test_ctx__free(&t); 284 298 } 285 299 286 300 #define STRSIZE 4096 ··· 943 873 } 944 874 if (test__start_subtest("btf_dump: incremental")) 945 875 test_btf_dump_incremental(); 876 + 877 + if (test__start_subtest("btf_dump: type_tags")) 878 + test_btf_dump_type_tags(); 946 879 947 880 btf = libbpf_find_kernel_btf(); 948 881 if (!ASSERT_OK_PTR(btf, "no kernel BTF found"))