Commit | Line | Data |
---|---|---|
6e05abc9 AN |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <test_progs.h> | |
3 | #include <bpf/btf.h> | |
4 | ||
5 | static int duration = 0; | |
2d2a3ad8 AN |
6 | |
7 | void btf_dump_printf(void *ctx, const char *fmt, va_list args) | |
8 | { | |
9 | vfprintf(ctx, fmt, args); | |
10 | } | |
11 | ||
6e05abc9 | 12 | static struct btf_dump_test_case { |
2d2a3ad8 | 13 | const char *name; |
6e05abc9 | 14 | const char *file; |
eed7818a | 15 | bool known_ptr_sz; |
2d2a3ad8 | 16 | } btf_dump_test_cases[] = { |
60ba87bb AN |
17 | {"btf_dump: syntax", "btf_dump_test_case_syntax", true}, |
18 | {"btf_dump: ordering", "btf_dump_test_case_ordering", false}, | |
19 | {"btf_dump: padding", "btf_dump_test_case_padding", true}, | |
20 | {"btf_dump: packing", "btf_dump_test_case_packing", true}, | |
21 | {"btf_dump: bitfields", "btf_dump_test_case_bitfields", true}, | |
22 | {"btf_dump: multidim", "btf_dump_test_case_multidim", false}, | |
23 | {"btf_dump: namespacing", "btf_dump_test_case_namespacing", false}, | |
2d2a3ad8 AN |
24 | }; |
25 | ||
60ba87bb | 26 | static int btf_dump_all_types(const struct btf *btf, void *ctx) |
2d2a3ad8 | 27 | { |
487ef148 | 28 | size_t type_cnt = btf__type_cnt(btf); |
2d2a3ad8 AN |
29 | struct btf_dump *d; |
30 | int err = 0, id; | |
31 | ||
60ba87bb | 32 | d = btf_dump__new(btf, btf_dump_printf, ctx, NULL); |
bad2e478 AN |
33 | err = libbpf_get_error(d); |
34 | if (err) | |
35 | return err; | |
2d2a3ad8 | 36 | |
487ef148 | 37 | for (id = 1; id < type_cnt; id++) { |
2d2a3ad8 AN |
38 | err = btf_dump__dump_type(d, id); |
39 | if (err) | |
40 | goto done; | |
41 | } | |
42 | ||
43 | done: | |
44 | btf_dump__free(d); | |
45 | return err; | |
46 | } | |
47 | ||
6e05abc9 | 48 | static int test_btf_dump_case(int n, struct btf_dump_test_case *t) |
2d2a3ad8 AN |
49 | { |
50 | char test_file[256], out_file[256], diff_cmd[1024]; | |
51 | struct btf *btf = NULL; | |
52 | int err = 0, fd = -1; | |
53 | FILE *f = NULL; | |
54 | ||
6e05abc9 | 55 | snprintf(test_file, sizeof(test_file), "%s.o", t->file); |
2d2a3ad8 AN |
56 | |
57 | btf = btf__parse_elf(test_file, NULL); | |
bad2e478 | 58 | if (!ASSERT_OK_PTR(btf, "btf_parse_elf")) { |
2d2a3ad8 AN |
59 | err = -PTR_ERR(btf); |
60 | btf = NULL; | |
61 | goto done; | |
62 | } | |
63 | ||
eed7818a AN |
64 | /* tests with t->known_ptr_sz have no "long" or "unsigned long" type, |
65 | * so it's impossible to determine correct pointer size; but if they | |
66 | * do, it should be 8 regardless of host architecture, becaues BPF | |
67 | * target is always 64-bit | |
68 | */ | |
69 | if (!t->known_ptr_sz) { | |
70 | btf__set_pointer_size(btf, 8); | |
71 | } else { | |
72 | CHECK(btf__pointer_size(btf) != 8, "ptr_sz", "exp %d, got %zu\n", | |
73 | 8, btf__pointer_size(btf)); | |
74 | } | |
75 | ||
6e05abc9 | 76 | snprintf(out_file, sizeof(out_file), "/tmp/%s.output.XXXXXX", t->file); |
2d2a3ad8 | 77 | fd = mkstemp(out_file); |
7a2fa70a | 78 | if (!ASSERT_GE(fd, 0, "create_tmp")) { |
2d2a3ad8 AN |
79 | err = fd; |
80 | goto done; | |
81 | } | |
82 | f = fdopen(fd, "w"); | |
6e05abc9 | 83 | if (CHECK(f == NULL, "open_tmp", "failed to open file: %s(%d)\n", |
2d2a3ad8 AN |
84 | strerror(errno), errno)) { |
85 | close(fd); | |
86 | goto done; | |
87 | } | |
88 | ||
60ba87bb | 89 | err = btf_dump_all_types(btf, f); |
2d2a3ad8 AN |
90 | fclose(f); |
91 | close(fd); | |
6e05abc9 | 92 | if (CHECK(err, "btf_dump", "failure during C dumping: %d\n", err)) { |
2d2a3ad8 AN |
93 | goto done; |
94 | } | |
95 | ||
6e05abc9 | 96 | snprintf(test_file, sizeof(test_file), "progs/%s.c", t->file); |
e91dcb53 IL |
97 | if (access(test_file, R_OK) == -1) |
98 | /* | |
99 | * When the test is run with O=, kselftest copies TEST_FILES | |
100 | * without preserving the directory structure. | |
101 | */ | |
6e05abc9 | 102 | snprintf(test_file, sizeof(test_file), "%s.c", t->file); |
2d2a3ad8 AN |
103 | /* |
104 | * Diff test output and expected test output, contained between | |
105 | * START-EXPECTED-OUTPUT and END-EXPECTED-OUTPUT lines in test case. | |
106 | * For expected output lines, everything before '*' is stripped out. | |
107 | * Also lines containing comment start and comment end markers are | |
108 | * ignored. | |
109 | */ | |
110 | snprintf(diff_cmd, sizeof(diff_cmd), | |
111 | "awk '/START-EXPECTED-OUTPUT/{out=1;next} " | |
112 | "/END-EXPECTED-OUTPUT/{out=0} " | |
113 | "/\\/\\*|\\*\\//{next} " /* ignore comment start/end lines */ | |
114 | "out {sub(/^[ \\t]*\\*/, \"\"); print}' '%s' | diff -u - '%s'", | |
115 | test_file, out_file); | |
116 | err = system(diff_cmd); | |
6e05abc9 | 117 | if (CHECK(err, "diff", |
2d2a3ad8 AN |
118 | "differing test output, output=%s, err=%d, diff cmd:\n%s\n", |
119 | out_file, err, diff_cmd)) | |
120 | goto done; | |
121 | ||
122 | remove(out_file); | |
2d2a3ad8 AN |
123 | |
124 | done: | |
125 | btf__free(btf); | |
126 | return err; | |
127 | } | |
128 | ||
f4d385e4 AN |
129 | static char *dump_buf; |
130 | static size_t dump_buf_sz; | |
131 | static FILE *dump_buf_file; | |
132 | ||
6972dc3b | 133 | static void test_btf_dump_incremental(void) |
f4d385e4 AN |
134 | { |
135 | struct btf *btf = NULL; | |
136 | struct btf_dump *d = NULL; | |
f4d385e4 AN |
137 | int id, err, i; |
138 | ||
139 | dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz); | |
140 | if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream")) | |
141 | return; | |
142 | btf = btf__new_empty(); | |
143 | if (!ASSERT_OK_PTR(btf, "new_empty")) | |
144 | goto err_out; | |
60ba87bb | 145 | d = btf_dump__new(btf, btf_dump_printf, dump_buf_file, NULL); |
f4d385e4 AN |
146 | if (!ASSERT_OK(libbpf_get_error(d), "btf_dump__new")) |
147 | goto err_out; | |
148 | ||
149 | /* First, generate BTF corresponding to the following C code: | |
150 | * | |
151 | * enum { VAL = 1 }; | |
152 | * | |
153 | * struct s { int x; }; | |
154 | * | |
155 | */ | |
156 | id = btf__add_enum(btf, NULL, 4); | |
157 | ASSERT_EQ(id, 1, "enum_id"); | |
158 | err = btf__add_enum_value(btf, "VAL", 1); | |
159 | ASSERT_OK(err, "enum_val_ok"); | |
160 | ||
161 | id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); | |
162 | ASSERT_EQ(id, 2, "int_id"); | |
163 | ||
164 | id = btf__add_struct(btf, "s", 4); | |
165 | ASSERT_EQ(id, 3, "struct_id"); | |
166 | err = btf__add_field(btf, "x", 2, 0, 0); | |
167 | ASSERT_OK(err, "field_ok"); | |
168 | ||
487ef148 | 169 | for (i = 1; i < btf__type_cnt(btf); i++) { |
f4d385e4 AN |
170 | err = btf_dump__dump_type(d, i); |
171 | ASSERT_OK(err, "dump_type_ok"); | |
172 | } | |
173 | ||
174 | fflush(dump_buf_file); | |
175 | dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ | |
176 | ASSERT_STREQ(dump_buf, | |
177 | "enum {\n" | |
178 | " VAL = 1,\n" | |
179 | "};\n" | |
180 | "\n" | |
181 | "struct s {\n" | |
182 | " int x;\n" | |
183 | "};\n\n", "c_dump1"); | |
184 | ||
185 | /* Now, after dumping original BTF, append another struct that embeds | |
186 | * anonymous enum. It also has a name conflict with the first struct: | |
187 | * | |
188 | * struct s___2 { | |
189 | * enum { VAL___2 = 1 } x; | |
190 | * struct s s; | |
191 | * }; | |
192 | * | |
193 | * This will test that btf_dump'er maintains internal state properly. | |
194 | * Note that VAL___2 enum value. It's because we've already emitted | |
195 | * that enum as a global anonymous enum, so btf_dump will ensure that | |
196 | * enum values don't conflict; | |
197 | * | |
198 | */ | |
199 | fseek(dump_buf_file, 0, SEEK_SET); | |
200 | ||
201 | id = btf__add_struct(btf, "s", 4); | |
202 | ASSERT_EQ(id, 4, "struct_id"); | |
203 | err = btf__add_field(btf, "x", 1, 0, 0); | |
204 | ASSERT_OK(err, "field_ok"); | |
205 | err = btf__add_field(btf, "s", 3, 32, 0); | |
206 | ASSERT_OK(err, "field_ok"); | |
207 | ||
487ef148 | 208 | for (i = 1; i < btf__type_cnt(btf); i++) { |
f4d385e4 AN |
209 | err = btf_dump__dump_type(d, i); |
210 | ASSERT_OK(err, "dump_type_ok"); | |
211 | } | |
212 | ||
213 | fflush(dump_buf_file); | |
214 | dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */ | |
215 | ASSERT_STREQ(dump_buf, | |
216 | "struct s___2 {\n" | |
217 | " enum {\n" | |
218 | " VAL___2 = 1,\n" | |
219 | " } x;\n" | |
220 | " struct s s;\n" | |
221 | "};\n\n" , "c_dump1"); | |
222 | ||
223 | err_out: | |
224 | fclose(dump_buf_file); | |
225 | free(dump_buf); | |
226 | btf_dump__free(d); | |
227 | btf__free(btf); | |
228 | } | |
229 | ||
70a9241f AM |
230 | #define STRSIZE 4096 |
231 | ||
232 | static void btf_dump_snprintf(void *ctx, const char *fmt, va_list args) | |
233 | { | |
234 | char *s = ctx, new[STRSIZE]; | |
235 | ||
236 | vsnprintf(new, STRSIZE, fmt, args); | |
237 | if (strlen(s) < STRSIZE) | |
238 | strncat(s, new, STRSIZE - strlen(s) - 1); | |
239 | } | |
240 | ||
241 | static int btf_dump_data(struct btf *btf, struct btf_dump *d, | |
242 | char *name, char *prefix, __u64 flags, void *ptr, | |
243 | size_t ptr_sz, char *str, const char *expected_val) | |
244 | { | |
245 | DECLARE_LIBBPF_OPTS(btf_dump_type_data_opts, opts); | |
246 | size_t type_sz; | |
247 | __s32 type_id; | |
248 | int ret = 0; | |
249 | ||
250 | if (flags & BTF_F_COMPACT) | |
251 | opts.compact = true; | |
252 | if (flags & BTF_F_NONAME) | |
253 | opts.skip_names = true; | |
254 | if (flags & BTF_F_ZERO) | |
255 | opts.emit_zeroes = true; | |
256 | if (prefix) { | |
257 | ASSERT_STRNEQ(name, prefix, strlen(prefix), | |
258 | "verify prefix match"); | |
259 | name += strlen(prefix) + 1; | |
260 | } | |
261 | type_id = btf__find_by_name(btf, name); | |
262 | if (!ASSERT_GE(type_id, 0, "find type id")) | |
263 | return -ENOENT; | |
264 | type_sz = btf__resolve_size(btf, type_id); | |
265 | str[0] = '\0'; | |
266 | ret = btf_dump__dump_type_data(d, type_id, ptr, ptr_sz, &opts); | |
267 | if (type_sz <= ptr_sz) { | |
268 | if (!ASSERT_EQ(ret, type_sz, "failed/unexpected type_sz")) | |
269 | return -EINVAL; | |
270 | } else { | |
271 | if (!ASSERT_EQ(ret, -E2BIG, "failed to return -E2BIG")) | |
272 | return -EINVAL; | |
273 | } | |
274 | if (!ASSERT_STREQ(str, expected_val, "ensure expected/actual match")) | |
275 | return -EFAULT; | |
276 | return 0; | |
277 | } | |
278 | ||
279 | #define TEST_BTF_DUMP_DATA(_b, _d, _prefix, _str, _type, _flags, \ | |
280 | _expected, ...) \ | |
281 | do { \ | |
282 | char __ptrtype[64] = #_type; \ | |
283 | char *_ptrtype = (char *)__ptrtype; \ | |
284 | _type _ptrdata = __VA_ARGS__; \ | |
285 | void *_ptr = &_ptrdata; \ | |
286 | \ | |
287 | (void) btf_dump_data(_b, _d, _ptrtype, _prefix, _flags, \ | |
288 | _ptr, sizeof(_type), _str, \ | |
289 | _expected); \ | |
290 | } while (0) | |
291 | ||
292 | /* Use where expected data string matches its stringified declaration */ | |
293 | #define TEST_BTF_DUMP_DATA_C(_b, _d, _prefix, _str, _type, _flags, \ | |
294 | ...) \ | |
295 | TEST_BTF_DUMP_DATA(_b, _d, _prefix, _str, _type, _flags, \ | |
296 | "(" #_type ")" #__VA_ARGS__, __VA_ARGS__) | |
297 | ||
298 | /* overflow test; pass typesize < expected type size, ensure E2BIG returned */ | |
299 | #define TEST_BTF_DUMP_DATA_OVER(_b, _d, _prefix, _str, _type, _type_sz, \ | |
300 | _expected, ...) \ | |
301 | do { \ | |
302 | char __ptrtype[64] = #_type; \ | |
303 | char *_ptrtype = (char *)__ptrtype; \ | |
304 | _type _ptrdata = __VA_ARGS__; \ | |
305 | void *_ptr = &_ptrdata; \ | |
306 | \ | |
307 | (void) btf_dump_data(_b, _d, _ptrtype, _prefix, 0, \ | |
308 | _ptr, _type_sz, _str, _expected); \ | |
309 | } while (0) | |
310 | ||
311 | #define TEST_BTF_DUMP_VAR(_b, _d, _prefix, _str, _var, _type, _flags, \ | |
312 | _expected, ...) \ | |
313 | do { \ | |
314 | _type _ptrdata = __VA_ARGS__; \ | |
315 | void *_ptr = &_ptrdata; \ | |
316 | \ | |
317 | (void) btf_dump_data(_b, _d, _var, _prefix, _flags, \ | |
318 | _ptr, sizeof(_type), _str, \ | |
319 | _expected); \ | |
320 | } while (0) | |
321 | ||
322 | static void test_btf_dump_int_data(struct btf *btf, struct btf_dump *d, | |
323 | char *str) | |
324 | { | |
a17553dd | 325 | #ifdef __SIZEOF_INT128__ |
486e648c | 326 | unsigned __int128 i = 0xffffffffffffffff; |
a17553dd AM |
327 | |
328 | /* this dance is required because we cannot directly initialize | |
329 | * a 128-bit value to anything larger than a 64-bit value. | |
330 | */ | |
331 | i = (i << 64) | (i - 1); | |
332 | #endif | |
70a9241f AM |
333 | /* simple int */ |
334 | TEST_BTF_DUMP_DATA_C(btf, d, NULL, str, int, BTF_F_COMPACT, 1234); | |
335 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, BTF_F_COMPACT | BTF_F_NONAME, | |
336 | "1234", 1234); | |
337 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, 0, "(int)1234", 1234); | |
338 | ||
339 | /* zero value should be printed at toplevel */ | |
340 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, BTF_F_COMPACT, "(int)0", 0); | |
341 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, BTF_F_COMPACT | BTF_F_NONAME, | |
342 | "0", 0); | |
343 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, BTF_F_COMPACT | BTF_F_ZERO, | |
344 | "(int)0", 0); | |
345 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, | |
346 | BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
347 | "0", 0); | |
348 | TEST_BTF_DUMP_DATA_C(btf, d, NULL, str, int, BTF_F_COMPACT, -4567); | |
349 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, BTF_F_COMPACT | BTF_F_NONAME, | |
350 | "-4567", -4567); | |
351 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, int, 0, "(int)-4567", -4567); | |
352 | ||
353 | TEST_BTF_DUMP_DATA_OVER(btf, d, NULL, str, int, sizeof(int)-1, "", 1); | |
a17553dd AM |
354 | |
355 | #ifdef __SIZEOF_INT128__ | |
091037fb YS |
356 | /* gcc encode unsigned __int128 type with name "__int128 unsigned" in dwarf, |
357 | * and clang encode it with name "unsigned __int128" in dwarf. | |
358 | * Do an availability test for either variant before doing actual test. | |
359 | */ | |
360 | if (btf__find_by_name(btf, "unsigned __int128") > 0) { | |
361 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, unsigned __int128, BTF_F_COMPACT, | |
362 | "(unsigned __int128)0xffffffffffffffff", | |
363 | 0xffffffffffffffff); | |
364 | ASSERT_OK(btf_dump_data(btf, d, "unsigned __int128", NULL, 0, &i, 16, str, | |
365 | "(unsigned __int128)0xfffffffffffffffffffffffffffffffe"), | |
366 | "dump unsigned __int128"); | |
367 | } else if (btf__find_by_name(btf, "__int128 unsigned") > 0) { | |
368 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, __int128 unsigned, BTF_F_COMPACT, | |
369 | "(__int128 unsigned)0xffffffffffffffff", | |
370 | 0xffffffffffffffff); | |
371 | ASSERT_OK(btf_dump_data(btf, d, "__int128 unsigned", NULL, 0, &i, 16, str, | |
372 | "(__int128 unsigned)0xfffffffffffffffffffffffffffffffe"), | |
373 | "dump unsigned __int128"); | |
374 | } else { | |
375 | ASSERT_TRUE(false, "unsigned_int128_not_found"); | |
376 | } | |
a17553dd | 377 | #endif |
70a9241f AM |
378 | } |
379 | ||
380 | static void test_btf_dump_float_data(struct btf *btf, struct btf_dump *d, | |
381 | char *str) | |
382 | { | |
383 | float t1 = 1.234567; | |
384 | float t2 = -1.234567; | |
385 | float t3 = 0.0; | |
386 | double t4 = 5.678912; | |
387 | double t5 = -5.678912; | |
388 | double t6 = 0.0; | |
389 | long double t7 = 9.876543; | |
390 | long double t8 = -9.876543; | |
391 | long double t9 = 0.0; | |
392 | ||
393 | /* since the kernel does not likely have any float types in its BTF, we | |
394 | * will need to add some of various sizes. | |
395 | */ | |
396 | ||
397 | ASSERT_GT(btf__add_float(btf, "test_float", 4), 0, "add float"); | |
398 | ASSERT_OK(btf_dump_data(btf, d, "test_float", NULL, 0, &t1, 4, str, | |
399 | "(test_float)1.234567"), "dump float"); | |
400 | ASSERT_OK(btf_dump_data(btf, d, "test_float", NULL, 0, &t2, 4, str, | |
401 | "(test_float)-1.234567"), "dump float"); | |
402 | ASSERT_OK(btf_dump_data(btf, d, "test_float", NULL, 0, &t3, 4, str, | |
403 | "(test_float)0.000000"), "dump float"); | |
404 | ||
405 | ASSERT_GT(btf__add_float(btf, "test_double", 8), 0, "add_double"); | |
406 | ASSERT_OK(btf_dump_data(btf, d, "test_double", NULL, 0, &t4, 8, str, | |
407 | "(test_double)5.678912"), "dump double"); | |
408 | ASSERT_OK(btf_dump_data(btf, d, "test_double", NULL, 0, &t5, 8, str, | |
409 | "(test_double)-5.678912"), "dump double"); | |
410 | ASSERT_OK(btf_dump_data(btf, d, "test_double", NULL, 0, &t6, 8, str, | |
411 | "(test_double)0.000000"), "dump double"); | |
412 | ||
413 | ASSERT_GT(btf__add_float(btf, "test_long_double", 16), 0, "add long double"); | |
414 | ASSERT_OK(btf_dump_data(btf, d, "test_long_double", NULL, 0, &t7, 16, | |
415 | str, "(test_long_double)9.876543"), | |
416 | "dump long_double"); | |
417 | ASSERT_OK(btf_dump_data(btf, d, "test_long_double", NULL, 0, &t8, 16, | |
418 | str, "(test_long_double)-9.876543"), | |
419 | "dump long_double"); | |
420 | ASSERT_OK(btf_dump_data(btf, d, "test_long_double", NULL, 0, &t9, 16, | |
421 | str, "(test_long_double)0.000000"), | |
422 | "dump long_double"); | |
423 | } | |
424 | ||
425 | static void test_btf_dump_char_data(struct btf *btf, struct btf_dump *d, | |
426 | char *str) | |
427 | { | |
428 | /* simple char */ | |
429 | TEST_BTF_DUMP_DATA_C(btf, d, NULL, str, char, BTF_F_COMPACT, 100); | |
430 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, BTF_F_COMPACT | BTF_F_NONAME, | |
431 | "100", 100); | |
432 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, 0, "(char)100", 100); | |
433 | /* zero value should be printed at toplevel */ | |
434 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, BTF_F_COMPACT, | |
435 | "(char)0", 0); | |
436 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, BTF_F_COMPACT | BTF_F_NONAME, | |
437 | "0", 0); | |
438 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, BTF_F_COMPACT | BTF_F_ZERO, | |
439 | "(char)0", 0); | |
440 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
441 | "0", 0); | |
442 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, char, 0, "(char)0", 0); | |
443 | ||
444 | TEST_BTF_DUMP_DATA_OVER(btf, d, NULL, str, char, sizeof(char)-1, "", 100); | |
445 | } | |
446 | ||
447 | static void test_btf_dump_typedef_data(struct btf *btf, struct btf_dump *d, | |
448 | char *str) | |
449 | { | |
450 | /* simple typedef */ | |
451 | TEST_BTF_DUMP_DATA_C(btf, d, NULL, str, uint64_t, BTF_F_COMPACT, 100); | |
452 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, BTF_F_COMPACT | BTF_F_NONAME, | |
453 | "1", 1); | |
454 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, 0, "(u64)1", 1); | |
455 | /* zero value should be printed at toplevel */ | |
456 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, BTF_F_COMPACT, "(u64)0", 0); | |
457 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, BTF_F_COMPACT | BTF_F_NONAME, | |
458 | "0", 0); | |
459 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, BTF_F_COMPACT | BTF_F_ZERO, | |
460 | "(u64)0", 0); | |
461 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, | |
462 | BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
463 | "0", 0); | |
464 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, u64, 0, "(u64)0", 0); | |
465 | ||
466 | /* typedef struct */ | |
467 | TEST_BTF_DUMP_DATA_C(btf, d, NULL, str, atomic_t, BTF_F_COMPACT, | |
468 | {.counter = (int)1,}); | |
469 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, BTF_F_COMPACT | BTF_F_NONAME, | |
470 | "{1,}", { .counter = 1 }); | |
471 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, 0, | |
472 | "(atomic_t){\n" | |
473 | " .counter = (int)1,\n" | |
474 | "}", | |
475 | {.counter = 1,}); | |
476 | /* typedef with 0 value should be printed at toplevel */ | |
477 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, BTF_F_COMPACT, "(atomic_t){}", | |
478 | {.counter = 0,}); | |
479 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, BTF_F_COMPACT | BTF_F_NONAME, | |
480 | "{}", {.counter = 0,}); | |
481 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, 0, | |
482 | "(atomic_t){\n" | |
483 | "}", | |
484 | {.counter = 0,}); | |
485 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, BTF_F_COMPACT | BTF_F_ZERO, | |
486 | "(atomic_t){.counter = (int)0,}", | |
487 | {.counter = 0,}); | |
488 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, | |
489 | BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
490 | "{0,}", {.counter = 0,}); | |
491 | TEST_BTF_DUMP_DATA(btf, d, NULL, str, atomic_t, BTF_F_ZERO, | |
492 | "(atomic_t){\n" | |
493 | " .counter = (int)0,\n" | |
494 | "}", | |
495 | { .counter = 0,}); | |
496 | ||
497 | /* overflow should show type but not value since it overflows */ | |
498 | TEST_BTF_DUMP_DATA_OVER(btf, d, NULL, str, atomic_t, sizeof(atomic_t)-1, | |
499 | "(atomic_t){\n", { .counter = 1}); | |
500 | } | |
501 | ||
502 | static void test_btf_dump_enum_data(struct btf *btf, struct btf_dump *d, | |
503 | char *str) | |
504 | { | |
505 | /* enum where enum value does (and does not) exist */ | |
506 | TEST_BTF_DUMP_DATA_C(btf, d, "enum", str, enum bpf_cmd, BTF_F_COMPACT, | |
507 | BPF_MAP_CREATE); | |
508 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, BTF_F_COMPACT, | |
509 | "(enum bpf_cmd)BPF_MAP_CREATE", 0); | |
510 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, | |
511 | BTF_F_COMPACT | BTF_F_NONAME, | |
512 | "BPF_MAP_CREATE", | |
513 | BPF_MAP_CREATE); | |
514 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, 0, | |
515 | "(enum bpf_cmd)BPF_MAP_CREATE", | |
516 | BPF_MAP_CREATE); | |
517 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, | |
518 | BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
519 | "BPF_MAP_CREATE", 0); | |
520 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, | |
521 | BTF_F_COMPACT | BTF_F_ZERO, | |
522 | "(enum bpf_cmd)BPF_MAP_CREATE", | |
523 | BPF_MAP_CREATE); | |
524 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, | |
525 | BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
526 | "BPF_MAP_CREATE", BPF_MAP_CREATE); | |
527 | TEST_BTF_DUMP_DATA_C(btf, d, "enum", str, enum bpf_cmd, BTF_F_COMPACT, 2000); | |
528 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, | |
529 | BTF_F_COMPACT | BTF_F_NONAME, | |
530 | "2000", 2000); | |
531 | TEST_BTF_DUMP_DATA(btf, d, "enum", str, enum bpf_cmd, 0, | |
532 | "(enum bpf_cmd)2000", 2000); | |
533 | ||
534 | TEST_BTF_DUMP_DATA_OVER(btf, d, "enum", str, enum bpf_cmd, | |
535 | sizeof(enum bpf_cmd) - 1, "", BPF_MAP_CREATE); | |
536 | } | |
537 | ||
538 | static void test_btf_dump_struct_data(struct btf *btf, struct btf_dump *d, | |
539 | char *str) | |
540 | { | |
541 | DECLARE_LIBBPF_OPTS(btf_dump_type_data_opts, opts); | |
542 | char zero_data[512] = { }; | |
543 | char type_data[512]; | |
544 | void *fops = type_data; | |
545 | void *skb = type_data; | |
546 | size_t type_sz; | |
547 | __s32 type_id; | |
548 | char *cmpstr; | |
549 | int ret; | |
550 | ||
551 | memset(type_data, 255, sizeof(type_data)); | |
552 | ||
553 | /* simple struct */ | |
554 | TEST_BTF_DUMP_DATA_C(btf, d, "struct", str, struct btf_enum, BTF_F_COMPACT, | |
555 | {.name_off = (__u32)3,.val = (__s32)-1,}); | |
556 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, | |
557 | BTF_F_COMPACT | BTF_F_NONAME, | |
558 | "{3,-1,}", | |
559 | { .name_off = 3, .val = -1,}); | |
560 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, 0, | |
561 | "(struct btf_enum){\n" | |
562 | " .name_off = (__u32)3,\n" | |
563 | " .val = (__s32)-1,\n" | |
564 | "}", | |
565 | { .name_off = 3, .val = -1,}); | |
566 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, | |
567 | BTF_F_COMPACT | BTF_F_NONAME, | |
568 | "{-1,}", | |
569 | { .name_off = 0, .val = -1,}); | |
570 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, | |
571 | BTF_F_COMPACT | BTF_F_NONAME | BTF_F_ZERO, | |
572 | "{0,-1,}", | |
573 | { .name_off = 0, .val = -1,}); | |
574 | /* empty struct should be printed */ | |
575 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, BTF_F_COMPACT, | |
576 | "(struct btf_enum){}", | |
577 | { .name_off = 0, .val = 0,}); | |
578 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, | |
579 | BTF_F_COMPACT | BTF_F_NONAME, | |
580 | "{}", | |
581 | { .name_off = 0, .val = 0,}); | |
582 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, 0, | |
583 | "(struct btf_enum){\n" | |
584 | "}", | |
585 | { .name_off = 0, .val = 0,}); | |
586 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, | |
587 | BTF_F_COMPACT | BTF_F_ZERO, | |
588 | "(struct btf_enum){.name_off = (__u32)0,.val = (__s32)0,}", | |
589 | { .name_off = 0, .val = 0,}); | |
590 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct btf_enum, | |
591 | BTF_F_ZERO, | |
592 | "(struct btf_enum){\n" | |
593 | " .name_off = (__u32)0,\n" | |
594 | " .val = (__s32)0,\n" | |
595 | "}", | |
596 | { .name_off = 0, .val = 0,}); | |
597 | ||
598 | /* struct with pointers */ | |
599 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct list_head, BTF_F_COMPACT, | |
600 | "(struct list_head){.next = (struct list_head *)0x1,}", | |
601 | { .next = (struct list_head *)1 }); | |
602 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct list_head, 0, | |
603 | "(struct list_head){\n" | |
604 | " .next = (struct list_head *)0x1,\n" | |
605 | "}", | |
606 | { .next = (struct list_head *)1 }); | |
607 | /* NULL pointer should not be displayed */ | |
608 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct list_head, BTF_F_COMPACT, | |
609 | "(struct list_head){}", | |
610 | { .next = (struct list_head *)0 }); | |
611 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct list_head, 0, | |
612 | "(struct list_head){\n" | |
613 | "}", | |
614 | { .next = (struct list_head *)0 }); | |
615 | ||
616 | /* struct with function pointers */ | |
617 | type_id = btf__find_by_name(btf, "file_operations"); | |
618 | if (ASSERT_GT(type_id, 0, "find type id")) { | |
619 | type_sz = btf__resolve_size(btf, type_id); | |
620 | str[0] = '\0'; | |
621 | ||
622 | ret = btf_dump__dump_type_data(d, type_id, fops, type_sz, &opts); | |
623 | ASSERT_EQ(ret, type_sz, | |
624 | "unexpected return value dumping file_operations"); | |
625 | cmpstr = | |
626 | "(struct file_operations){\n" | |
627 | " .owner = (struct module *)0xffffffffffffffff,\n" | |
628 | " .llseek = (loff_t (*)(struct file *, loff_t, int))0xffffffffffffffff,"; | |
629 | ||
630 | ASSERT_STRNEQ(str, cmpstr, strlen(cmpstr), "file_operations"); | |
631 | } | |
632 | ||
633 | /* struct with char array */ | |
634 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_prog_info, BTF_F_COMPACT, | |
635 | "(struct bpf_prog_info){.name = (char[16])['f','o','o',],}", | |
636 | { .name = "foo",}); | |
637 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_prog_info, | |
638 | BTF_F_COMPACT | BTF_F_NONAME, | |
639 | "{['f','o','o',],}", | |
640 | {.name = "foo",}); | |
641 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_prog_info, 0, | |
642 | "(struct bpf_prog_info){\n" | |
643 | " .name = (char[16])[\n" | |
644 | " 'f',\n" | |
645 | " 'o',\n" | |
646 | " 'o',\n" | |
647 | " ],\n" | |
648 | "}", | |
649 | {.name = "foo",}); | |
650 | /* leading null char means do not display string */ | |
651 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_prog_info, BTF_F_COMPACT, | |
652 | "(struct bpf_prog_info){}", | |
653 | {.name = {'\0', 'f', 'o', 'o'}}); | |
654 | /* handle non-printable characters */ | |
655 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_prog_info, BTF_F_COMPACT, | |
656 | "(struct bpf_prog_info){.name = (char[16])[1,2,3,],}", | |
657 | { .name = {1, 2, 3, 0}}); | |
658 | ||
659 | /* struct with non-char array */ | |
660 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct __sk_buff, BTF_F_COMPACT, | |
661 | "(struct __sk_buff){.cb = (__u32[5])[1,2,3,4,5,],}", | |
662 | { .cb = {1, 2, 3, 4, 5,},}); | |
663 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct __sk_buff, | |
664 | BTF_F_COMPACT | BTF_F_NONAME, | |
665 | "{[1,2,3,4,5,],}", | |
666 | { .cb = { 1, 2, 3, 4, 5},}); | |
667 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct __sk_buff, 0, | |
668 | "(struct __sk_buff){\n" | |
669 | " .cb = (__u32[5])[\n" | |
670 | " 1,\n" | |
671 | " 2,\n" | |
672 | " 3,\n" | |
673 | " 4,\n" | |
674 | " 5,\n" | |
675 | " ],\n" | |
676 | "}", | |
677 | { .cb = { 1, 2, 3, 4, 5},}); | |
678 | /* For non-char, arrays, show non-zero values only */ | |
679 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct __sk_buff, BTF_F_COMPACT, | |
680 | "(struct __sk_buff){.cb = (__u32[5])[0,0,1,0,0,],}", | |
681 | { .cb = { 0, 0, 1, 0, 0},}); | |
682 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct __sk_buff, 0, | |
683 | "(struct __sk_buff){\n" | |
684 | " .cb = (__u32[5])[\n" | |
685 | " 0,\n" | |
686 | " 0,\n" | |
687 | " 1,\n" | |
688 | " 0,\n" | |
689 | " 0,\n" | |
690 | " ],\n" | |
691 | "}", | |
692 | { .cb = { 0, 0, 1, 0, 0},}); | |
693 | ||
694 | /* struct with bitfields */ | |
695 | TEST_BTF_DUMP_DATA_C(btf, d, "struct", str, struct bpf_insn, BTF_F_COMPACT, | |
696 | {.code = (__u8)1,.dst_reg = (__u8)0x2,.src_reg = (__u8)0x3,.off = (__s16)4,.imm = (__s32)5,}); | |
697 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_insn, | |
698 | BTF_F_COMPACT | BTF_F_NONAME, | |
699 | "{1,0x2,0x3,4,5,}", | |
700 | { .code = 1, .dst_reg = 0x2, .src_reg = 0x3, .off = 4, | |
701 | .imm = 5,}); | |
702 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_insn, 0, | |
703 | "(struct bpf_insn){\n" | |
704 | " .code = (__u8)1,\n" | |
705 | " .dst_reg = (__u8)0x2,\n" | |
706 | " .src_reg = (__u8)0x3,\n" | |
707 | " .off = (__s16)4,\n" | |
708 | " .imm = (__s32)5,\n" | |
709 | "}", | |
710 | {.code = 1, .dst_reg = 2, .src_reg = 3, .off = 4, .imm = 5}); | |
711 | ||
712 | /* zeroed bitfields should not be displayed */ | |
713 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_insn, BTF_F_COMPACT, | |
714 | "(struct bpf_insn){.dst_reg = (__u8)0x1,}", | |
715 | { .code = 0, .dst_reg = 1}); | |
716 | ||
717 | /* struct with enum bitfield */ | |
718 | type_id = btf__find_by_name(btf, "fs_context"); | |
719 | if (ASSERT_GT(type_id, 0, "find fs_context")) { | |
720 | type_sz = btf__resolve_size(btf, type_id); | |
721 | str[0] = '\0'; | |
722 | ||
723 | opts.emit_zeroes = true; | |
724 | ret = btf_dump__dump_type_data(d, type_id, zero_data, type_sz, &opts); | |
725 | ASSERT_EQ(ret, type_sz, | |
726 | "unexpected return value dumping fs_context"); | |
727 | ||
728 | ASSERT_NEQ(strstr(str, "FS_CONTEXT_FOR_MOUNT"), NULL, | |
729 | "bitfield value not present"); | |
730 | } | |
731 | ||
732 | /* struct with nested anon union */ | |
733 | TEST_BTF_DUMP_DATA(btf, d, "struct", str, struct bpf_sock_ops, BTF_F_COMPACT, | |
734 | "(struct bpf_sock_ops){.op = (__u32)1,(union){.args = (__u32[4])[1,2,3,4,],.reply = (__u32)1,.replylong = (__u32[4])[1,2,3,4,],},}", | |
735 | { .op = 1, .args = { 1, 2, 3, 4}}); | |
736 | ||
737 | /* union with nested struct */ | |
738 | TEST_BTF_DUMP_DATA(btf, d, "union", str, union bpf_iter_link_info, BTF_F_COMPACT, | |
739 | "(union bpf_iter_link_info){.map = (struct){.map_fd = (__u32)1,},}", | |
740 | { .map = { .map_fd = 1 }}); | |
741 | ||
742 | /* struct skb with nested structs/unions; because type output is so | |
743 | * complex, we don't do a string comparison, just verify we return | |
744 | * the type size as the amount of data displayed. | |
745 | */ | |
746 | type_id = btf__find_by_name(btf, "sk_buff"); | |
747 | if (ASSERT_GT(type_id, 0, "find struct sk_buff")) { | |
748 | type_sz = btf__resolve_size(btf, type_id); | |
749 | str[0] = '\0'; | |
750 | ||
751 | ret = btf_dump__dump_type_data(d, type_id, skb, type_sz, &opts); | |
752 | ASSERT_EQ(ret, type_sz, | |
753 | "unexpected return value dumping sk_buff"); | |
754 | } | |
755 | ||
756 | /* overflow bpf_sock_ops struct with final element nonzero/zero. | |
757 | * Regardless of the value of the final field, we don't have all the | |
758 | * data we need to display it, so we should trigger an overflow. | |
fa721d4f | 759 | * In other words overflow checking should trump "is field zero?" |
70a9241f AM |
760 | * checks because if we've overflowed, it shouldn't matter what the |
761 | * field is - we can't trust its value so shouldn't display it. | |
762 | */ | |
763 | TEST_BTF_DUMP_DATA_OVER(btf, d, "struct", str, struct bpf_sock_ops, | |
764 | sizeof(struct bpf_sock_ops) - 1, | |
765 | "(struct bpf_sock_ops){\n\t.op = (__u32)1,\n", | |
766 | { .op = 1, .skb_tcp_flags = 2}); | |
767 | TEST_BTF_DUMP_DATA_OVER(btf, d, "struct", str, struct bpf_sock_ops, | |
768 | sizeof(struct bpf_sock_ops) - 1, | |
769 | "(struct bpf_sock_ops){\n\t.op = (__u32)1,\n", | |
770 | { .op = 1, .skb_tcp_flags = 0}); | |
771 | } | |
772 | ||
773 | static void test_btf_dump_var_data(struct btf *btf, struct btf_dump *d, | |
774 | char *str) | |
775 | { | |
b16d12f3 | 776 | #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) |
70a9241f AM |
777 | TEST_BTF_DUMP_VAR(btf, d, NULL, str, "cpu_number", int, BTF_F_COMPACT, |
778 | "int cpu_number = (int)100", 100); | |
b16d12f3 | 779 | #endif |
70a9241f AM |
780 | TEST_BTF_DUMP_VAR(btf, d, NULL, str, "cpu_profile_flip", int, BTF_F_COMPACT, |
781 | "static int cpu_profile_flip = (int)2", 2); | |
782 | } | |
783 | ||
784 | static void test_btf_datasec(struct btf *btf, struct btf_dump *d, char *str, | |
785 | const char *name, const char *expected_val, | |
786 | void *data, size_t data_sz) | |
787 | { | |
788 | DECLARE_LIBBPF_OPTS(btf_dump_type_data_opts, opts); | |
789 | int ret = 0, cmp; | |
790 | size_t secsize; | |
791 | __s32 type_id; | |
792 | ||
793 | opts.compact = true; | |
794 | ||
795 | type_id = btf__find_by_name(btf, name); | |
796 | if (!ASSERT_GT(type_id, 0, "find type id")) | |
797 | return; | |
798 | ||
799 | secsize = btf__resolve_size(btf, type_id); | |
800 | ASSERT_EQ(secsize, 0, "verify section size"); | |
801 | ||
802 | str[0] = '\0'; | |
803 | ret = btf_dump__dump_type_data(d, type_id, data, data_sz, &opts); | |
804 | ASSERT_EQ(ret, 0, "unexpected return value"); | |
805 | ||
806 | cmp = strcmp(str, expected_val); | |
807 | ASSERT_EQ(cmp, 0, "ensure expected/actual match"); | |
808 | } | |
809 | ||
810 | static void test_btf_dump_datasec_data(char *str) | |
811 | { | |
f7958752 | 812 | struct btf *btf; |
70a9241f AM |
813 | char license[4] = "GPL"; |
814 | struct btf_dump *d; | |
815 | ||
f7958752 | 816 | btf = btf__parse("xdping_kern.o", NULL); |
70a9241f AM |
817 | if (!ASSERT_OK_PTR(btf, "xdping_kern.o BTF not found")) |
818 | return; | |
819 | ||
60ba87bb | 820 | d = btf_dump__new(btf, btf_dump_snprintf, str, NULL); |
70a9241f | 821 | if (!ASSERT_OK_PTR(d, "could not create BTF dump")) |
f7958752 | 822 | goto out; |
70a9241f AM |
823 | |
824 | test_btf_datasec(btf, d, str, "license", | |
825 | "SEC(\"license\") char[4] _license = (char[4])['G','P','L',];", | |
826 | license, sizeof(license)); | |
f7958752 AN |
827 | out: |
828 | btf_dump__free(d); | |
829 | btf__free(btf); | |
70a9241f AM |
830 | } |
831 | ||
6e05abc9 | 832 | void test_btf_dump() { |
70a9241f | 833 | char str[STRSIZE]; |
70a9241f AM |
834 | struct btf_dump *d; |
835 | struct btf *btf; | |
6e05abc9 | 836 | int i; |
2d2a3ad8 | 837 | |
6e05abc9 AN |
838 | for (i = 0; i < ARRAY_SIZE(btf_dump_test_cases); i++) { |
839 | struct btf_dump_test_case *t = &btf_dump_test_cases[i]; | |
2d2a3ad8 | 840 | |
6e05abc9 AN |
841 | if (!test__start_subtest(t->name)) |
842 | continue; | |
2d2a3ad8 | 843 | |
1440e792 | 844 | test_btf_dump_case(i, &btf_dump_test_cases[i]); |
6e05abc9 | 845 | } |
f4d385e4 AN |
846 | if (test__start_subtest("btf_dump: incremental")) |
847 | test_btf_dump_incremental(); | |
70a9241f AM |
848 | |
849 | btf = libbpf_find_kernel_btf(); | |
850 | if (!ASSERT_OK_PTR(btf, "no kernel BTF found")) | |
851 | return; | |
852 | ||
60ba87bb | 853 | d = btf_dump__new(btf, btf_dump_snprintf, str, NULL); |
70a9241f AM |
854 | if (!ASSERT_OK_PTR(d, "could not create BTF dump")) |
855 | return; | |
856 | ||
857 | /* Verify type display for various types. */ | |
858 | if (test__start_subtest("btf_dump: int_data")) | |
859 | test_btf_dump_int_data(btf, d, str); | |
860 | if (test__start_subtest("btf_dump: float_data")) | |
861 | test_btf_dump_float_data(btf, d, str); | |
862 | if (test__start_subtest("btf_dump: char_data")) | |
863 | test_btf_dump_char_data(btf, d, str); | |
864 | if (test__start_subtest("btf_dump: typedef_data")) | |
865 | test_btf_dump_typedef_data(btf, d, str); | |
866 | if (test__start_subtest("btf_dump: enum_data")) | |
867 | test_btf_dump_enum_data(btf, d, str); | |
868 | if (test__start_subtest("btf_dump: struct_data")) | |
869 | test_btf_dump_struct_data(btf, d, str); | |
870 | if (test__start_subtest("btf_dump: var_data")) | |
871 | test_btf_dump_var_data(btf, d, str); | |
872 | btf_dump__free(d); | |
873 | btf__free(btf); | |
874 | ||
875 | if (test__start_subtest("btf_dump: datasec_data")) | |
876 | test_btf_dump_datasec_data(str); | |
2d2a3ad8 | 877 | } |