1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <test_progs.h> 4 #include <bpf/btf.h> 5 #include <search.h> 6 #include "bpf/libbpf_internal.h" 7 #include "tracing_multi.skel.h" 8 #include "tracing_multi_module.skel.h" 9 #include "tracing_multi_intersect.skel.h" 10 #include "trace_helpers.h" 11 12 static __u64 bpf_fentry_test_cookies[] = { 13 8, /* bpf_fentry_test1 */ 14 9, /* bpf_fentry_test2 */ 15 7, /* bpf_fentry_test3 */ 16 5, /* bpf_fentry_test4 */ 17 4, /* bpf_fentry_test5 */ 18 2, /* bpf_fentry_test6 */ 19 3, /* bpf_fentry_test7 */ 20 1, /* bpf_fentry_test8 */ 21 10, /* bpf_fentry_test9 */ 22 6, /* bpf_fentry_test10 */ 23 }; 24 25 static const char * const bpf_fentry_test[] = { 26 "bpf_fentry_test1", 27 "bpf_fentry_test2", 28 "bpf_fentry_test3", 29 "bpf_fentry_test4", 30 "bpf_fentry_test5", 31 "bpf_fentry_test6", 32 "bpf_fentry_test7", 33 "bpf_fentry_test8", 34 "bpf_fentry_test9", 35 "bpf_fentry_test10", 36 }; 37 38 static const char * const bpf_testmod_fentry_test[] = { 39 "bpf_testmod_fentry_test1", 40 "bpf_testmod_fentry_test2", 41 "bpf_testmod_fentry_test3", 42 "bpf_testmod_fentry_test7", 43 "bpf_testmod_fentry_test11", 44 }; 45 46 #define FUNCS_CNT (ARRAY_SIZE(bpf_fentry_test)) 47 48 static int get_random_funcs(const char **funcs) 49 { 50 int i, cnt = 0; 51 52 for (i = 0; i < FUNCS_CNT; i++) { 53 if (rand() % 2) 54 funcs[cnt++] = bpf_fentry_test[i]; 55 } 56 /* we always need at least one.. */ 57 if (!cnt) 58 funcs[cnt++] = bpf_fentry_test[rand() % FUNCS_CNT]; 59 return cnt; 60 } 61 62 static int compare(const void *ppa, const void *ppb) 63 { 64 const char *pa = *(const char **) ppa; 65 const char *pb = *(const char **) ppb; 66 67 return strcmp(pa, pb); 68 } 69 70 static void tdestroy_free_nop(void *ptr) 71 { 72 } 73 74 static __u32 *get_ids(const char * const funcs[], int funcs_cnt, const char *mod) 75 { 76 struct btf *btf, *vmlinux_btf = NULL; 77 __u32 nr, type_id, cnt = 0; 78 void *root = NULL; 79 __u32 *ids = NULL; 80 int i, err = 0; 81 82 btf = btf__load_vmlinux_btf(); 83 if (!ASSERT_OK_PTR(btf, "btf__load_vmlinux_btf")) 84 return NULL; 85 86 if (mod) { 87 vmlinux_btf = btf; 88 btf = btf__load_module_btf(mod, vmlinux_btf); 89 if (!ASSERT_OK_PTR(btf, "btf__load_module_btf")) { 90 btf__free(vmlinux_btf); 91 return NULL; 92 } 93 } 94 95 ids = calloc(funcs_cnt, sizeof(ids[0])); 96 if (!ids) 97 goto out; 98 99 /* 100 * We sort function names by name and search them 101 * below for each function. 102 */ 103 for (i = 0; i < funcs_cnt; i++) { 104 if (!tsearch(&funcs[i], &root, compare)) { 105 ASSERT_FAIL("tsearch failed"); 106 err = -1; 107 goto error; 108 } 109 } 110 111 nr = btf__type_cnt(btf); 112 for (type_id = 1; type_id < nr && cnt < funcs_cnt; type_id++) { 113 const struct btf_type *type; 114 const char *str, ***val; 115 unsigned int idx; 116 117 type = btf__type_by_id(btf, type_id); 118 if (!type) { 119 err = -1; 120 break; 121 } 122 123 if (BTF_INFO_KIND(type->info) != BTF_KIND_FUNC) 124 continue; 125 126 str = btf__name_by_offset(btf, type->name_off); 127 if (!str) { 128 err = -1; 129 break; 130 } 131 132 val = tfind(&str, &root, compare); 133 if (!val) 134 continue; 135 136 /* 137 * We keep pointer for each function name so we can get the original 138 * array index and have the resulting ids array matching the original 139 * function array. 140 * 141 * Doing it this way allow us to easily test the cookies support, 142 * because each cookie is attached to particular function/id. 143 */ 144 idx = *val - funcs; 145 ids[idx] = type_id; 146 cnt++; 147 } 148 149 error: 150 if (err) { 151 free(ids); 152 ids = NULL; 153 } 154 155 out: 156 tdestroy(root, tdestroy_free_nop); 157 btf__free(vmlinux_btf); 158 btf__free(btf); 159 return ids; 160 } 161 162 static void tracing_multi_test_run(struct tracing_multi *skel) 163 { 164 LIBBPF_OPTS(bpf_test_run_opts, topts); 165 int err, prog_fd; 166 167 prog_fd = bpf_program__fd(skel->progs.test_fentry); 168 err = bpf_prog_test_run_opts(prog_fd, &topts); 169 ASSERT_OK(err, "test_run"); 170 171 /* extra +1 count for sleepable programs */ 172 ASSERT_EQ(skel->bss->test_result_fentry, FUNCS_CNT + 1, "test_result_fentry"); 173 ASSERT_EQ(skel->bss->test_result_fexit, FUNCS_CNT + 1, "test_result_fexit"); 174 } 175 176 static void test_skel_api(void) 177 { 178 struct tracing_multi *skel; 179 int err; 180 181 skel = tracing_multi__open_and_load(); 182 if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load")) 183 return; 184 185 skel->bss->pid = getpid(); 186 187 err = tracing_multi__attach(skel); 188 if (!ASSERT_OK(err, "tracing_multi__attach")) 189 goto cleanup; 190 191 tracing_multi_test_run(skel); 192 193 cleanup: 194 tracing_multi__destroy(skel); 195 } 196 197 static void test_link_api_pattern(void) 198 { 199 struct tracing_multi *skel; 200 201 skel = tracing_multi__open_and_load(); 202 if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load")) 203 return; 204 205 skel->bss->pid = getpid(); 206 207 skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry, 208 "bpf_fentry_test*", NULL); 209 if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi")) 210 goto cleanup; 211 212 skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit, 213 "bpf_fentry_test*", NULL); 214 if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi")) 215 goto cleanup; 216 217 skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s, 218 "bpf_fentry_test1", NULL); 219 if (!ASSERT_OK_PTR(skel->links.test_fentry_s, "bpf_program__attach_tracing_multi")) 220 goto cleanup; 221 222 skel->links.test_fexit_s = bpf_program__attach_tracing_multi(skel->progs.test_fexit_s, 223 "bpf_fentry_test1", NULL); 224 if (!ASSERT_OK_PTR(skel->links.test_fexit_s, "bpf_program__attach_tracing_multi")) 225 goto cleanup; 226 227 tracing_multi_test_run(skel); 228 229 cleanup: 230 tracing_multi__destroy(skel); 231 } 232 233 static void test_link_api_ids(bool test_cookies) 234 { 235 LIBBPF_OPTS(bpf_tracing_multi_opts, opts); 236 struct tracing_multi *skel; 237 size_t cnt = FUNCS_CNT; 238 __u32 *ids; 239 240 skel = tracing_multi__open_and_load(); 241 if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load")) 242 return; 243 244 skel->bss->pid = getpid(); 245 skel->bss->test_cookies = test_cookies; 246 247 ids = get_ids(bpf_fentry_test, cnt, NULL); 248 if (!ASSERT_OK_PTR(ids, "get_ids")) 249 goto cleanup; 250 251 opts.ids = ids; 252 opts.cnt = cnt; 253 254 if (test_cookies) 255 opts.cookies = bpf_fentry_test_cookies; 256 257 skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry, 258 NULL, &opts); 259 if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi")) 260 goto cleanup; 261 262 skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit, 263 NULL, &opts); 264 if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi")) 265 goto cleanup; 266 267 /* Only bpf_fentry_test1 is allowed for sleepable programs. */ 268 opts.cnt = 1; 269 skel->links.test_fentry_s = bpf_program__attach_tracing_multi(skel->progs.test_fentry_s, 270 NULL, &opts); 271 if (!ASSERT_OK_PTR(skel->links.test_fentry_s, "bpf_program__attach_tracing_multi")) 272 goto cleanup; 273 274 skel->links.test_fexit_s = bpf_program__attach_tracing_multi(skel->progs.test_fexit_s, 275 NULL, &opts); 276 if (!ASSERT_OK_PTR(skel->links.test_fexit_s, "bpf_program__attach_tracing_multi")) 277 goto cleanup; 278 279 tracing_multi_test_run(skel); 280 281 cleanup: 282 tracing_multi__destroy(skel); 283 free(ids); 284 } 285 286 static void test_module_skel_api(void) 287 { 288 struct tracing_multi_module *skel = NULL; 289 int err; 290 291 skel = tracing_multi_module__open_and_load(); 292 if (!ASSERT_OK_PTR(skel, "tracing_multi__open_and_load")) 293 return; 294 295 skel->bss->pid = getpid(); 296 297 err = tracing_multi_module__attach(skel); 298 if (!ASSERT_OK(err, "tracing_multi__attach")) 299 goto cleanup; 300 301 ASSERT_OK(trigger_module_test_read(1), "trigger_read"); 302 ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry"); 303 ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit"); 304 305 cleanup: 306 tracing_multi_module__destroy(skel); 307 } 308 309 static void test_module_link_api_pattern(void) 310 { 311 struct tracing_multi_module *skel = NULL; 312 313 skel = tracing_multi_module__open_and_load(); 314 if (!ASSERT_OK_PTR(skel, "tracing_multi_module__open_and_load")) 315 return; 316 317 skel->bss->pid = getpid(); 318 319 skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry, 320 "bpf_testmod:bpf_testmod_fentry_test*", NULL); 321 if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi")) 322 goto cleanup; 323 324 skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit, 325 "bpf_testmod:bpf_testmod_fentry_test*", NULL); 326 if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi")) 327 goto cleanup; 328 329 ASSERT_OK(trigger_module_test_read(1), "trigger_read"); 330 ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry"); 331 ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit"); 332 333 cleanup: 334 tracing_multi_module__destroy(skel); 335 } 336 337 static void test_module_link_api_ids(void) 338 { 339 size_t cnt = ARRAY_SIZE(bpf_testmod_fentry_test); 340 LIBBPF_OPTS(bpf_tracing_multi_opts, opts); 341 struct tracing_multi_module *skel = NULL; 342 __u32 *ids; 343 344 skel = tracing_multi_module__open_and_load(); 345 if (!ASSERT_OK_PTR(skel, "tracing_multi_module__open_and_load")) 346 return; 347 348 skel->bss->pid = getpid(); 349 350 ids = get_ids(bpf_testmod_fentry_test, cnt, "bpf_testmod"); 351 if (!ASSERT_OK_PTR(ids, "get_ids")) 352 goto cleanup; 353 354 opts.ids = ids; 355 opts.cnt = cnt; 356 357 skel->links.test_fentry = bpf_program__attach_tracing_multi(skel->progs.test_fentry, 358 NULL, &opts); 359 if (!ASSERT_OK_PTR(skel->links.test_fentry, "bpf_program__attach_tracing_multi")) 360 goto cleanup; 361 362 skel->links.test_fexit = bpf_program__attach_tracing_multi(skel->progs.test_fexit, 363 NULL, &opts); 364 if (!ASSERT_OK_PTR(skel->links.test_fexit, "bpf_program__attach_tracing_multi")) 365 goto cleanup; 366 367 ASSERT_OK(trigger_module_test_read(1), "trigger_read"); 368 ASSERT_EQ(skel->bss->test_result_fentry, 5, "test_result_fentry"); 369 ASSERT_EQ(skel->bss->test_result_fexit, 5, "test_result_fexit"); 370 371 cleanup: 372 tracing_multi_module__destroy(skel); 373 free(ids); 374 } 375 376 static bool is_set(__u32 mask, __u32 bit) 377 { 378 return (1 << bit) & mask; 379 } 380 381 static void __test_intersect(__u32 mask, const struct bpf_program *progs[4], __u64 *test_results[4]) 382 { 383 LIBBPF_OPTS(bpf_tracing_multi_opts, opts); 384 LIBBPF_OPTS(bpf_test_run_opts, topts); 385 struct bpf_link *links[4] = { NULL }; 386 const char *funcs[FUNCS_CNT]; 387 __u64 expected[4]; 388 __u32 *ids, i; 389 int err, cnt; 390 391 /* 392 * We have 4 programs in progs and the mask bits pick which 393 * of them gets attached to randomly chosen functions. 394 */ 395 for (i = 0; i < 4; i++) { 396 if (!is_set(mask, i)) 397 continue; 398 399 cnt = get_random_funcs(funcs); 400 ids = get_ids(funcs, cnt, NULL); 401 if (!ASSERT_OK_PTR(ids, "get_ids")) 402 goto cleanup; 403 404 opts.ids = ids; 405 opts.cnt = cnt; 406 links[i] = bpf_program__attach_tracing_multi(progs[i], NULL, &opts); 407 free(ids); 408 409 if (!ASSERT_OK_PTR(links[i], "bpf_program__attach_tracing_multi")) 410 goto cleanup; 411 412 expected[i] = *test_results[i] + cnt; 413 } 414 415 err = bpf_prog_test_run_opts(bpf_program__fd(progs[0]), &topts); 416 ASSERT_OK(err, "test_run"); 417 418 for (i = 0; i < 4; i++) { 419 if (!is_set(mask, i)) 420 continue; 421 ASSERT_EQ(*test_results[i], expected[i], "test_results"); 422 } 423 424 cleanup: 425 for (i = 0; i < 4; i++) 426 bpf_link__destroy(links[i]); 427 } 428 429 static void test_intersect(void) 430 { 431 struct tracing_multi_intersect *skel; 432 const struct bpf_program *progs[4]; 433 __u64 *test_results[4]; 434 __u32 i; 435 436 skel = tracing_multi_intersect__open_and_load(); 437 if (!ASSERT_OK_PTR(skel, "tracing_multi_intersect__open_and_load")) 438 return; 439 440 skel->bss->pid = getpid(); 441 442 progs[0] = skel->progs.fentry_1; 443 progs[1] = skel->progs.fexit_1; 444 progs[2] = skel->progs.fentry_2; 445 progs[3] = skel->progs.fexit_2; 446 447 test_results[0] = &skel->bss->test_result_fentry_1; 448 test_results[1] = &skel->bss->test_result_fexit_1; 449 test_results[2] = &skel->bss->test_result_fentry_2; 450 test_results[3] = &skel->bss->test_result_fexit_2; 451 452 for (i = 1; i < 16; i++) 453 __test_intersect(i, progs, test_results); 454 455 tracing_multi_intersect__destroy(skel); 456 } 457 458 void test_tracing_multi_test(void) 459 { 460 #ifndef __x86_64__ 461 test__skip(); 462 return; 463 #endif 464 465 if (test__start_subtest("skel_api")) 466 test_skel_api(); 467 if (test__start_subtest("link_api_pattern")) 468 test_link_api_pattern(); 469 if (test__start_subtest("link_api_ids")) 470 test_link_api_ids(false); 471 if (test__start_subtest("module_skel_api")) 472 test_module_skel_api(); 473 if (test__start_subtest("module_link_api_pattern")) 474 test_module_link_api_pattern(); 475 if (test__start_subtest("module_link_api_ids")) 476 test_module_link_api_ids(); 477 if (test__start_subtest("intersect")) 478 test_intersect(); 479 if (test__start_subtest("cookies")) 480 test_link_api_ids(true); 481 } 482