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