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
uprobe_multi_func_1(void)13 noinline void uprobe_multi_func_1(void)
14 {
15 asm volatile ("");
16 }
17
uprobe_multi_func_2(void)18 noinline void uprobe_multi_func_2(void)
19 {
20 asm volatile ("");
21 }
22
uprobe_multi_func_3(void)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
release_child(struct child * child)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
kick_child(struct child * child)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
spawn_child(void)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
uprobe_multi_test_run(struct uprobe_multi * skel,struct child * child)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
test_skel_api(void)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
__test_attach_api(const char * binary,const char * pattern,struct bpf_uprobe_multi_opts * opts,struct child * child)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
test_attach_api(const char * binary,const char * pattern,struct bpf_uprobe_multi_opts * opts)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
test_attach_api_pattern(void)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
test_attach_api_syms(void)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
__test_link_api(struct child * child)237 static void __test_link_api(struct child *child)
238 {
239 int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1;
240 LIBBPF_OPTS(bpf_link_create_opts, opts);
241 const char *path = "/proc/self/exe";
242 struct uprobe_multi *skel = NULL;
243 unsigned long *offsets = NULL;
244 const char *syms[3] = {
245 "uprobe_multi_func_1",
246 "uprobe_multi_func_2",
247 "uprobe_multi_func_3",
248 };
249 int link_extra_fd = -1;
250 int err;
251
252 err = elf_resolve_syms_offsets(path, 3, syms, (unsigned long **) &offsets);
253 if (!ASSERT_OK(err, "elf_resolve_syms_offsets"))
254 return;
255
256 opts.uprobe_multi.path = path;
257 opts.uprobe_multi.offsets = offsets;
258 opts.uprobe_multi.cnt = ARRAY_SIZE(syms);
259 opts.uprobe_multi.pid = child ? child->pid : 0;
260
261 skel = uprobe_multi__open_and_load();
262 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
263 goto cleanup;
264
265 opts.kprobe_multi.flags = 0;
266 prog_fd = bpf_program__fd(skel->progs.uprobe);
267 link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
268 if (!ASSERT_GE(link1_fd, 0, "link1_fd"))
269 goto cleanup;
270
271 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;
272 prog_fd = bpf_program__fd(skel->progs.uretprobe);
273 link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
274 if (!ASSERT_GE(link2_fd, 0, "link2_fd"))
275 goto cleanup;
276
277 opts.kprobe_multi.flags = 0;
278 prog_fd = bpf_program__fd(skel->progs.uprobe_sleep);
279 link3_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
280 if (!ASSERT_GE(link3_fd, 0, "link3_fd"))
281 goto cleanup;
282
283 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;
284 prog_fd = bpf_program__fd(skel->progs.uretprobe_sleep);
285 link4_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
286 if (!ASSERT_GE(link4_fd, 0, "link4_fd"))
287 goto cleanup;
288
289 opts.kprobe_multi.flags = 0;
290 opts.uprobe_multi.pid = 0;
291 prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
292 link_extra_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
293 if (!ASSERT_GE(link_extra_fd, 0, "link_extra_fd"))
294 goto cleanup;
295
296 uprobe_multi_test_run(skel, child);
297
298 cleanup:
299 if (link1_fd >= 0)
300 close(link1_fd);
301 if (link2_fd >= 0)
302 close(link2_fd);
303 if (link3_fd >= 0)
304 close(link3_fd);
305 if (link4_fd >= 0)
306 close(link4_fd);
307 if (link_extra_fd >= 0)
308 close(link_extra_fd);
309
310 uprobe_multi__destroy(skel);
311 free(offsets);
312 }
313
test_link_api(void)314 void test_link_api(void)
315 {
316 struct child *child;
317
318 /* no pid filter */
319 __test_link_api(NULL);
320
321 /* pid filter */
322 child = spawn_child();
323 if (!ASSERT_OK_PTR(child, "spawn_child"))
324 return;
325
326 __test_link_api(child);
327 }
328
test_bench_attach_uprobe(void)329 static void test_bench_attach_uprobe(void)
330 {
331 long attach_start_ns = 0, attach_end_ns = 0;
332 struct uprobe_multi_bench *skel = NULL;
333 long detach_start_ns, detach_end_ns;
334 double attach_delta, detach_delta;
335 int err;
336
337 skel = uprobe_multi_bench__open_and_load();
338 if (!ASSERT_OK_PTR(skel, "uprobe_multi_bench__open_and_load"))
339 goto cleanup;
340
341 attach_start_ns = get_time_ns();
342
343 err = uprobe_multi_bench__attach(skel);
344 if (!ASSERT_OK(err, "uprobe_multi_bench__attach"))
345 goto cleanup;
346
347 attach_end_ns = get_time_ns();
348
349 system("./uprobe_multi bench");
350
351 ASSERT_EQ(skel->bss->count, 50000, "uprobes_count");
352
353 cleanup:
354 detach_start_ns = get_time_ns();
355 uprobe_multi_bench__destroy(skel);
356 detach_end_ns = get_time_ns();
357
358 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
359 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
360
361 printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
362 printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
363 }
364
test_bench_attach_usdt(void)365 static void test_bench_attach_usdt(void)
366 {
367 long attach_start_ns = 0, attach_end_ns = 0;
368 struct uprobe_multi_usdt *skel = NULL;
369 long detach_start_ns, detach_end_ns;
370 double attach_delta, detach_delta;
371
372 skel = uprobe_multi_usdt__open_and_load();
373 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open"))
374 goto cleanup;
375
376 attach_start_ns = get_time_ns();
377
378 skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, -1, "./uprobe_multi",
379 "test", "usdt", NULL);
380 if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt"))
381 goto cleanup;
382
383 attach_end_ns = get_time_ns();
384
385 system("./uprobe_multi usdt");
386
387 ASSERT_EQ(skel->bss->count, 50000, "usdt_count");
388
389 cleanup:
390 detach_start_ns = get_time_ns();
391 uprobe_multi_usdt__destroy(skel);
392 detach_end_ns = get_time_ns();
393
394 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
395 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
396
397 printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
398 printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
399 }
400
test_uprobe_multi_test(void)401 void test_uprobe_multi_test(void)
402 {
403 if (test__start_subtest("skel_api"))
404 test_skel_api();
405 if (test__start_subtest("attach_api_pattern"))
406 test_attach_api_pattern();
407 if (test__start_subtest("attach_api_syms"))
408 test_attach_api_syms();
409 if (test__start_subtest("link_api"))
410 test_link_api();
411 if (test__start_subtest("bench_uprobe"))
412 test_bench_attach_uprobe();
413 if (test__start_subtest("bench_usdt"))
414 test_bench_attach_usdt();
415 }
416