|
7 | 7 | #include "uprobe_multi_bench.skel.h"
|
8 | 8 | #include "uprobe_multi_usdt.skel.h"
|
9 | 9 | #include "uprobe_multi_consumers.skel.h"
|
| 10 | +#include "uprobe_multi_pid_filter.skel.h" |
10 | 11 | #include "bpf/libbpf_internal.h"
|
11 | 12 | #include "testing_helpers.h"
|
12 | 13 | #include "../sdt.h"
|
@@ -39,6 +40,7 @@ struct child {
|
39 | 40 | int pid;
|
40 | 41 | int tid;
|
41 | 42 | pthread_t thread;
|
| 43 | + char stack[65536]; |
42 | 44 | };
|
43 | 45 |
|
44 | 46 | static void release_child(struct child *child)
|
@@ -68,41 +70,54 @@ static void kick_child(struct child *child)
|
68 | 70 | fflush(NULL);
|
69 | 71 | }
|
70 | 72 |
|
71 |
| -static struct child *spawn_child(void) |
| 73 | +static int child_func(void *arg) |
72 | 74 | {
|
73 |
| - static struct child child; |
74 |
| - int err; |
75 |
| - int c; |
| 75 | + struct child *child = arg; |
| 76 | + int err, c; |
76 | 77 |
|
77 |
| - /* pipe to notify child to execute the trigger functions */ |
78 |
| - if (pipe(child.go)) |
79 |
| - return NULL; |
| 78 | + close(child->go[1]); |
80 | 79 |
|
81 |
| - child.pid = child.tid = fork(); |
82 |
| - if (child.pid < 0) { |
83 |
| - release_child(&child); |
84 |
| - errno = EINVAL; |
85 |
| - return NULL; |
86 |
| - } |
| 80 | + /* wait for parent's kick */ |
| 81 | + err = read(child->go[0], &c, 1); |
| 82 | + if (err != 1) |
| 83 | + exit(err); |
87 | 84 |
|
88 |
| - /* child */ |
89 |
| - if (child.pid == 0) { |
90 |
| - close(child.go[1]); |
| 85 | + uprobe_multi_func_1(); |
| 86 | + uprobe_multi_func_2(); |
| 87 | + uprobe_multi_func_3(); |
| 88 | + usdt_trigger(); |
91 | 89 |
|
92 |
| - /* wait for parent's kick */ |
93 |
| - err = read(child.go[0], &c, 1); |
94 |
| - if (err != 1) |
95 |
| - exit(err); |
| 90 | + exit(errno); |
| 91 | +} |
96 | 92 |
|
97 |
| - uprobe_multi_func_1(); |
98 |
| - uprobe_multi_func_2(); |
99 |
| - uprobe_multi_func_3(); |
100 |
| - usdt_trigger(); |
| 93 | +static 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; |
101 | 98 |
|
102 |
| - exit(errno); |
| 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; |
103 | 109 | }
|
104 | 110 |
|
105 |
| - return &child; |
| 111 | + /* fork-ed child */ |
| 112 | + if (!clone_vm && child->pid == 0) |
| 113 | + child_func(child); |
| 114 | + |
| 115 | + return 0; |
| 116 | +} |
| 117 | + |
| 118 | +static int spawn_child(struct child *child) |
| 119 | +{ |
| 120 | + return spawn_child_flag(child, false); |
106 | 121 | }
|
107 | 122 |
|
108 | 123 | static void *child_thread(void *ctx)
|
@@ -131,39 +146,38 @@ static void *child_thread(void *ctx)
|
131 | 146 | pthread_exit(&err);
|
132 | 147 | }
|
133 | 148 |
|
134 |
| -static struct child *spawn_thread(void) |
| 149 | +static int spawn_thread(struct child *child) |
135 | 150 | {
|
136 |
| - static struct child child; |
137 | 151 | int c, err;
|
138 | 152 |
|
139 | 153 | /* pipe to notify child to execute the trigger functions */
|
140 |
| - if (pipe(child.go)) |
141 |
| - return NULL; |
| 154 | + if (pipe(child->go)) |
| 155 | + return -1; |
142 | 156 | /* pipe to notify parent that child thread is ready */
|
143 |
| - if (pipe(child.c2p)) { |
144 |
| - close(child.go[0]); |
145 |
| - close(child.go[1]); |
146 |
| - return NULL; |
| 157 | + if (pipe(child->c2p)) { |
| 158 | + close(child->go[0]); |
| 159 | + close(child->go[1]); |
| 160 | + return -1; |
147 | 161 | }
|
148 | 162 |
|
149 |
| - child.pid = getpid(); |
| 163 | + child->pid = getpid(); |
150 | 164 |
|
151 |
| - err = pthread_create(&child.thread, NULL, child_thread, &child); |
| 165 | + err = pthread_create(&child->thread, NULL, child_thread, child); |
152 | 166 | if (err) {
|
153 | 167 | err = -errno;
|
154 |
| - close(child.go[0]); |
155 |
| - close(child.go[1]); |
156 |
| - close(child.c2p[0]); |
157 |
| - close(child.c2p[1]); |
| 168 | + close(child->go[0]); |
| 169 | + close(child->go[1]); |
| 170 | + close(child->c2p[0]); |
| 171 | + close(child->c2p[1]); |
158 | 172 | errno = -err;
|
159 |
| - return NULL; |
| 173 | + return -1; |
160 | 174 | }
|
161 | 175 |
|
162 |
| - err = read(child.c2p[0], &c, 1); |
| 176 | + err = read(child->c2p[0], &c, 1); |
163 | 177 | if (!ASSERT_EQ(err, 1, "child_thread_ready"))
|
164 |
| - return NULL; |
| 178 | + return -1; |
165 | 179 |
|
166 |
| - return &child; |
| 180 | + return 0; |
167 | 181 | }
|
168 | 182 |
|
169 | 183 | static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child)
|
@@ -304,24 +318,22 @@ __test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_mul
|
304 | 318 | static void
|
305 | 319 | test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts)
|
306 | 320 | {
|
307 |
| - struct child *child; |
| 321 | + static struct child child; |
308 | 322 |
|
309 | 323 | /* no pid filter */
|
310 | 324 | __test_attach_api(binary, pattern, opts, NULL);
|
311 | 325 |
|
312 | 326 | /* pid filter */
|
313 |
| - child = spawn_child(); |
314 |
| - if (!ASSERT_OK_PTR(child, "spawn_child")) |
| 327 | + if (!ASSERT_OK(spawn_child(&child), "spawn_child")) |
315 | 328 | return;
|
316 | 329 |
|
317 |
| - __test_attach_api(binary, pattern, opts, child); |
| 330 | + __test_attach_api(binary, pattern, opts, &child); |
318 | 331 |
|
319 | 332 | /* pid filter (thread) */
|
320 |
| - child = spawn_thread(); |
321 |
| - if (!ASSERT_OK_PTR(child, "spawn_thread")) |
| 333 | + if (!ASSERT_OK(spawn_thread(&child), "spawn_thread")) |
322 | 334 | return;
|
323 | 335 |
|
324 |
| - __test_attach_api(binary, pattern, opts, child); |
| 336 | + __test_attach_api(binary, pattern, opts, &child); |
325 | 337 | }
|
326 | 338 |
|
327 | 339 | static void test_attach_api_pattern(void)
|
@@ -712,24 +724,22 @@ static void __test_link_api(struct child *child)
|
712 | 724 |
|
713 | 725 | static void test_link_api(void)
|
714 | 726 | {
|
715 |
| - struct child *child; |
| 727 | + static struct child child; |
716 | 728 |
|
717 | 729 | /* no pid filter */
|
718 | 730 | __test_link_api(NULL);
|
719 | 731 |
|
720 | 732 | /* pid filter */
|
721 |
| - child = spawn_child(); |
722 |
| - if (!ASSERT_OK_PTR(child, "spawn_child")) |
| 733 | + if (!ASSERT_OK(spawn_child(&child), "spawn_child")) |
723 | 734 | return;
|
724 | 735 |
|
725 |
| - __test_link_api(child); |
| 736 | + __test_link_api(&child); |
726 | 737 |
|
727 | 738 | /* pid filter (thread) */
|
728 |
| - child = spawn_thread(); |
729 |
| - if (!ASSERT_OK_PTR(child, "spawn_thread")) |
| 739 | + if (!ASSERT_OK(spawn_thread(&child), "spawn_thread")) |
730 | 740 | return;
|
731 | 741 |
|
732 |
| - __test_link_api(child); |
| 742 | + __test_link_api(&child); |
733 | 743 | }
|
734 | 744 |
|
735 | 745 | static struct bpf_program *
|
@@ -942,6 +952,70 @@ static void test_consumers(void)
|
942 | 952 | uprobe_multi_consumers__destroy(skel);
|
943 | 953 | }
|
944 | 954 |
|
| 955 | +static 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 3 |
| 966 | + |
| 967 | +static 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 | + |
| 998 | +cleanup: |
| 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 | + |
| 1005 | +static 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 | + |
945 | 1019 | static void test_bench_attach_uprobe(void)
|
946 | 1020 | {
|
947 | 1021 | long attach_start_ns = 0, attach_end_ns = 0;
|
@@ -1034,4 +1108,8 @@ void test_uprobe_multi_test(void)
|
1034 | 1108 | test_attach_uprobe_fails();
|
1035 | 1109 | if (test__start_subtest("consumers"))
|
1036 | 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); |
1037 | 1115 | }
|
0 commit comments