* [PATCHv2 bpf-next 0/2] selftests/bpf: Add more uprobe multi tests
@ 2024-07-18 13:27 Jiri Olsa
2024-07-18 13:27 ` [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi Jiri Olsa
2024-07-18 13:27 ` [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test Jiri Olsa
0 siblings, 2 replies; 6+ messages in thread
From: Jiri Olsa @ 2024-07-18 13:27 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Oleg Nesterov,
Peter Zijlstra, Masami Hiramatsu
hi,
adding more uprobe multi tests for failed attachments
inside the uprobe register code.
v2 changes:
- addressed review comments [Andrii]
- added new test
thanks,
jirka
---
Jiri Olsa (2):
selftests/bpf: Add uprobe fail tests for uprobe multi
selftests/bpf: Add uprobe multi consumers test
tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c | 318 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c | 39 ++++++++++++
2 files changed, 357 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi
2024-07-18 13:27 [PATCHv2 bpf-next 0/2] selftests/bpf: Add more uprobe multi tests Jiri Olsa
@ 2024-07-18 13:27 ` Jiri Olsa
2024-07-19 17:41 ` Andrii Nakryiko
2024-07-18 13:27 ` [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test Jiri Olsa
1 sibling, 1 reply; 6+ messages in thread
From: Jiri Olsa @ 2024-07-18 13:27 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Oleg Nesterov,
Peter Zijlstra, Masami Hiramatsu
Addig tests for checking on recovery after failing to
attach uprobe.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
.../bpf/prog_tests/uprobe_multi_test.c | 109 ++++++++++++++++++
1 file changed, 109 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
index bf6ca8e3eb13..da8873f24a53 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
@@ -516,6 +516,113 @@ static void test_attach_api_fails(void)
uprobe_multi__destroy(skel);
}
+#ifdef __x86_64__
+noinline __attribute__((naked)) void uprobe_multi_error_func(void)
+{
+ asm volatile ("int3");
+}
+
+/*
+ * Attaching uprobe on uprobe_multi_error_func results in error
+ * because it already starts with int3 instruction.
+ */
+static void attach_uprobe_fail_trap(struct uprobe_multi *skel)
+{
+ LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
+ const char *syms[4] = {
+ "uprobe_multi_func_1",
+ "uprobe_multi_func_2",
+ "uprobe_multi_func_3",
+ "uprobe_multi_error_func",
+ };
+
+ opts.syms = syms;
+ opts.cnt = ARRAY_SIZE(syms);
+
+ skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, -1,
+ "/proc/self/exe", NULL, &opts);
+ if (!ASSERT_ERR_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) {
+ bpf_link__destroy(skel->links.uprobe);
+ skel->links.uprobe = NULL;
+ }
+}
+#else
+static void attach_uprobe_fail_trap(struct uprobe_multi *skel) { }
+#endif
+
+static short sema_1 __maybe_unused, sema_2 __maybe_unused;
+
+static void attach_uprobe_fail_refctr(struct uprobe_multi *skel)
+{
+ unsigned long *tmp_offsets = NULL, *tmp_ref_ctr_offsets = NULL;
+ unsigned long offsets[3], ref_ctr_offsets[3];
+ LIBBPF_OPTS(bpf_link_create_opts, opts);
+ const char *path = "/proc/self/exe";
+ const char *syms[3] = {
+ "uprobe_multi_func_1",
+ "uprobe_multi_func_2",
+ };
+ const char *sema[3] = {
+ "sema_1",
+ "sema_2",
+ };
+ int prog_fd, link_fd, err;
+
+ prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
+
+ err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &syms,
+ &tmp_offsets, STT_FUNC);
+ if (!ASSERT_OK(err, "elf_resolve_syms_offsets_func"))
+ return;
+
+ err = elf_resolve_syms_offsets("/proc/self/exe", 2, (const char **) &sema,
+ &tmp_ref_ctr_offsets, STT_OBJECT);
+ if (!ASSERT_OK(err, "elf_resolve_syms_offsets_sema"))
+ goto cleanup;
+
+ /*
+ * We attach to 3 uprobes on 2 functions so 2 uprobes share single function,
+ * but with different ref_ctr_offset which is not allowed and results in fail.
+ */
+ offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */
+ offsets[1] = tmp_offsets[1]; /* uprobe_multi_func_2 */
+ offsets[2] = tmp_offsets[1]; /* uprobe_multi_func_2 */
+
+ ref_ctr_offsets[0] = tmp_ref_ctr_offsets[0]; /* sema_1 */
+ ref_ctr_offsets[1] = tmp_ref_ctr_offsets[1]; /* sema_2 */
+ ref_ctr_offsets[2] = tmp_ref_ctr_offsets[0]; /* sema_1, error */
+
+ opts.uprobe_multi.path = path;
+ opts.uprobe_multi.offsets = (const unsigned long *) &offsets;
+ opts.uprobe_multi.ref_ctr_offsets = (const unsigned long *) &ref_ctr_offsets;
+ opts.uprobe_multi.cnt = 3;
+
+ link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
+ if (!ASSERT_ERR(link_fd, "link_fd"))
+ close(link_fd);
+
+cleanup:
+ free(tmp_ref_ctr_offsets);
+ free(tmp_offsets);
+}
+
+static void test_attach_uprobe_fails(void)
+{
+ struct uprobe_multi *skel = NULL;
+
+ skel = uprobe_multi__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
+ return;
+
+ /* attach fails due to adding uprobe on trap instruction, x86_64 only */
+ attach_uprobe_fail_trap(skel);
+
+ /* attach fail due to wrong ref_ctr_offs on one of the uprobes */
+ attach_uprobe_fail_refctr(skel);
+
+ uprobe_multi__destroy(skel);
+}
+
static void __test_link_api(struct child *child)
{
int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1;
@@ -703,4 +810,6 @@ void test_uprobe_multi_test(void)
test_bench_attach_usdt();
if (test__start_subtest("attach_api_fails"))
test_attach_api_fails();
+ if (test__start_subtest("attach_uprobe_fails"))
+ test_attach_uprobe_fails();
}
--
2.45.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test
2024-07-18 13:27 [PATCHv2 bpf-next 0/2] selftests/bpf: Add more uprobe multi tests Jiri Olsa
2024-07-18 13:27 ` [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi Jiri Olsa
@ 2024-07-18 13:27 ` Jiri Olsa
2024-07-19 17:58 ` Andrii Nakryiko
1 sibling, 1 reply; 6+ messages in thread
From: Jiri Olsa @ 2024-07-18 13:27 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Oleg Nesterov,
Peter Zijlstra, Masami Hiramatsu
Adding test that attached/detaches multiple consumers on
single uprobe and verifies all were hit as expected.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
.../bpf/prog_tests/uprobe_multi_test.c | 211 +++++++++++++++++-
.../bpf/progs/uprobe_multi_consumers.c | 39 ++++
2 files changed, 249 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c
diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
index da8873f24a53..5228085c2240 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
@@ -6,6 +6,7 @@
#include "uprobe_multi.skel.h"
#include "uprobe_multi_bench.skel.h"
#include "uprobe_multi_usdt.skel.h"
+#include "uprobe_multi_consumers.skel.h"
#include "bpf/libbpf_internal.h"
#include "testing_helpers.h"
#include "../sdt.h"
@@ -581,7 +582,7 @@ static void attach_uprobe_fail_refctr(struct uprobe_multi *skel)
goto cleanup;
/*
- * We attach to 3 uprobes on 2 functions so 2 uprobes share single function,
+ * We attach to 3 uprobes on 2 functions, so 2 uprobes share single function,
* but with different ref_ctr_offset which is not allowed and results in fail.
*/
offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */
@@ -722,6 +723,212 @@ static void test_link_api(void)
__test_link_api(child);
}
+static struct bpf_program *
+get_program(struct uprobe_multi_consumers *skel, int prog)
+{
+ switch (prog) {
+ case 0:
+ return skel->progs.uprobe_0;
+ case 1:
+ return skel->progs.uprobe_1;
+ case 2:
+ return skel->progs.uprobe_2;
+ case 3:
+ return skel->progs.uprobe_3;
+ default:
+ ASSERT_FAIL("get_program");
+ return NULL;
+ }
+}
+
+static struct bpf_link **
+get_link(struct uprobe_multi_consumers *skel, int link)
+{
+ switch (link) {
+ case 0:
+ return &skel->links.uprobe_0;
+ case 1:
+ return &skel->links.uprobe_1;
+ case 2:
+ return &skel->links.uprobe_2;
+ case 3:
+ return &skel->links.uprobe_3;
+ default:
+ ASSERT_FAIL("get_link");
+ return NULL;
+ }
+}
+
+static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx)
+{
+ struct bpf_program *prog = get_program(skel, idx);
+ struct bpf_link **link = get_link(skel, idx);
+ LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
+
+ /*
+ * bit/prog: 0,1 uprobe entry
+ * bit/prog: 2,3 uprobe return
+ */
+ opts.retprobe = idx == 2 || idx == 3;
+
+ *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe",
+ "uprobe_session_consumer_test",
+ &opts);
+ if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi"))
+ return -1;
+ return 0;
+}
+
+static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx)
+{
+ struct bpf_link **link = get_link(skel, idx);
+
+ bpf_link__destroy(*link);
+ *link = NULL;
+}
+
+static bool test_bit(int bit, unsigned long val)
+{
+ return val & (1 << bit);
+}
+
+noinline int
+uprobe_session_consumer_test(struct uprobe_multi_consumers *skel,
+ unsigned long before, unsigned long after)
+{
+ int idx;
+
+ /* detach uprobe for each unset programs in 'before' state ... */
+ for (idx = 0; idx < 4; idx++) {
+ if (test_bit(idx, before) && !test_bit(idx, after))
+ uprobe_detach(skel, idx);
+ }
+
+ /* ... and attach all new programs in 'after' state */
+ for (idx = 0; idx < 4; idx++) {
+ if (!test_bit(idx, before) && test_bit(idx, after)) {
+ if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after"))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void session_consumer_test(struct uprobe_multi_consumers *skel,
+ unsigned long before, unsigned long after)
+{
+ int err, idx;
+
+ printf("session_consumer_test before %lu after %lu\n", before, after);
+
+ /* 'before' is each, we attach uprobe for every set idx */
+ for (idx = 0; idx < 4; idx++) {
+ if (test_bit(idx, before)) {
+ if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before"))
+ goto cleanup;
+ }
+ }
+
+ err = uprobe_session_consumer_test(skel, before, after);
+ if (!ASSERT_EQ(err, 0, "uprobe_session_consumer_test"))
+ goto cleanup;
+
+ for (idx = 0; idx < 4; idx++) {
+ const char *fmt = "BUG";
+ __u64 val = 0;
+
+ if (idx < 2) {
+ /*
+ * uprobe entry
+ * +1 if define in 'before'
+ */
+ if (test_bit(idx, before))
+ val++;
+ fmt = "prog 0/1: uprobe";
+ } else {
+ /* uprobe return is tricky ;-)
+ *
+ * to trigger uretprobe consumer, the uretprobe needs to be installed,
+ * which means one of the 'return' uprobes was alive when probe was hit:
+ *
+ * idxs: 2/3 uprobe return in 'installed' mask
+ *
+ * in addition if 'after' state removes everything that was installed in
+ * 'before' state, then uprobe kernel object goes away and return uprobe
+ * is not installed and we won't hit it even if it's in 'after' state.
+ */
+ unsigned long installed = before & 0b1100; // is uretprobe installed
+ unsigned long exists = before & after; // did uprobe go away
+
+ if (installed && exists && test_bit(idx, after))
+ val++;
+ fmt = "idx 2/3: uretprobe";
+ }
+
+ ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt);
+ skel->bss->uprobe_result[idx] = 0;
+ }
+
+cleanup:
+ for (idx = 0; idx < 4; idx++)
+ uprobe_detach(skel, idx);
+}
+
+static void test_consumers(void)
+{
+ struct uprobe_multi_consumers *skel;
+ int before, after;
+
+ skel = uprobe_multi_consumers__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load"))
+ return;
+
+ /*
+ * The idea of this test is to try all possible combinations of
+ * uprobes consumers attached on single function.
+ *
+ * - 2 uprobe entry consumer
+ * - 2 uprobe exit consumers
+ *
+ * The test uses 4 uprobes attached on single function, but that
+ * translates into single uprobe with 4 consumers in kernel.
+ *
+ * The before/after values present the state of attached consumers
+ * before and after the probed function:
+ *
+ * bit/prog 0,1 : uprobe entry
+ * bit/prog 2,3 : uprobe return
+ *
+ * For example for:
+ *
+ * before = 0b0101
+ * after = 0b0110
+ *
+ * it means that before we call 'uprobe_session_consumer_test' we
+ * attach uprobes defined in 'before' value:
+ *
+ * - bit/prog 0: uprobe entry
+ * - bit/prog 2: uprobe return
+ *
+ * uprobe_session_consumer_test is called and inside it we attach
+ * and detach * uprobes based on 'after' value:
+ *
+ * - bit/prog 0: stays untouched
+ * - bit/prog 2: uprobe return is detached
+ *
+ * uprobe_session_consumer_test returns and we check counters values
+ * increased by bpf programs on each uprobe to match the expected
+ * count based on before/after bits.
+ */
+
+ for (before = 0; before < 16; before++) {
+ for (after = 0; after < 16; after++)
+ session_consumer_test(skel, before, after);
+ }
+
+ uprobe_multi_consumers__destroy(skel);
+}
+
static void test_bench_attach_uprobe(void)
{
long attach_start_ns = 0, attach_end_ns = 0;
@@ -812,4 +1019,6 @@ void test_uprobe_multi_test(void)
test_attach_api_fails();
if (test__start_subtest("attach_uprobe_fails"))
test_attach_uprobe_fails();
+ if (test__start_subtest("consumers"))
+ test_consumers();
}
diff --git a/tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c b/tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c
new file mode 100644
index 000000000000..7e0fdcbbd242
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <stdbool.h>
+#include "bpf_kfuncs.h"
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+__u64 uprobe_result[4];
+
+SEC("uprobe.multi")
+int uprobe_0(struct pt_regs *ctx)
+{
+ uprobe_result[0]++;
+ return 0;
+}
+
+SEC("uprobe.multi")
+int uprobe_1(struct pt_regs *ctx)
+{
+ uprobe_result[1]++;
+ return 0;
+}
+
+SEC("uprobe.multi")
+int uprobe_2(struct pt_regs *ctx)
+{
+ uprobe_result[2]++;
+ return 0;
+}
+
+SEC("uprobe.multi")
+int uprobe_3(struct pt_regs *ctx)
+{
+ uprobe_result[3]++;
+ return 0;
+}
--
2.45.2
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi
2024-07-18 13:27 ` [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi Jiri Olsa
@ 2024-07-19 17:41 ` Andrii Nakryiko
0 siblings, 0 replies; 6+ messages in thread
From: Andrii Nakryiko @ 2024-07-19 17:41 UTC (permalink / raw)
To: Jiri Olsa
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, bpf,
Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Oleg Nesterov,
Peter Zijlstra, Masami Hiramatsu
On Thu, Jul 18, 2024 at 6:28 AM Jiri Olsa <jolsa@kernel.org> wrote:
>
> Addig tests for checking on recovery after failing to
typo: Adding
> attach uprobe.
>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> ---
> .../bpf/prog_tests/uprobe_multi_test.c | 109 ++++++++++++++++++
> 1 file changed, 109 insertions(+)
>
LGTM, thanks for adding these failing test cases! CI is failing, so
please check what's wrong
pw-bot: cr
> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> index bf6ca8e3eb13..da8873f24a53 100644
> --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> @@ -516,6 +516,113 @@ static void test_attach_api_fails(void)
> uprobe_multi__destroy(skel);
> }
>
[...]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test
2024-07-18 13:27 ` [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test Jiri Olsa
@ 2024-07-19 17:58 ` Andrii Nakryiko
2024-07-22 10:30 ` Jiri Olsa
0 siblings, 1 reply; 6+ messages in thread
From: Andrii Nakryiko @ 2024-07-19 17:58 UTC (permalink / raw)
To: Jiri Olsa
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, bpf,
Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Oleg Nesterov,
Peter Zijlstra, Masami Hiramatsu
On Thu, Jul 18, 2024 at 6:28 AM Jiri Olsa <jolsa@kernel.org> wrote:
>
> Adding test that attached/detaches multiple consumers on
typo: attaches
> single uprobe and verifies all were hit as expected.
>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> ---
> .../bpf/prog_tests/uprobe_multi_test.c | 211 +++++++++++++++++-
> .../bpf/progs/uprobe_multi_consumers.c | 39 ++++
> 2 files changed, 249 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c
>
LGTM, took me a bit of extra time to validate the counting logic, but
it looks correct.
> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> index da8873f24a53..5228085c2240 100644
> --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> @@ -6,6 +6,7 @@
> #include "uprobe_multi.skel.h"
> #include "uprobe_multi_bench.skel.h"
> #include "uprobe_multi_usdt.skel.h"
> +#include "uprobe_multi_consumers.skel.h"
> #include "bpf/libbpf_internal.h"
> #include "testing_helpers.h"
> #include "../sdt.h"
> @@ -581,7 +582,7 @@ static void attach_uprobe_fail_refctr(struct uprobe_multi *skel)
> goto cleanup;
>
> /*
> - * We attach to 3 uprobes on 2 functions so 2 uprobes share single function,
> + * We attach to 3 uprobes on 2 functions, so 2 uprobes share single function,
this probably belongs in patch #1
> * but with different ref_ctr_offset which is not allowed and results in fail.
> */
> offsets[0] = tmp_offsets[0]; /* uprobe_multi_func_1 */
> @@ -722,6 +723,212 @@ static void test_link_api(void)
> __test_link_api(child);
> }
>
> +static struct bpf_program *
> +get_program(struct uprobe_multi_consumers *skel, int prog)
> +{
> + switch (prog) {
> + case 0:
> + return skel->progs.uprobe_0;
> + case 1:
> + return skel->progs.uprobe_1;
> + case 2:
> + return skel->progs.uprobe_2;
> + case 3:
> + return skel->progs.uprobe_3;
> + default:
> + ASSERT_FAIL("get_program");
> + return NULL;
> + }
> +}
> +
> +static struct bpf_link **
> +get_link(struct uprobe_multi_consumers *skel, int link)
> +{
> + switch (link) {
> + case 0:
> + return &skel->links.uprobe_0;
> + case 1:
> + return &skel->links.uprobe_1;
> + case 2:
> + return &skel->links.uprobe_2;
> + case 3:
> + return &skel->links.uprobe_3;
> + default:
> + ASSERT_FAIL("get_link");
> + return NULL;
> + }
> +}
> +
> +static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx)
> +{
> + struct bpf_program *prog = get_program(skel, idx);
> + struct bpf_link **link = get_link(skel, idx);
> + LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
> +
> + /*
> + * bit/prog: 0,1 uprobe entry
> + * bit/prog: 2,3 uprobe return
> + */
> + opts.retprobe = idx == 2 || idx == 3;
> +
> + *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe",
this will crash if idx is wrong, let's add explicit NULL checks for
link and prog, just to fail gracefully?
> + "uprobe_session_consumer_test",
> + &opts);
> + if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi"))
> + return -1;
> + return 0;
> +}
> +
> +static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx)
> +{
> + struct bpf_link **link = get_link(skel, idx);
> +
> + bpf_link__destroy(*link);
> + *link = NULL;
> +}
> +
> +static bool test_bit(int bit, unsigned long val)
> +{
> + return val & (1 << bit);
> +}
> +
> +noinline int
> +uprobe_session_consumer_test(struct uprobe_multi_consumers *skel,
this gave me pause, I was frantically recalling when did we end up
landing uprobe sessions support :)
> + unsigned long before, unsigned long after)
> +{
> + int idx;
> +
> + /* detach uprobe for each unset programs in 'before' state ... */
> + for (idx = 0; idx < 4; idx++) {
> + if (test_bit(idx, before) && !test_bit(idx, after))
> + uprobe_detach(skel, idx);
> + }
> +
> + /* ... and attach all new programs in 'after' state */
> + for (idx = 0; idx < 4; idx++) {
> + if (!test_bit(idx, before) && test_bit(idx, after)) {
> + if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after"))
> + return -1;
> + }
> + }
> + return 0;
> +}
> +
> +static void session_consumer_test(struct uprobe_multi_consumers *skel,
> + unsigned long before, unsigned long after)
> +{
> + int err, idx;
> +
> + printf("session_consumer_test before %lu after %lu\n", before, after);
> +
> + /* 'before' is each, we attach uprobe for every set idx */
> + for (idx = 0; idx < 4; idx++) {
> + if (test_bit(idx, before)) {
> + if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before"))
> + goto cleanup;
> + }
> + }
> +
> + err = uprobe_session_consumer_test(skel, before, after);
> + if (!ASSERT_EQ(err, 0, "uprobe_session_consumer_test"))
> + goto cleanup;
> +
> + for (idx = 0; idx < 4; idx++) {
> + const char *fmt = "BUG";
> + __u64 val = 0;
> +
> + if (idx < 2) {
> + /*
> + * uprobe entry
> + * +1 if define in 'before'
> + */
> + if (test_bit(idx, before))
> + val++;
> + fmt = "prog 0/1: uprobe";
> + } else {
> + /* uprobe return is tricky ;-)
> + *
> + * to trigger uretprobe consumer, the uretprobe needs to be installed,
> + * which means one of the 'return' uprobes was alive when probe was hit:
> + *
> + * idxs: 2/3 uprobe return in 'installed' mask
> + *
> + * in addition if 'after' state removes everything that was installed in
> + * 'before' state, then uprobe kernel object goes away and return uprobe
> + * is not installed and we won't hit it even if it's in 'after' state.
> + */
yeah, this is tricky, thanks for writing this out, seems correct to me
> + unsigned long installed = before & 0b1100; // is uretprobe installed
> + unsigned long exists = before & after; // did uprobe go away
> +
> + if (installed && exists && test_bit(idx, after))
nit: naming didn't really help (actually probably hurt the analysis).
installed is whether we had any uretprobes, so "had_uretprobes"?
exists is whether uprobe stayed attached during function call, right,
so maybe "probe_preserved" or something like that?
I.e., the condition should say "if we had any uretprobes, and the
probe instance stayed alive, and the program is still attached at
return".
> + val++;
> + fmt = "idx 2/3: uretprobe";
> + }
> +
> + ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt);
> + skel->bss->uprobe_result[idx] = 0;
> + }
> +
> +cleanup:
> + for (idx = 0; idx < 4; idx++)
> + uprobe_detach(skel, idx);
> +}
> +
[...]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test
2024-07-19 17:58 ` Andrii Nakryiko
@ 2024-07-22 10:30 ` Jiri Olsa
0 siblings, 0 replies; 6+ messages in thread
From: Jiri Olsa @ 2024-07-22 10:30 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, bpf,
Martin KaFai Lau, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Oleg Nesterov,
Peter Zijlstra, Masami Hiramatsu
On Fri, Jul 19, 2024 at 10:58:07AM -0700, Andrii Nakryiko wrote:
> On Thu, Jul 18, 2024 at 6:28 AM Jiri Olsa <jolsa@kernel.org> wrote:
> >
> > Adding test that attached/detaches multiple consumers on
>
> typo: attaches
>
> > single uprobe and verifies all were hit as expected.
> >
> > Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> > ---
> > .../bpf/prog_tests/uprobe_multi_test.c | 211 +++++++++++++++++-
> > .../bpf/progs/uprobe_multi_consumers.c | 39 ++++
> > 2 files changed, 249 insertions(+), 1 deletion(-)
> > create mode 100644 tools/testing/selftests/bpf/progs/uprobe_multi_consumers.c
> >
>
> LGTM, took me a bit of extra time to validate the counting logic, but
> it looks correct.
>
> > diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> > index da8873f24a53..5228085c2240 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> > @@ -6,6 +6,7 @@
> > #include "uprobe_multi.skel.h"
> > #include "uprobe_multi_bench.skel.h"
> > #include "uprobe_multi_usdt.skel.h"
> > +#include "uprobe_multi_consumers.skel.h"
> > #include "bpf/libbpf_internal.h"
> > #include "testing_helpers.h"
> > #include "../sdt.h"
> > @@ -581,7 +582,7 @@ static void attach_uprobe_fail_refctr(struct uprobe_multi *skel)
> > goto cleanup;
> >
> > /*
> > - * We attach to 3 uprobes on 2 functions so 2 uprobes share single function,
> > + * We attach to 3 uprobes on 2 functions, so 2 uprobes share single function,
>
> this probably belongs in patch #1
ugh yep
SNIP
> > +static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx)
> > +{
> > + struct bpf_program *prog = get_program(skel, idx);
> > + struct bpf_link **link = get_link(skel, idx);
> > + LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
> > +
> > + /*
> > + * bit/prog: 0,1 uprobe entry
> > + * bit/prog: 2,3 uprobe return
> > + */
> > + opts.retprobe = idx == 2 || idx == 3;
> > +
> > + *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe",
>
>
> this will crash if idx is wrong, let's add explicit NULL checks for
> link and prog, just to fail gracefully?
ok
>
>
> > + "uprobe_session_consumer_test",
> > + &opts);
> > + if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi"))
> > + return -1;
> > + return 0;
> > +}
> > +
> > +static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx)
> > +{
> > + struct bpf_link **link = get_link(skel, idx);
> > +
> > + bpf_link__destroy(*link);
> > + *link = NULL;
> > +}
> > +
> > +static bool test_bit(int bit, unsigned long val)
> > +{
> > + return val & (1 << bit);
> > +}
> > +
> > +noinline int
> > +uprobe_session_consumer_test(struct uprobe_multi_consumers *skel,
>
> this gave me pause, I was frantically recalling when did we end up
> landing uprobe sessions support :)
rename leftover sry ;-)
SNIP
> > + } else {
> > + /* uprobe return is tricky ;-)
> > + *
> > + * to trigger uretprobe consumer, the uretprobe needs to be installed,
> > + * which means one of the 'return' uprobes was alive when probe was hit:
> > + *
> > + * idxs: 2/3 uprobe return in 'installed' mask
> > + *
> > + * in addition if 'after' state removes everything that was installed in
> > + * 'before' state, then uprobe kernel object goes away and return uprobe
> > + * is not installed and we won't hit it even if it's in 'after' state.
> > + */
>
> yeah, this is tricky, thanks for writing this out, seems correct to me
>
> > + unsigned long installed = before & 0b1100; // is uretprobe installed
> > + unsigned long exists = before & after; // did uprobe go away
> > +
> > + if (installed && exists && test_bit(idx, after))
>
> nit: naming didn't really help (actually probably hurt the analysis).
> installed is whether we had any uretprobes, so "had_uretprobes"?
> exists is whether uprobe stayed attached during function call, right,
> so maybe "probe_preserved" or something like that?
>
> I.e., the condition should say "if we had any uretprobes, and the
> probe instance stayed alive, and the program is still attached at
> return".
yep, looks much better, will rename, thanks
jirka
>
> > + val++;
> > + fmt = "idx 2/3: uretprobe";
> > + }
> > +
> > + ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt);
> > + skel->bss->uprobe_result[idx] = 0;
> > + }
> > +
> > +cleanup:
> > + for (idx = 0; idx < 4; idx++)
> > + uprobe_detach(skel, idx);
> > +}
> > +
>
> [...]
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-07-22 10:30 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-18 13:27 [PATCHv2 bpf-next 0/2] selftests/bpf: Add more uprobe multi tests Jiri Olsa
2024-07-18 13:27 ` [PATCHv2 bpf-next 1/2] selftests/bpf: Add uprobe fail tests for uprobe multi Jiri Olsa
2024-07-19 17:41 ` Andrii Nakryiko
2024-07-18 13:27 ` [PATCHv2 bpf-next 2/2] selftests/bpf: Add uprobe multi consumers test Jiri Olsa
2024-07-19 17:58 ` Andrii Nakryiko
2024-07-22 10:30 ` Jiri Olsa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox