1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <unistd.h> 4 #include <test_progs.h> 5 #include "uprobe_multi.skel.h" 6 #include "uprobe_multi_bench.skel.h" 7 #include "uprobe_multi_usdt.skel.h" 8 #include "bpf/libbpf_internal.h" 9 #include "testing_helpers.h" 10 11 static char test_data[] = "test_data"; 12 13 noinline void uprobe_multi_func_1(void) 14 { 15 asm volatile (""); 16 } 17 18 noinline void uprobe_multi_func_2(void) 19 { 20 asm volatile (""); 21 } 22 23 noinline void uprobe_multi_func_3(void) 24 { 25 asm volatile (""); 26 } 27 28 struct child { 29 int go[2]; 30 int pid; 31 }; 32 33 static void release_child(struct child *child) 34 { 35 int child_status; 36 37 if (!child) 38 return; 39 close(child->go[1]); 40 close(child->go[0]); 41 if (child->pid > 0) 42 waitpid(child->pid, &child_status, 0); 43 } 44 45 static void kick_child(struct child *child) 46 { 47 char c = 1; 48 49 if (child) { 50 write(child->go[1], &c, 1); 51 release_child(child); 52 } 53 fflush(NULL); 54 } 55 56 static struct child *spawn_child(void) 57 { 58 static struct child child; 59 int err; 60 int c; 61 62 /* pipe to notify child to execute the trigger functions */ 63 if (pipe(child.go)) 64 return NULL; 65 66 child.pid = fork(); 67 if (child.pid < 0) { 68 release_child(&child); 69 errno = EINVAL; 70 return NULL; 71 } 72 73 /* child */ 74 if (child.pid == 0) { 75 close(child.go[1]); 76 77 /* wait for parent's kick */ 78 err = read(child.go[0], &c, 1); 79 if (err != 1) 80 exit(err); 81 82 uprobe_multi_func_1(); 83 uprobe_multi_func_2(); 84 uprobe_multi_func_3(); 85 86 exit(errno); 87 } 88 89 return &child; 90 } 91 92 static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child) 93 { 94 skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1; 95 skel->bss->uprobe_multi_func_2_addr = (__u64) uprobe_multi_func_2; 96 skel->bss->uprobe_multi_func_3_addr = (__u64) uprobe_multi_func_3; 97 98 skel->bss->user_ptr = test_data; 99 100 /* 101 * Disable pid check in bpf program if we are pid filter test, 102 * because the probe should be executed only by child->pid 103 * passed at the probe attach. 104 */ 105 skel->bss->pid = child ? 0 : getpid(); 106 107 if (child) 108 kick_child(child); 109 110 /* trigger all probes */ 111 uprobe_multi_func_1(); 112 uprobe_multi_func_2(); 113 uprobe_multi_func_3(); 114 115 /* 116 * There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123] 117 * function and each slepable probe (6) increments uprobe_multi_sleep_result. 118 */ 119 ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 2, "uprobe_multi_func_1_result"); 120 ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 2, "uprobe_multi_func_2_result"); 121 ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 2, "uprobe_multi_func_3_result"); 122 123 ASSERT_EQ(skel->bss->uretprobe_multi_func_1_result, 2, "uretprobe_multi_func_1_result"); 124 ASSERT_EQ(skel->bss->uretprobe_multi_func_2_result, 2, "uretprobe_multi_func_2_result"); 125 ASSERT_EQ(skel->bss->uretprobe_multi_func_3_result, 2, "uretprobe_multi_func_3_result"); 126 127 ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result"); 128 129 if (child) 130 ASSERT_EQ(skel->bss->child_pid, child->pid, "uprobe_multi_child_pid"); 131 } 132 133 static void test_skel_api(void) 134 { 135 struct uprobe_multi *skel = NULL; 136 int err; 137 138 skel = uprobe_multi__open_and_load(); 139 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 140 goto cleanup; 141 142 err = uprobe_multi__attach(skel); 143 if (!ASSERT_OK(err, "uprobe_multi__attach")) 144 goto cleanup; 145 146 uprobe_multi_test_run(skel, NULL); 147 148 cleanup: 149 uprobe_multi__destroy(skel); 150 } 151 152 static void 153 __test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts, 154 struct child *child) 155 { 156 pid_t pid = child ? child->pid : -1; 157 struct uprobe_multi *skel = NULL; 158 159 skel = uprobe_multi__open_and_load(); 160 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 161 goto cleanup; 162 163 opts->retprobe = false; 164 skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, pid, 165 binary, pattern, opts); 166 if (!ASSERT_OK_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) 167 goto cleanup; 168 169 opts->retprobe = true; 170 skel->links.uretprobe = bpf_program__attach_uprobe_multi(skel->progs.uretprobe, pid, 171 binary, pattern, opts); 172 if (!ASSERT_OK_PTR(skel->links.uretprobe, "bpf_program__attach_uprobe_multi")) 173 goto cleanup; 174 175 opts->retprobe = false; 176 skel->links.uprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uprobe_sleep, pid, 177 binary, pattern, opts); 178 if (!ASSERT_OK_PTR(skel->links.uprobe_sleep, "bpf_program__attach_uprobe_multi")) 179 goto cleanup; 180 181 opts->retprobe = true; 182 skel->links.uretprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uretprobe_sleep, 183 pid, binary, pattern, opts); 184 if (!ASSERT_OK_PTR(skel->links.uretprobe_sleep, "bpf_program__attach_uprobe_multi")) 185 goto cleanup; 186 187 opts->retprobe = false; 188 skel->links.uprobe_extra = bpf_program__attach_uprobe_multi(skel->progs.uprobe_extra, -1, 189 binary, pattern, opts); 190 if (!ASSERT_OK_PTR(skel->links.uprobe_extra, "bpf_program__attach_uprobe_multi")) 191 goto cleanup; 192 193 uprobe_multi_test_run(skel, child); 194 195 cleanup: 196 uprobe_multi__destroy(skel); 197 } 198 199 static void 200 test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts) 201 { 202 struct child *child; 203 204 /* no pid filter */ 205 __test_attach_api(binary, pattern, opts, NULL); 206 207 /* pid filter */ 208 child = spawn_child(); 209 if (!ASSERT_OK_PTR(child, "spawn_child")) 210 return; 211 212 __test_attach_api(binary, pattern, opts, child); 213 } 214 215 static void test_attach_api_pattern(void) 216 { 217 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); 218 219 test_attach_api("/proc/self/exe", "uprobe_multi_func_*", &opts); 220 test_attach_api("/proc/self/exe", "uprobe_multi_func_?", &opts); 221 } 222 223 static void test_attach_api_syms(void) 224 { 225 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); 226 const char *syms[3] = { 227 "uprobe_multi_func_1", 228 "uprobe_multi_func_2", 229 "uprobe_multi_func_3", 230 }; 231 232 opts.syms = syms; 233 opts.cnt = ARRAY_SIZE(syms); 234 test_attach_api("/proc/self/exe", NULL, &opts); 235 } 236 237 static void test_attach_api_fails(void) 238 { 239 LIBBPF_OPTS(bpf_link_create_opts, opts); 240 const char *path = "/proc/self/exe"; 241 struct uprobe_multi *skel = NULL; 242 int prog_fd, link_fd = -1; 243 unsigned long offset = 0; 244 245 skel = uprobe_multi__open_and_load(); 246 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 247 goto cleanup; 248 249 prog_fd = bpf_program__fd(skel->progs.uprobe_extra); 250 251 /* abnormal cnt */ 252 opts.uprobe_multi.path = path; 253 opts.uprobe_multi.offsets = &offset; 254 opts.uprobe_multi.cnt = INT_MAX; 255 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 256 if (!ASSERT_ERR(link_fd, "link_fd")) 257 goto cleanup; 258 if (!ASSERT_EQ(link_fd, -E2BIG, "big cnt")) 259 goto cleanup; 260 261 /* cnt is 0 */ 262 LIBBPF_OPTS_RESET(opts, 263 .uprobe_multi.path = path, 264 .uprobe_multi.offsets = (unsigned long *) &offset, 265 ); 266 267 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 268 if (!ASSERT_ERR(link_fd, "link_fd")) 269 goto cleanup; 270 if (!ASSERT_EQ(link_fd, -EINVAL, "cnt_is_zero")) 271 goto cleanup; 272 273 /* negative offset */ 274 offset = -1; 275 opts.uprobe_multi.path = path; 276 opts.uprobe_multi.offsets = (unsigned long *) &offset; 277 opts.uprobe_multi.cnt = 1; 278 279 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 280 if (!ASSERT_ERR(link_fd, "link_fd")) 281 goto cleanup; 282 if (!ASSERT_EQ(link_fd, -EINVAL, "offset_is_negative")) 283 goto cleanup; 284 285 /* offsets is NULL */ 286 LIBBPF_OPTS_RESET(opts, 287 .uprobe_multi.path = path, 288 .uprobe_multi.cnt = 1, 289 ); 290 291 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 292 if (!ASSERT_ERR(link_fd, "link_fd")) 293 goto cleanup; 294 if (!ASSERT_EQ(link_fd, -EINVAL, "offsets_is_null")) 295 goto cleanup; 296 297 /* wrong offsets pointer */ 298 LIBBPF_OPTS_RESET(opts, 299 .uprobe_multi.path = path, 300 .uprobe_multi.offsets = (unsigned long *) 1, 301 .uprobe_multi.cnt = 1, 302 ); 303 304 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 305 if (!ASSERT_ERR(link_fd, "link_fd")) 306 goto cleanup; 307 if (!ASSERT_EQ(link_fd, -EFAULT, "offsets_is_wrong")) 308 goto cleanup; 309 310 /* path is NULL */ 311 offset = 1; 312 LIBBPF_OPTS_RESET(opts, 313 .uprobe_multi.offsets = (unsigned long *) &offset, 314 .uprobe_multi.cnt = 1, 315 ); 316 317 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 318 if (!ASSERT_ERR(link_fd, "link_fd")) 319 goto cleanup; 320 if (!ASSERT_EQ(link_fd, -EINVAL, "path_is_null")) 321 goto cleanup; 322 323 /* wrong path pointer */ 324 LIBBPF_OPTS_RESET(opts, 325 .uprobe_multi.path = (const char *) 1, 326 .uprobe_multi.offsets = (unsigned long *) &offset, 327 .uprobe_multi.cnt = 1, 328 ); 329 330 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 331 if (!ASSERT_ERR(link_fd, "link_fd")) 332 goto cleanup; 333 if (!ASSERT_EQ(link_fd, -EFAULT, "path_is_wrong")) 334 goto cleanup; 335 336 /* wrong path type */ 337 LIBBPF_OPTS_RESET(opts, 338 .uprobe_multi.path = "/", 339 .uprobe_multi.offsets = (unsigned long *) &offset, 340 .uprobe_multi.cnt = 1, 341 ); 342 343 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 344 if (!ASSERT_ERR(link_fd, "link_fd")) 345 goto cleanup; 346 if (!ASSERT_EQ(link_fd, -EBADF, "path_is_wrong_type")) 347 goto cleanup; 348 349 /* wrong cookies pointer */ 350 LIBBPF_OPTS_RESET(opts, 351 .uprobe_multi.path = path, 352 .uprobe_multi.offsets = (unsigned long *) &offset, 353 .uprobe_multi.cookies = (__u64 *) 1ULL, 354 .uprobe_multi.cnt = 1, 355 ); 356 357 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 358 if (!ASSERT_ERR(link_fd, "link_fd")) 359 goto cleanup; 360 if (!ASSERT_EQ(link_fd, -EFAULT, "cookies_is_wrong")) 361 goto cleanup; 362 363 /* wrong ref_ctr_offsets pointer */ 364 LIBBPF_OPTS_RESET(opts, 365 .uprobe_multi.path = path, 366 .uprobe_multi.offsets = (unsigned long *) &offset, 367 .uprobe_multi.cookies = (__u64 *) &offset, 368 .uprobe_multi.ref_ctr_offsets = (unsigned long *) 1, 369 .uprobe_multi.cnt = 1, 370 ); 371 372 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 373 if (!ASSERT_ERR(link_fd, "link_fd")) 374 goto cleanup; 375 if (!ASSERT_EQ(link_fd, -EFAULT, "ref_ctr_offsets_is_wrong")) 376 goto cleanup; 377 378 /* wrong flags */ 379 LIBBPF_OPTS_RESET(opts, 380 .uprobe_multi.flags = 1 << 31, 381 ); 382 383 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 384 if (!ASSERT_ERR(link_fd, "link_fd")) 385 goto cleanup; 386 if (!ASSERT_EQ(link_fd, -EINVAL, "wrong_flags")) 387 goto cleanup; 388 389 /* wrong pid */ 390 LIBBPF_OPTS_RESET(opts, 391 .uprobe_multi.path = path, 392 .uprobe_multi.offsets = (unsigned long *) &offset, 393 .uprobe_multi.cnt = 1, 394 .uprobe_multi.pid = -2, 395 ); 396 397 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 398 if (!ASSERT_ERR(link_fd, "link_fd")) 399 goto cleanup; 400 ASSERT_EQ(link_fd, -ESRCH, "pid_is_wrong"); 401 402 cleanup: 403 if (link_fd >= 0) 404 close(link_fd); 405 uprobe_multi__destroy(skel); 406 } 407 408 static void __test_link_api(struct child *child) 409 { 410 int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1; 411 LIBBPF_OPTS(bpf_link_create_opts, opts); 412 const char *path = "/proc/self/exe"; 413 struct uprobe_multi *skel = NULL; 414 unsigned long *offsets = NULL; 415 const char *syms[3] = { 416 "uprobe_multi_func_1", 417 "uprobe_multi_func_2", 418 "uprobe_multi_func_3", 419 }; 420 int link_extra_fd = -1; 421 int err; 422 423 err = elf_resolve_syms_offsets(path, 3, syms, (unsigned long **) &offsets, STT_FUNC); 424 if (!ASSERT_OK(err, "elf_resolve_syms_offsets")) 425 return; 426 427 opts.uprobe_multi.path = path; 428 opts.uprobe_multi.offsets = offsets; 429 opts.uprobe_multi.cnt = ARRAY_SIZE(syms); 430 opts.uprobe_multi.pid = child ? child->pid : 0; 431 432 skel = uprobe_multi__open_and_load(); 433 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 434 goto cleanup; 435 436 opts.kprobe_multi.flags = 0; 437 prog_fd = bpf_program__fd(skel->progs.uprobe); 438 link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 439 if (!ASSERT_GE(link1_fd, 0, "link1_fd")) 440 goto cleanup; 441 442 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN; 443 prog_fd = bpf_program__fd(skel->progs.uretprobe); 444 link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 445 if (!ASSERT_GE(link2_fd, 0, "link2_fd")) 446 goto cleanup; 447 448 opts.kprobe_multi.flags = 0; 449 prog_fd = bpf_program__fd(skel->progs.uprobe_sleep); 450 link3_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 451 if (!ASSERT_GE(link3_fd, 0, "link3_fd")) 452 goto cleanup; 453 454 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN; 455 prog_fd = bpf_program__fd(skel->progs.uretprobe_sleep); 456 link4_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 457 if (!ASSERT_GE(link4_fd, 0, "link4_fd")) 458 goto cleanup; 459 460 opts.kprobe_multi.flags = 0; 461 opts.uprobe_multi.pid = 0; 462 prog_fd = bpf_program__fd(skel->progs.uprobe_extra); 463 link_extra_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 464 if (!ASSERT_GE(link_extra_fd, 0, "link_extra_fd")) 465 goto cleanup; 466 467 uprobe_multi_test_run(skel, child); 468 469 cleanup: 470 if (link1_fd >= 0) 471 close(link1_fd); 472 if (link2_fd >= 0) 473 close(link2_fd); 474 if (link3_fd >= 0) 475 close(link3_fd); 476 if (link4_fd >= 0) 477 close(link4_fd); 478 if (link_extra_fd >= 0) 479 close(link_extra_fd); 480 481 uprobe_multi__destroy(skel); 482 free(offsets); 483 } 484 485 static void test_link_api(void) 486 { 487 struct child *child; 488 489 /* no pid filter */ 490 __test_link_api(NULL); 491 492 /* pid filter */ 493 child = spawn_child(); 494 if (!ASSERT_OK_PTR(child, "spawn_child")) 495 return; 496 497 __test_link_api(child); 498 } 499 500 static void test_bench_attach_uprobe(void) 501 { 502 long attach_start_ns = 0, attach_end_ns = 0; 503 struct uprobe_multi_bench *skel = NULL; 504 long detach_start_ns, detach_end_ns; 505 double attach_delta, detach_delta; 506 int err; 507 508 skel = uprobe_multi_bench__open_and_load(); 509 if (!ASSERT_OK_PTR(skel, "uprobe_multi_bench__open_and_load")) 510 goto cleanup; 511 512 attach_start_ns = get_time_ns(); 513 514 err = uprobe_multi_bench__attach(skel); 515 if (!ASSERT_OK(err, "uprobe_multi_bench__attach")) 516 goto cleanup; 517 518 attach_end_ns = get_time_ns(); 519 520 system("./uprobe_multi bench"); 521 522 ASSERT_EQ(skel->bss->count, 50000, "uprobes_count"); 523 524 cleanup: 525 detach_start_ns = get_time_ns(); 526 uprobe_multi_bench__destroy(skel); 527 detach_end_ns = get_time_ns(); 528 529 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0; 530 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0; 531 532 printf("%s: attached in %7.3lfs\n", __func__, attach_delta); 533 printf("%s: detached in %7.3lfs\n", __func__, detach_delta); 534 } 535 536 static void test_bench_attach_usdt(void) 537 { 538 long attach_start_ns = 0, attach_end_ns = 0; 539 struct uprobe_multi_usdt *skel = NULL; 540 long detach_start_ns, detach_end_ns; 541 double attach_delta, detach_delta; 542 543 skel = uprobe_multi_usdt__open_and_load(); 544 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open")) 545 goto cleanup; 546 547 attach_start_ns = get_time_ns(); 548 549 skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, -1, "./uprobe_multi", 550 "test", "usdt", NULL); 551 if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt")) 552 goto cleanup; 553 554 attach_end_ns = get_time_ns(); 555 556 system("./uprobe_multi usdt"); 557 558 ASSERT_EQ(skel->bss->count, 50000, "usdt_count"); 559 560 cleanup: 561 detach_start_ns = get_time_ns(); 562 uprobe_multi_usdt__destroy(skel); 563 detach_end_ns = get_time_ns(); 564 565 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0; 566 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0; 567 568 printf("%s: attached in %7.3lfs\n", __func__, attach_delta); 569 printf("%s: detached in %7.3lfs\n", __func__, detach_delta); 570 } 571 572 void test_uprobe_multi_test(void) 573 { 574 if (test__start_subtest("skel_api")) 575 test_skel_api(); 576 if (test__start_subtest("attach_api_pattern")) 577 test_attach_api_pattern(); 578 if (test__start_subtest("attach_api_syms")) 579 test_attach_api_syms(); 580 if (test__start_subtest("link_api")) 581 test_link_api(); 582 if (test__start_subtest("bench_uprobe")) 583 test_bench_attach_uprobe(); 584 if (test__start_subtest("bench_usdt")) 585 test_bench_attach_usdt(); 586 if (test__start_subtest("attach_api_fails")) 587 test_attach_api_fails(); 588 } 589