Skip to content

Commit c5ec71c

Browse files
olsajirianakryiko
authored andcommitted
selftests/bpf: Add uprobe multi consumers test
Adding test that attaches/detaches multiple consumers on single uprobe and verifies all were hit as expected. Signed-off-by: Jiri Olsa <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent f5ee7d5 commit c5ec71c

File tree

2 files changed

+252
-0
lines changed

2 files changed

+252
-0
lines changed

tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "uprobe_multi.skel.h"
77
#include "uprobe_multi_bench.skel.h"
88
#include "uprobe_multi_usdt.skel.h"
9+
#include "uprobe_multi_consumers.skel.h"
910
#include "bpf/libbpf_internal.h"
1011
#include "testing_helpers.h"
1112
#include "../sdt.h"
@@ -731,6 +732,216 @@ static void test_link_api(void)
731732
__test_link_api(child);
732733
}
733734

735+
static struct bpf_program *
736+
get_program(struct uprobe_multi_consumers *skel, int prog)
737+
{
738+
switch (prog) {
739+
case 0:
740+
return skel->progs.uprobe_0;
741+
case 1:
742+
return skel->progs.uprobe_1;
743+
case 2:
744+
return skel->progs.uprobe_2;
745+
case 3:
746+
return skel->progs.uprobe_3;
747+
default:
748+
ASSERT_FAIL("get_program");
749+
return NULL;
750+
}
751+
}
752+
753+
static struct bpf_link **
754+
get_link(struct uprobe_multi_consumers *skel, int link)
755+
{
756+
switch (link) {
757+
case 0:
758+
return &skel->links.uprobe_0;
759+
case 1:
760+
return &skel->links.uprobe_1;
761+
case 2:
762+
return &skel->links.uprobe_2;
763+
case 3:
764+
return &skel->links.uprobe_3;
765+
default:
766+
ASSERT_FAIL("get_link");
767+
return NULL;
768+
}
769+
}
770+
771+
static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx)
772+
{
773+
struct bpf_program *prog = get_program(skel, idx);
774+
struct bpf_link **link = get_link(skel, idx);
775+
LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
776+
777+
if (!prog || !link)
778+
return -1;
779+
780+
/*
781+
* bit/prog: 0,1 uprobe entry
782+
* bit/prog: 2,3 uprobe return
783+
*/
784+
opts.retprobe = idx == 2 || idx == 3;
785+
786+
*link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe",
787+
"uprobe_consumer_test",
788+
&opts);
789+
if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi"))
790+
return -1;
791+
return 0;
792+
}
793+
794+
static void uprobe_detach(struct uprobe_multi_consumers *skel, int idx)
795+
{
796+
struct bpf_link **link = get_link(skel, idx);
797+
798+
bpf_link__destroy(*link);
799+
*link = NULL;
800+
}
801+
802+
static bool test_bit(int bit, unsigned long val)
803+
{
804+
return val & (1 << bit);
805+
}
806+
807+
noinline int
808+
uprobe_consumer_test(struct uprobe_multi_consumers *skel,
809+
unsigned long before, unsigned long after)
810+
{
811+
int idx;
812+
813+
/* detach uprobe for each unset programs in 'before' state ... */
814+
for (idx = 0; idx < 4; idx++) {
815+
if (test_bit(idx, before) && !test_bit(idx, after))
816+
uprobe_detach(skel, idx);
817+
}
818+
819+
/* ... and attach all new programs in 'after' state */
820+
for (idx = 0; idx < 4; idx++) {
821+
if (!test_bit(idx, before) && test_bit(idx, after)) {
822+
if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after"))
823+
return -1;
824+
}
825+
}
826+
return 0;
827+
}
828+
829+
static void consumer_test(struct uprobe_multi_consumers *skel,
830+
unsigned long before, unsigned long after)
831+
{
832+
int err, idx;
833+
834+
printf("consumer_test before %lu after %lu\n", before, after);
835+
836+
/* 'before' is each, we attach uprobe for every set idx */
837+
for (idx = 0; idx < 4; idx++) {
838+
if (test_bit(idx, before)) {
839+
if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before"))
840+
goto cleanup;
841+
}
842+
}
843+
844+
err = uprobe_consumer_test(skel, before, after);
845+
if (!ASSERT_EQ(err, 0, "uprobe_consumer_test"))
846+
goto cleanup;
847+
848+
for (idx = 0; idx < 4; idx++) {
849+
const char *fmt = "BUG";
850+
__u64 val = 0;
851+
852+
if (idx < 2) {
853+
/*
854+
* uprobe entry
855+
* +1 if define in 'before'
856+
*/
857+
if (test_bit(idx, before))
858+
val++;
859+
fmt = "prog 0/1: uprobe";
860+
} else {
861+
/*
862+
* uprobe return is tricky ;-)
863+
*
864+
* to trigger uretprobe consumer, the uretprobe needs to be installed,
865+
* which means one of the 'return' uprobes was alive when probe was hit:
866+
*
867+
* idxs: 2/3 uprobe return in 'installed' mask
868+
*
869+
* in addition if 'after' state removes everything that was installed in
870+
* 'before' state, then uprobe kernel object goes away and return uprobe
871+
* is not installed and we won't hit it even if it's in 'after' state.
872+
*/
873+
unsigned long had_uretprobes = before & 0b1100; /* is uretprobe installed */
874+
unsigned long probe_preserved = before & after; /* did uprobe go away */
875+
876+
if (had_uretprobes && probe_preserved && test_bit(idx, after))
877+
val++;
878+
fmt = "idx 2/3: uretprobe";
879+
}
880+
881+
ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt);
882+
skel->bss->uprobe_result[idx] = 0;
883+
}
884+
885+
cleanup:
886+
for (idx = 0; idx < 4; idx++)
887+
uprobe_detach(skel, idx);
888+
}
889+
890+
static void test_consumers(void)
891+
{
892+
struct uprobe_multi_consumers *skel;
893+
int before, after;
894+
895+
skel = uprobe_multi_consumers__open_and_load();
896+
if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load"))
897+
return;
898+
899+
/*
900+
* The idea of this test is to try all possible combinations of
901+
* uprobes consumers attached on single function.
902+
*
903+
* - 2 uprobe entry consumer
904+
* - 2 uprobe exit consumers
905+
*
906+
* The test uses 4 uprobes attached on single function, but that
907+
* translates into single uprobe with 4 consumers in kernel.
908+
*
909+
* The before/after values present the state of attached consumers
910+
* before and after the probed function:
911+
*
912+
* bit/prog 0,1 : uprobe entry
913+
* bit/prog 2,3 : uprobe return
914+
*
915+
* For example for:
916+
*
917+
* before = 0b0101
918+
* after = 0b0110
919+
*
920+
* it means that before we call 'uprobe_consumer_test' we attach
921+
* uprobes defined in 'before' value:
922+
*
923+
* - bit/prog 0: uprobe entry
924+
* - bit/prog 2: uprobe return
925+
*
926+
* uprobe_consumer_test is called and inside it we attach and detach
927+
* uprobes based on 'after' value:
928+
*
929+
* - bit/prog 0: stays untouched
930+
* - bit/prog 2: uprobe return is detached
931+
*
932+
* uprobe_consumer_test returns and we check counters values increased
933+
* by bpf programs on each uprobe to match the expected count based on
934+
* before/after bits.
935+
*/
936+
937+
for (before = 0; before < 16; before++) {
938+
for (after = 0; after < 16; after++)
939+
consumer_test(skel, before, after);
940+
}
941+
942+
uprobe_multi_consumers__destroy(skel);
943+
}
944+
734945
static void test_bench_attach_uprobe(void)
735946
{
736947
long attach_start_ns = 0, attach_end_ns = 0;
@@ -821,4 +1032,6 @@ void test_uprobe_multi_test(void)
8211032
test_attach_api_fails();
8221033
if (test__start_subtest("attach_uprobe_fails"))
8231034
test_attach_uprobe_fails();
1035+
if (test__start_subtest("consumers"))
1036+
test_consumers();
8241037
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <bpf/bpf_helpers.h>
4+
#include <bpf/bpf_tracing.h>
5+
#include <stdbool.h>
6+
#include "bpf_kfuncs.h"
7+
#include "bpf_misc.h"
8+
9+
char _license[] SEC("license") = "GPL";
10+
11+
__u64 uprobe_result[4];
12+
13+
SEC("uprobe.multi")
14+
int uprobe_0(struct pt_regs *ctx)
15+
{
16+
uprobe_result[0]++;
17+
return 0;
18+
}
19+
20+
SEC("uprobe.multi")
21+
int uprobe_1(struct pt_regs *ctx)
22+
{
23+
uprobe_result[1]++;
24+
return 0;
25+
}
26+
27+
SEC("uprobe.multi")
28+
int uprobe_2(struct pt_regs *ctx)
29+
{
30+
uprobe_result[2]++;
31+
return 0;
32+
}
33+
34+
SEC("uprobe.multi")
35+
int uprobe_3(struct pt_regs *ctx)
36+
{
37+
uprobe_result[3]++;
38+
return 0;
39+
}

0 commit comments

Comments
 (0)