1116 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <unistd.h>4#include <pthread.h>5#include <test_progs.h>6#include "uprobe_multi.skel.h"7#include "uprobe_multi_bench.skel.h"8#include "uprobe_multi_usdt.skel.h"9#include "uprobe_multi_consumers.skel.h"10#include "uprobe_multi_pid_filter.skel.h"11#include "bpf/libbpf_internal.h"12#include "testing_helpers.h"13#include "../sdt.h"14 15static char test_data[] = "test_data";16 17noinline void uprobe_multi_func_1(void)18{19 asm volatile ("");20}21 22noinline void uprobe_multi_func_2(void)23{24 asm volatile ("");25}26 27noinline void uprobe_multi_func_3(void)28{29 asm volatile ("");30}31 32noinline void usdt_trigger(void)33{34 STAP_PROBE(test, pid_filter_usdt);35}36 37struct child {38 int go[2];39 int c2p[2]; /* child -> parent channel */40 int pid;41 int tid;42 pthread_t thread;43 char stack[65536];44};45 46static void release_child(struct child *child)47{48 int child_status;49 50 if (!child)51 return;52 close(child->go[1]);53 close(child->go[0]);54 if (child->thread)55 pthread_join(child->thread, NULL);56 close(child->c2p[0]);57 close(child->c2p[1]);58 if (child->pid > 0)59 waitpid(child->pid, &child_status, 0);60}61 62static void kick_child(struct child *child)63{64 char c = 1;65 66 if (child) {67 write(child->go[1], &c, 1);68 release_child(child);69 }70 fflush(NULL);71}72 73static int child_func(void *arg)74{75 struct child *child = arg;76 int err, c;77 78 close(child->go[1]);79 80 /* wait for parent's kick */81 err = read(child->go[0], &c, 1);82 if (err != 1)83 exit(err);84 85 uprobe_multi_func_1();86 uprobe_multi_func_2();87 uprobe_multi_func_3();88 usdt_trigger();89 90 exit(errno);91}92 93static int spawn_child_flag(struct child *child, bool clone_vm)94{95 /* pipe to notify child to execute the trigger functions */96 if (pipe(child->go))97 return -1;98 99 if (clone_vm) {100 child->pid = child->tid = clone(child_func, child->stack + sizeof(child->stack)/2,101 CLONE_VM|SIGCHLD, child);102 } else {103 child->pid = child->tid = fork();104 }105 if (child->pid < 0) {106 release_child(child);107 errno = EINVAL;108 return -1;109 }110 111 /* fork-ed child */112 if (!clone_vm && child->pid == 0)113 child_func(child);114 115 return 0;116}117 118static int spawn_child(struct child *child)119{120 return spawn_child_flag(child, false);121}122 123static void *child_thread(void *ctx)124{125 struct child *child = ctx;126 int c = 0, err;127 128 child->tid = syscall(SYS_gettid);129 130 /* let parent know we are ready */131 err = write(child->c2p[1], &c, 1);132 if (err != 1)133 pthread_exit(&err);134 135 /* wait for parent's kick */136 err = read(child->go[0], &c, 1);137 if (err != 1)138 pthread_exit(&err);139 140 uprobe_multi_func_1();141 uprobe_multi_func_2();142 uprobe_multi_func_3();143 usdt_trigger();144 145 err = 0;146 pthread_exit(&err);147}148 149static int spawn_thread(struct child *child)150{151 int c, err;152 153 /* pipe to notify child to execute the trigger functions */154 if (pipe(child->go))155 return -1;156 /* pipe to notify parent that child thread is ready */157 if (pipe(child->c2p)) {158 close(child->go[0]);159 close(child->go[1]);160 return -1;161 }162 163 child->pid = getpid();164 165 err = pthread_create(&child->thread, NULL, child_thread, child);166 if (err) {167 err = -errno;168 close(child->go[0]);169 close(child->go[1]);170 close(child->c2p[0]);171 close(child->c2p[1]);172 errno = -err;173 return -1;174 }175 176 err = read(child->c2p[0], &c, 1);177 if (!ASSERT_EQ(err, 1, "child_thread_ready"))178 return -1;179 180 return 0;181}182 183static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child)184{185 skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1;186 skel->bss->uprobe_multi_func_2_addr = (__u64) uprobe_multi_func_2;187 skel->bss->uprobe_multi_func_3_addr = (__u64) uprobe_multi_func_3;188 189 skel->bss->user_ptr = test_data;190 191 /*192 * Disable pid check in bpf program if we are pid filter test,193 * because the probe should be executed only by child->pid194 * passed at the probe attach.195 */196 skel->bss->pid = child ? 0 : getpid();197 skel->bss->expect_pid = child ? child->pid : 0;198 199 /* trigger all probes, if we are testing child *process*, just to make200 * sure that PID filtering doesn't let through activations from wrong201 * PIDs; when we test child *thread*, we don't want to do this to202 * avoid double counting number of triggering events203 */204 if (!child || !child->thread) {205 uprobe_multi_func_1();206 uprobe_multi_func_2();207 uprobe_multi_func_3();208 usdt_trigger();209 }210 211 if (child)212 kick_child(child);213 214 /*215 * There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123]216 * function and each sleepable probe (6) increments uprobe_multi_sleep_result.217 */218 ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 2, "uprobe_multi_func_1_result");219 ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 2, "uprobe_multi_func_2_result");220 ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 2, "uprobe_multi_func_3_result");221 222 ASSERT_EQ(skel->bss->uretprobe_multi_func_1_result, 2, "uretprobe_multi_func_1_result");223 ASSERT_EQ(skel->bss->uretprobe_multi_func_2_result, 2, "uretprobe_multi_func_2_result");224 ASSERT_EQ(skel->bss->uretprobe_multi_func_3_result, 2, "uretprobe_multi_func_3_result");225 226 ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result");227 228 ASSERT_FALSE(skel->bss->bad_pid_seen, "bad_pid_seen");229 230 if (child) {231 ASSERT_EQ(skel->bss->child_pid, child->pid, "uprobe_multi_child_pid");232 ASSERT_EQ(skel->bss->child_tid, child->tid, "uprobe_multi_child_tid");233 }234}235 236static void test_skel_api(void)237{238 struct uprobe_multi *skel = NULL;239 int err;240 241 skel = uprobe_multi__open_and_load();242 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))243 goto cleanup;244 245 err = uprobe_multi__attach(skel);246 if (!ASSERT_OK(err, "uprobe_multi__attach"))247 goto cleanup;248 249 uprobe_multi_test_run(skel, NULL);250 251cleanup:252 uprobe_multi__destroy(skel);253}254 255static void256__test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts,257 struct child *child)258{259 pid_t pid = child ? child->pid : -1;260 struct uprobe_multi *skel = NULL;261 262 skel = uprobe_multi__open_and_load();263 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))264 goto cleanup;265 266 opts->retprobe = false;267 skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, pid,268 binary, pattern, opts);269 if (!ASSERT_OK_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi"))270 goto cleanup;271 272 opts->retprobe = true;273 skel->links.uretprobe = bpf_program__attach_uprobe_multi(skel->progs.uretprobe, pid,274 binary, pattern, opts);275 if (!ASSERT_OK_PTR(skel->links.uretprobe, "bpf_program__attach_uprobe_multi"))276 goto cleanup;277 278 opts->retprobe = false;279 skel->links.uprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uprobe_sleep, pid,280 binary, pattern, opts);281 if (!ASSERT_OK_PTR(skel->links.uprobe_sleep, "bpf_program__attach_uprobe_multi"))282 goto cleanup;283 284 opts->retprobe = true;285 skel->links.uretprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uretprobe_sleep,286 pid, binary, pattern, opts);287 if (!ASSERT_OK_PTR(skel->links.uretprobe_sleep, "bpf_program__attach_uprobe_multi"))288 goto cleanup;289 290 opts->retprobe = false;291 skel->links.uprobe_extra = bpf_program__attach_uprobe_multi(skel->progs.uprobe_extra, -1,292 binary, pattern, opts);293 if (!ASSERT_OK_PTR(skel->links.uprobe_extra, "bpf_program__attach_uprobe_multi"))294 goto cleanup;295 296 /* Attach (uprobe-backed) USDTs */297 skel->links.usdt_pid = bpf_program__attach_usdt(skel->progs.usdt_pid, pid, binary,298 "test", "pid_filter_usdt", NULL);299 if (!ASSERT_OK_PTR(skel->links.usdt_pid, "attach_usdt_pid"))300 goto cleanup;301 302 skel->links.usdt_extra = bpf_program__attach_usdt(skel->progs.usdt_extra, -1, binary,303 "test", "pid_filter_usdt", NULL);304 if (!ASSERT_OK_PTR(skel->links.usdt_extra, "attach_usdt_extra"))305 goto cleanup;306 307 uprobe_multi_test_run(skel, child);308 309 ASSERT_FALSE(skel->bss->bad_pid_seen_usdt, "bad_pid_seen_usdt");310 if (child) {311 ASSERT_EQ(skel->bss->child_pid_usdt, child->pid, "usdt_multi_child_pid");312 ASSERT_EQ(skel->bss->child_tid_usdt, child->tid, "usdt_multi_child_tid");313 }314cleanup:315 uprobe_multi__destroy(skel);316}317 318static void319test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts)320{321 static struct child child;322 323 /* no pid filter */324 __test_attach_api(binary, pattern, opts, NULL);325 326 /* pid filter */327 if (!ASSERT_OK(spawn_child(&child), "spawn_child"))328 return;329 330 __test_attach_api(binary, pattern, opts, &child);331 332 /* pid filter (thread) */333 if (!ASSERT_OK(spawn_thread(&child), "spawn_thread"))334 return;335 336 __test_attach_api(binary, pattern, opts, &child);337}338 339static void test_attach_api_pattern(void)340{341 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);342 343 test_attach_api("/proc/self/exe", "uprobe_multi_func_*", &opts);344 test_attach_api("/proc/self/exe", "uprobe_multi_func_?", &opts);345}346 347static void test_attach_api_syms(void)348{349 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);350 const char *syms[3] = {351 "uprobe_multi_func_1",352 "uprobe_multi_func_2",353 "uprobe_multi_func_3",354 };355 356 opts.syms = syms;357 opts.cnt = ARRAY_SIZE(syms);358 test_attach_api("/proc/self/exe", NULL, &opts);359}360 361static void test_attach_api_fails(void)362{363 LIBBPF_OPTS(bpf_link_create_opts, opts);364 const char *path = "/proc/self/exe";365 struct uprobe_multi *skel = NULL;366 int prog_fd, link_fd = -1;367 unsigned long offset = 0;368 369 skel = uprobe_multi__open_and_load();370 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))371 goto cleanup;372 373 prog_fd = bpf_program__fd(skel->progs.uprobe_extra);374 375 /* abnormal cnt */376 opts.uprobe_multi.path = path;377 opts.uprobe_multi.offsets = &offset;378 opts.uprobe_multi.cnt = INT_MAX;379 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);380 if (!ASSERT_ERR(link_fd, "link_fd"))381 goto cleanup;382 if (!ASSERT_EQ(link_fd, -E2BIG, "big cnt"))383 goto cleanup;384 385 /* cnt is 0 */386 LIBBPF_OPTS_RESET(opts,387 .uprobe_multi.path = path,388 .uprobe_multi.offsets = (unsigned long *) &offset,389 );390 391 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);392 if (!ASSERT_ERR(link_fd, "link_fd"))393 goto cleanup;394 if (!ASSERT_EQ(link_fd, -EINVAL, "cnt_is_zero"))395 goto cleanup;396 397 /* negative offset */398 offset = -1;399 opts.uprobe_multi.path = path;400 opts.uprobe_multi.offsets = (unsigned long *) &offset;401 opts.uprobe_multi.cnt = 1;402 403 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);404 if (!ASSERT_ERR(link_fd, "link_fd"))405 goto cleanup;406 if (!ASSERT_EQ(link_fd, -EINVAL, "offset_is_negative"))407 goto cleanup;408 409 /* offsets is NULL */410 LIBBPF_OPTS_RESET(opts,411 .uprobe_multi.path = path,412 .uprobe_multi.cnt = 1,413 );414 415 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);416 if (!ASSERT_ERR(link_fd, "link_fd"))417 goto cleanup;418 if (!ASSERT_EQ(link_fd, -EINVAL, "offsets_is_null"))419 goto cleanup;420 421 /* wrong offsets pointer */422 LIBBPF_OPTS_RESET(opts,423 .uprobe_multi.path = path,424 .uprobe_multi.offsets = (unsigned long *) 1,425 .uprobe_multi.cnt = 1,426 );427 428 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);429 if (!ASSERT_ERR(link_fd, "link_fd"))430 goto cleanup;431 if (!ASSERT_EQ(link_fd, -EFAULT, "offsets_is_wrong"))432 goto cleanup;433 434 /* path is NULL */435 offset = 1;436 LIBBPF_OPTS_RESET(opts,437 .uprobe_multi.offsets = (unsigned long *) &offset,438 .uprobe_multi.cnt = 1,439 );440 441 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);442 if (!ASSERT_ERR(link_fd, "link_fd"))443 goto cleanup;444 if (!ASSERT_EQ(link_fd, -EINVAL, "path_is_null"))445 goto cleanup;446 447 /* wrong path pointer */448 LIBBPF_OPTS_RESET(opts,449 .uprobe_multi.path = (const char *) 1,450 .uprobe_multi.offsets = (unsigned long *) &offset,451 .uprobe_multi.cnt = 1,452 );453 454 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);455 if (!ASSERT_ERR(link_fd, "link_fd"))456 goto cleanup;457 if (!ASSERT_EQ(link_fd, -EFAULT, "path_is_wrong"))458 goto cleanup;459 460 /* wrong path type */461 LIBBPF_OPTS_RESET(opts,462 .uprobe_multi.path = "/",463 .uprobe_multi.offsets = (unsigned long *) &offset,464 .uprobe_multi.cnt = 1,465 );466 467 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);468 if (!ASSERT_ERR(link_fd, "link_fd"))469 goto cleanup;470 if (!ASSERT_EQ(link_fd, -EBADF, "path_is_wrong_type"))471 goto cleanup;472 473 /* wrong cookies pointer */474 LIBBPF_OPTS_RESET(opts,475 .uprobe_multi.path = path,476 .uprobe_multi.offsets = (unsigned long *) &offset,477 .uprobe_multi.cookies = (__u64 *) 1ULL,478 .uprobe_multi.cnt = 1,479 );480 481 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);482 if (!ASSERT_ERR(link_fd, "link_fd"))483 goto cleanup;484 if (!ASSERT_EQ(link_fd, -EFAULT, "cookies_is_wrong"))485 goto cleanup;486 487 /* wrong ref_ctr_offsets pointer */488 LIBBPF_OPTS_RESET(opts,489 .uprobe_multi.path = path,490 .uprobe_multi.offsets = (unsigned long *) &offset,491 .uprobe_multi.cookies = (__u64 *) &offset,492 .uprobe_multi.ref_ctr_offsets = (unsigned long *) 1,493 .uprobe_multi.cnt = 1,494 );495 496 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);497 if (!ASSERT_ERR(link_fd, "link_fd"))498 goto cleanup;499 if (!ASSERT_EQ(link_fd, -EFAULT, "ref_ctr_offsets_is_wrong"))500 goto cleanup;501 502 /* wrong flags */503 LIBBPF_OPTS_RESET(opts,504 .uprobe_multi.flags = 1 << 31,505 );506 507 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);508 if (!ASSERT_ERR(link_fd, "link_fd"))509 goto cleanup;510 if (!ASSERT_EQ(link_fd, -EINVAL, "wrong_flags"))511 goto cleanup;512 513 /* wrong pid */514 LIBBPF_OPTS_RESET(opts,515 .uprobe_multi.path = path,516 .uprobe_multi.offsets = (unsigned long *) &offset,517 .uprobe_multi.cnt = 1,518 .uprobe_multi.pid = -2,519 );520 521 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);522 if (!ASSERT_ERR(link_fd, "link_fd"))523 goto cleanup;524 ASSERT_EQ(link_fd, -EINVAL, "pid_is_wrong");525 526cleanup:527 if (link_fd >= 0)528 close(link_fd);529 uprobe_multi__destroy(skel);530}531 532#ifdef __x86_64__533noinline void uprobe_multi_error_func(void)534{535 /*536 * If --fcf-protection=branch is enabled the gcc generates endbr as537 * first instruction, so marking the exact address of int3 with the538 * symbol to be used in the attach_uprobe_fail_trap test below.539 */540 asm volatile (541 ".globl uprobe_multi_error_func_int3; \n"542 "uprobe_multi_error_func_int3: \n"543 "int3 \n"544 );545}546 547/*548 * Attaching uprobe on uprobe_multi_error_func results in error549 * because it already starts with int3 instruction.550 */551static void attach_uprobe_fail_trap(struct uprobe_multi *skel)552{553 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);554 const char *syms[4] = {555 "uprobe_multi_func_1",556 "uprobe_multi_func_2",557 "uprobe_multi_func_3",558 "uprobe_multi_error_func_int3",559 };560 561 opts.syms = syms;562 opts.cnt = ARRAY_SIZE(syms);563 564 skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, -1,565 "/proc/self/exe", NULL, &opts);566 if (!ASSERT_ERR_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) {567 bpf_link__destroy(skel->links.uprobe);568 skel->links.uprobe = NULL;569 }570}571#else572static void attach_uprobe_fail_trap(struct uprobe_multi *skel) { }573#endif574 575short sema_1 __used, sema_2 __used;576 577static void attach_uprobe_fail_refctr(struct uprobe_multi *skel)578{579 unsigned long *tmp_offsets = NULL, *tmp_ref_ctr_offsets = NULL;580 unsigned long offsets[3], ref_ctr_offsets[3];581 LIBBPF_OPTS(bpf_link_create_opts, opts);582 const char *path = "/proc/self/exe";583 const char *syms[3] = {584 "uprobe_multi_func_1",585 "uprobe_multi_func_2",586 };587 const char *sema[3] = {588 "sema_1",589 "sema_2",590 };591 int prog_fd, link_fd, err;592 593 prog_fd = bpf_program__fd(skel->progs.uprobe_extra);594 595 err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &syms,596 &tmp_offsets, STT_FUNC);597 if (!ASSERT_OK(err, "elf_resolve_syms_offsets_func"))598 return;599 600 err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &sema,601 &tmp_ref_ctr_offsets, STT_OBJECT);602 if (!ASSERT_OK(err, "elf_resolve_syms_offsets_sema"))603 goto cleanup;604 605 /*606 * We attach to 3 uprobes on 2 functions, so 2 uprobes share single function,607 * but with different ref_ctr_offset which is not allowed and results in fail.608 */609 offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */610 offsets[1] = tmp_offsets[1]; /* uprobe_multi_func_2 */611 offsets[2] = tmp_offsets[1]; /* uprobe_multi_func_2 */612 613 ref_ctr_offsets[0] = tmp_ref_ctr_offsets[0]; /* sema_1 */614 ref_ctr_offsets[1] = tmp_ref_ctr_offsets[1]; /* sema_2 */615 ref_ctr_offsets[2] = tmp_ref_ctr_offsets[0]; /* sema_1, error */616 617 opts.uprobe_multi.path = path;618 opts.uprobe_multi.offsets = (const unsigned long *) &offsets;619 opts.uprobe_multi.ref_ctr_offsets = (const unsigned long *) &ref_ctr_offsets;620 opts.uprobe_multi.cnt = 3;621 622 link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);623 if (!ASSERT_ERR(link_fd, "link_fd"))624 close(link_fd);625 626cleanup:627 free(tmp_ref_ctr_offsets);628 free(tmp_offsets);629}630 631static void test_attach_uprobe_fails(void)632{633 struct uprobe_multi *skel = NULL;634 635 skel = uprobe_multi__open_and_load();636 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))637 return;638 639 /* attach fails due to adding uprobe on trap instruction, x86_64 only */640 attach_uprobe_fail_trap(skel);641 642 /* attach fail due to wrong ref_ctr_offs on one of the uprobes */643 attach_uprobe_fail_refctr(skel);644 645 uprobe_multi__destroy(skel);646}647 648static void __test_link_api(struct child *child)649{650 int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1;651 LIBBPF_OPTS(bpf_link_create_opts, opts);652 const char *path = "/proc/self/exe";653 struct uprobe_multi *skel = NULL;654 unsigned long *offsets = NULL;655 const char *syms[3] = {656 "uprobe_multi_func_1",657 "uprobe_multi_func_2",658 "uprobe_multi_func_3",659 };660 int link_extra_fd = -1;661 int err;662 663 err = elf_resolve_syms_offsets(path, 3, syms, (unsigned long **) &offsets, STT_FUNC);664 if (!ASSERT_OK(err, "elf_resolve_syms_offsets"))665 return;666 667 opts.uprobe_multi.path = path;668 opts.uprobe_multi.offsets = offsets;669 opts.uprobe_multi.cnt = ARRAY_SIZE(syms);670 opts.uprobe_multi.pid = child ? child->pid : 0;671 672 skel = uprobe_multi__open_and_load();673 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))674 goto cleanup;675 676 opts.kprobe_multi.flags = 0;677 prog_fd = bpf_program__fd(skel->progs.uprobe);678 link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);679 if (!ASSERT_GE(link1_fd, 0, "link1_fd"))680 goto cleanup;681 682 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;683 prog_fd = bpf_program__fd(skel->progs.uretprobe);684 link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);685 if (!ASSERT_GE(link2_fd, 0, "link2_fd"))686 goto cleanup;687 688 opts.kprobe_multi.flags = 0;689 prog_fd = bpf_program__fd(skel->progs.uprobe_sleep);690 link3_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);691 if (!ASSERT_GE(link3_fd, 0, "link3_fd"))692 goto cleanup;693 694 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;695 prog_fd = bpf_program__fd(skel->progs.uretprobe_sleep);696 link4_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);697 if (!ASSERT_GE(link4_fd, 0, "link4_fd"))698 goto cleanup;699 700 opts.kprobe_multi.flags = 0;701 opts.uprobe_multi.pid = 0;702 prog_fd = bpf_program__fd(skel->progs.uprobe_extra);703 link_extra_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);704 if (!ASSERT_GE(link_extra_fd, 0, "link_extra_fd"))705 goto cleanup;706 707 uprobe_multi_test_run(skel, child);708 709cleanup:710 if (link1_fd >= 0)711 close(link1_fd);712 if (link2_fd >= 0)713 close(link2_fd);714 if (link3_fd >= 0)715 close(link3_fd);716 if (link4_fd >= 0)717 close(link4_fd);718 if (link_extra_fd >= 0)719 close(link_extra_fd);720 721 uprobe_multi__destroy(skel);722 free(offsets);723}724 725static void test_link_api(void)726{727 static struct child child;728 729 /* no pid filter */730 __test_link_api(NULL);731 732 /* pid filter */733 if (!ASSERT_OK(spawn_child(&child), "spawn_child"))734 return;735 736 __test_link_api(&child);737 738 /* pid filter (thread) */739 if (!ASSERT_OK(spawn_thread(&child), "spawn_thread"))740 return;741 742 __test_link_api(&child);743}744 745static struct bpf_program *746get_program(struct uprobe_multi_consumers *skel, int prog)747{748 switch (prog) {749 case 0:750 return skel->progs.uprobe_0;751 case 1:752 return skel->progs.uprobe_1;753 case 2:754 return skel->progs.uprobe_2;755 case 3:756 return skel->progs.uprobe_3;757 default:758 ASSERT_FAIL("get_program");759 return NULL;760 }761}762 763static struct bpf_link **764get_link(struct uprobe_multi_consumers *skel, int link)765{766 switch (link) {767 case 0:768 return &skel->links.uprobe_0;769 case 1:770 return &skel->links.uprobe_1;771 case 2:772 return &skel->links.uprobe_2;773 case 3:774 return &skel->links.uprobe_3;775 default:776 ASSERT_FAIL("get_link");777 return NULL;778 }779}780 781static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx)782{783 struct bpf_program *prog = get_program(skel, idx);784 struct bpf_link **link = get_link(skel, idx);785 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);786 787 if (!prog || !link)788 return -1;789 790 /*791 * bit/prog: 0,1 uprobe entry792 * bit/prog: 2,3 uprobe return793 */794 opts.retprobe = idx == 2 || idx == 3;795 796 *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe",797 "uprobe_consumer_test",798 &opts);799 if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi"))800 return -1;801 return 0;802}803 804static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx)805{806 struct bpf_link **link = get_link(skel, idx);807 808 bpf_link__destroy(*link);809 *link = NULL;810}811 812static bool test_bit(int bit, unsigned long val)813{814 return val & (1 << bit);815}816 817noinline int818uprobe_consumer_test(struct uprobe_multi_consumers *skel,819 unsigned long before, unsigned long after)820{821 int idx;822 823 /* detach uprobe for each unset programs in 'before' state ... */824 for (idx = 0; idx < 4; idx++) {825 if (test_bit(idx, before) && !test_bit(idx, after))826 uprobe_detach(skel, idx);827 }828 829 /* ... and attach all new programs in 'after' state */830 for (idx = 0; idx < 4; idx++) {831 if (!test_bit(idx, before) && test_bit(idx, after)) {832 if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after"))833 return -1;834 }835 }836 return 0;837}838 839static void consumer_test(struct uprobe_multi_consumers *skel,840 unsigned long before, unsigned long after)841{842 int err, idx;843 844 printf("consumer_test before %lu after %lu\n", before, after);845 846 /* 'before' is each, we attach uprobe for every set idx */847 for (idx = 0; idx < 4; idx++) {848 if (test_bit(idx, before)) {849 if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before"))850 goto cleanup;851 }852 }853 854 err = uprobe_consumer_test(skel, before, after);855 if (!ASSERT_EQ(err, 0, "uprobe_consumer_test"))856 goto cleanup;857 858 for (idx = 0; idx < 4; idx++) {859 const char *fmt = "BUG";860 __u64 val = 0;861 862 if (idx < 2) {863 /*864 * uprobe entry865 * +1 if define in 'before'866 */867 if (test_bit(idx, before))868 val++;869 fmt = "prog 0/1: uprobe";870 } else {871 /*872 * uprobe return is tricky ;-)873 *874 * to trigger uretprobe consumer, the uretprobe needs to be installed,875 * which means one of the 'return' uprobes was alive when probe was hit:876 *877 * idxs: 2/3 uprobe return in 'installed' mask878 *879 * in addition if 'after' state removes everything that was installed in880 * 'before' state, then uprobe kernel object goes away and return uprobe881 * is not installed and we won't hit it even if it's in 'after' state.882 */883 unsigned long had_uretprobes = before & 0b1100; /* is uretprobe installed */884 unsigned long probe_preserved = before & after; /* did uprobe go away */885 886 if (had_uretprobes && probe_preserved && test_bit(idx, after))887 val++;888 fmt = "idx 2/3: uretprobe";889 }890 891 ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt);892 skel->bss->uprobe_result[idx] = 0;893 }894 895cleanup:896 for (idx = 0; idx < 4; idx++)897 uprobe_detach(skel, idx);898}899 900static void test_consumers(void)901{902 struct uprobe_multi_consumers *skel;903 int before, after;904 905 skel = uprobe_multi_consumers__open_and_load();906 if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load"))907 return;908 909 /*910 * The idea of this test is to try all possible combinations of911 * uprobes consumers attached on single function.912 *913 * - 2 uprobe entry consumer914 * - 2 uprobe exit consumers915 *916 * The test uses 4 uprobes attached on single function, but that917 * translates into single uprobe with 4 consumers in kernel.918 *919 * The before/after values present the state of attached consumers920 * before and after the probed function:921 *922 * bit/prog 0,1 : uprobe entry923 * bit/prog 2,3 : uprobe return924 *925 * For example for:926 *927 * before = 0b0101928 * after = 0b0110929 *930 * it means that before we call 'uprobe_consumer_test' we attach931 * uprobes defined in 'before' value:932 *933 * - bit/prog 0: uprobe entry934 * - bit/prog 2: uprobe return935 *936 * uprobe_consumer_test is called and inside it we attach and detach937 * uprobes based on 'after' value:938 *939 * - bit/prog 0: stays untouched940 * - bit/prog 2: uprobe return is detached941 *942 * uprobe_consumer_test returns and we check counters values increased943 * by bpf programs on each uprobe to match the expected count based on944 * before/after bits.945 */946 947 for (before = 0; before < 16; before++) {948 for (after = 0; after < 16; after++)949 consumer_test(skel, before, after);950 }951 952 uprobe_multi_consumers__destroy(skel);953}954 955static struct bpf_program *uprobe_multi_program(struct uprobe_multi_pid_filter *skel, int idx)956{957 switch (idx) {958 case 0: return skel->progs.uprobe_multi_0;959 case 1: return skel->progs.uprobe_multi_1;960 case 2: return skel->progs.uprobe_multi_2;961 }962 return NULL;963}964 965#define TASKS 3966 967static void run_pid_filter(struct uprobe_multi_pid_filter *skel, bool clone_vm, bool retprobe)968{969 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts, .retprobe = retprobe);970 struct bpf_link *link[TASKS] = {};971 struct child child[TASKS] = {};972 int i;973 974 memset(skel->bss->test, 0, sizeof(skel->bss->test));975 976 for (i = 0; i < TASKS; i++) {977 if (!ASSERT_OK(spawn_child_flag(&child[i], clone_vm), "spawn_child"))978 goto cleanup;979 skel->bss->pids[i] = child[i].pid;980 }981 982 for (i = 0; i < TASKS; i++) {983 link[i] = bpf_program__attach_uprobe_multi(uprobe_multi_program(skel, i),984 child[i].pid, "/proc/self/exe",985 "uprobe_multi_func_1", &opts);986 if (!ASSERT_OK_PTR(link[i], "bpf_program__attach_uprobe_multi"))987 goto cleanup;988 }989 990 for (i = 0; i < TASKS; i++)991 kick_child(&child[i]);992 993 for (i = 0; i < TASKS; i++) {994 ASSERT_EQ(skel->bss->test[i][0], 1, "pid");995 ASSERT_EQ(skel->bss->test[i][1], 0, "unknown");996 }997 998cleanup:999 for (i = 0; i < TASKS; i++)1000 bpf_link__destroy(link[i]);1001 for (i = 0; i < TASKS; i++)1002 release_child(&child[i]);1003}1004 1005static void test_pid_filter_process(bool clone_vm)1006{1007 struct uprobe_multi_pid_filter *skel;1008 1009 skel = uprobe_multi_pid_filter__open_and_load();1010 if (!ASSERT_OK_PTR(skel, "uprobe_multi_pid_filter__open_and_load"))1011 return;1012 1013 run_pid_filter(skel, clone_vm, false);1014 run_pid_filter(skel, clone_vm, true);1015 1016 uprobe_multi_pid_filter__destroy(skel);1017}1018 1019static void test_bench_attach_uprobe(void)1020{1021 long attach_start_ns = 0, attach_end_ns = 0;1022 struct uprobe_multi_bench *skel = NULL;1023 long detach_start_ns, detach_end_ns;1024 double attach_delta, detach_delta;1025 int err;1026 1027 skel = uprobe_multi_bench__open_and_load();1028 if (!ASSERT_OK_PTR(skel, "uprobe_multi_bench__open_and_load"))1029 goto cleanup;1030 1031 attach_start_ns = get_time_ns();1032 1033 err = uprobe_multi_bench__attach(skel);1034 if (!ASSERT_OK(err, "uprobe_multi_bench__attach"))1035 goto cleanup;1036 1037 attach_end_ns = get_time_ns();1038 1039 system("./uprobe_multi bench");1040 1041 ASSERT_EQ(skel->bss->count, 50000, "uprobes_count");1042 1043cleanup:1044 detach_start_ns = get_time_ns();1045 uprobe_multi_bench__destroy(skel);1046 detach_end_ns = get_time_ns();1047 1048 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;1049 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;1050 1051 printf("%s: attached in %7.3lfs\n", __func__, attach_delta);1052 printf("%s: detached in %7.3lfs\n", __func__, detach_delta);1053}1054 1055static void test_bench_attach_usdt(void)1056{1057 long attach_start_ns = 0, attach_end_ns = 0;1058 struct uprobe_multi_usdt *skel = NULL;1059 long detach_start_ns, detach_end_ns;1060 double attach_delta, detach_delta;1061 1062 skel = uprobe_multi_usdt__open_and_load();1063 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open"))1064 goto cleanup;1065 1066 attach_start_ns = get_time_ns();1067 1068 skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, -1, "./uprobe_multi",1069 "test", "usdt", NULL);1070 if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt"))1071 goto cleanup;1072 1073 attach_end_ns = get_time_ns();1074 1075 system("./uprobe_multi usdt");1076 1077 ASSERT_EQ(skel->bss->count, 50000, "usdt_count");1078 1079cleanup:1080 detach_start_ns = get_time_ns();1081 uprobe_multi_usdt__destroy(skel);1082 detach_end_ns = get_time_ns();1083 1084 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;1085 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;1086 1087 printf("%s: attached in %7.3lfs\n", __func__, attach_delta);1088 printf("%s: detached in %7.3lfs\n", __func__, detach_delta);1089}1090 1091void test_uprobe_multi_test(void)1092{1093 if (test__start_subtest("skel_api"))1094 test_skel_api();1095 if (test__start_subtest("attach_api_pattern"))1096 test_attach_api_pattern();1097 if (test__start_subtest("attach_api_syms"))1098 test_attach_api_syms();1099 if (test__start_subtest("link_api"))1100 test_link_api();1101 if (test__start_subtest("bench_uprobe"))1102 test_bench_attach_uprobe();1103 if (test__start_subtest("bench_usdt"))1104 test_bench_attach_usdt();1105 if (test__start_subtest("attach_api_fails"))1106 test_attach_api_fails();1107 if (test__start_subtest("attach_uprobe_fails"))1108 test_attach_uprobe_fails();1109 if (test__start_subtest("consumers"))1110 test_consumers();1111 if (test__start_subtest("filter_fork"))1112 test_pid_filter_process(false);1113 if (test__start_subtest("filter_clone_vm"))1114 test_pid_filter_process(true);1115}1116