]> Git Repo - linux.git/blob - tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
Merge patch series "riscv: Extension parsing fixes"
[linux.git] / tools / testing / selftests / bpf / prog_tests / uprobe_multi_test.c
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
13 noinline void uprobe_multi_func_1(void)
14 {
15         asm volatile ("");
16 }
17
18 noinline void uprobe_multi_func_2(void)
19 {
20         asm volatile ("");
21 }
22
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
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
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
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
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
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
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
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
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
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
237 static void test_attach_api_fails(void)
238 {
239         LIBBPF_OPTS(bpf_link_create_opts, opts);
240         const char *path = "/proc/self/exe";
241         struct uprobe_multi *skel = NULL;
242         int prog_fd, link_fd = -1;
243         unsigned long offset = 0;
244
245         skel = uprobe_multi__open_and_load();
246         if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
247                 goto cleanup;
248
249         prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
250
251         /* abnormal cnt */
252         opts.uprobe_multi.path = path;
253         opts.uprobe_multi.offsets = &offset;
254         opts.uprobe_multi.cnt = INT_MAX;
255         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
256         if (!ASSERT_ERR(link_fd, "link_fd"))
257                 goto cleanup;
258         if (!ASSERT_EQ(link_fd, -E2BIG, "big cnt"))
259                 goto cleanup;
260
261         /* cnt is 0 */
262         LIBBPF_OPTS_RESET(opts,
263                 .uprobe_multi.path = path,
264                 .uprobe_multi.offsets = (unsigned long *) &offset,
265         );
266
267         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
268         if (!ASSERT_ERR(link_fd, "link_fd"))
269                 goto cleanup;
270         if (!ASSERT_EQ(link_fd, -EINVAL, "cnt_is_zero"))
271                 goto cleanup;
272
273         /* negative offset */
274         offset = -1;
275         opts.uprobe_multi.path = path;
276         opts.uprobe_multi.offsets = (unsigned long *) &offset;
277         opts.uprobe_multi.cnt = 1;
278
279         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
280         if (!ASSERT_ERR(link_fd, "link_fd"))
281                 goto cleanup;
282         if (!ASSERT_EQ(link_fd, -EINVAL, "offset_is_negative"))
283                 goto cleanup;
284
285         /* offsets is NULL */
286         LIBBPF_OPTS_RESET(opts,
287                 .uprobe_multi.path = path,
288                 .uprobe_multi.cnt = 1,
289         );
290
291         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
292         if (!ASSERT_ERR(link_fd, "link_fd"))
293                 goto cleanup;
294         if (!ASSERT_EQ(link_fd, -EINVAL, "offsets_is_null"))
295                 goto cleanup;
296
297         /* wrong offsets pointer */
298         LIBBPF_OPTS_RESET(opts,
299                 .uprobe_multi.path = path,
300                 .uprobe_multi.offsets = (unsigned long *) 1,
301                 .uprobe_multi.cnt = 1,
302         );
303
304         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
305         if (!ASSERT_ERR(link_fd, "link_fd"))
306                 goto cleanup;
307         if (!ASSERT_EQ(link_fd, -EFAULT, "offsets_is_wrong"))
308                 goto cleanup;
309
310         /* path is NULL */
311         offset = 1;
312         LIBBPF_OPTS_RESET(opts,
313                 .uprobe_multi.offsets = (unsigned long *) &offset,
314                 .uprobe_multi.cnt = 1,
315         );
316
317         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
318         if (!ASSERT_ERR(link_fd, "link_fd"))
319                 goto cleanup;
320         if (!ASSERT_EQ(link_fd, -EINVAL, "path_is_null"))
321                 goto cleanup;
322
323         /* wrong path pointer  */
324         LIBBPF_OPTS_RESET(opts,
325                 .uprobe_multi.path = (const char *) 1,
326                 .uprobe_multi.offsets = (unsigned long *) &offset,
327                 .uprobe_multi.cnt = 1,
328         );
329
330         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
331         if (!ASSERT_ERR(link_fd, "link_fd"))
332                 goto cleanup;
333         if (!ASSERT_EQ(link_fd, -EFAULT, "path_is_wrong"))
334                 goto cleanup;
335
336         /* wrong path type */
337         LIBBPF_OPTS_RESET(opts,
338                 .uprobe_multi.path = "/",
339                 .uprobe_multi.offsets = (unsigned long *) &offset,
340                 .uprobe_multi.cnt = 1,
341         );
342
343         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
344         if (!ASSERT_ERR(link_fd, "link_fd"))
345                 goto cleanup;
346         if (!ASSERT_EQ(link_fd, -EBADF, "path_is_wrong_type"))
347                 goto cleanup;
348
349         /* wrong cookies pointer */
350         LIBBPF_OPTS_RESET(opts,
351                 .uprobe_multi.path = path,
352                 .uprobe_multi.offsets = (unsigned long *) &offset,
353                 .uprobe_multi.cookies = (__u64 *) 1ULL,
354                 .uprobe_multi.cnt = 1,
355         );
356
357         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
358         if (!ASSERT_ERR(link_fd, "link_fd"))
359                 goto cleanup;
360         if (!ASSERT_EQ(link_fd, -EFAULT, "cookies_is_wrong"))
361                 goto cleanup;
362
363         /* wrong ref_ctr_offsets pointer */
364         LIBBPF_OPTS_RESET(opts,
365                 .uprobe_multi.path = path,
366                 .uprobe_multi.offsets = (unsigned long *) &offset,
367                 .uprobe_multi.cookies = (__u64 *) &offset,
368                 .uprobe_multi.ref_ctr_offsets = (unsigned long *) 1,
369                 .uprobe_multi.cnt = 1,
370         );
371
372         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
373         if (!ASSERT_ERR(link_fd, "link_fd"))
374                 goto cleanup;
375         if (!ASSERT_EQ(link_fd, -EFAULT, "ref_ctr_offsets_is_wrong"))
376                 goto cleanup;
377
378         /* wrong flags */
379         LIBBPF_OPTS_RESET(opts,
380                 .uprobe_multi.flags = 1 << 31,
381         );
382
383         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
384         if (!ASSERT_ERR(link_fd, "link_fd"))
385                 goto cleanup;
386         if (!ASSERT_EQ(link_fd, -EINVAL, "wrong_flags"))
387                 goto cleanup;
388
389         /* wrong pid */
390         LIBBPF_OPTS_RESET(opts,
391                 .uprobe_multi.path = path,
392                 .uprobe_multi.offsets = (unsigned long *) &offset,
393                 .uprobe_multi.cnt = 1,
394                 .uprobe_multi.pid = -2,
395         );
396
397         link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
398         if (!ASSERT_ERR(link_fd, "link_fd"))
399                 goto cleanup;
400         ASSERT_EQ(link_fd, -ESRCH, "pid_is_wrong");
401
402 cleanup:
403         if (link_fd >= 0)
404                 close(link_fd);
405         uprobe_multi__destroy(skel);
406 }
407
408 static void __test_link_api(struct child *child)
409 {
410         int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1;
411         LIBBPF_OPTS(bpf_link_create_opts, opts);
412         const char *path = "/proc/self/exe";
413         struct uprobe_multi *skel = NULL;
414         unsigned long *offsets = NULL;
415         const char *syms[3] = {
416                 "uprobe_multi_func_1",
417                 "uprobe_multi_func_2",
418                 "uprobe_multi_func_3",
419         };
420         int link_extra_fd = -1;
421         int err;
422
423         err = elf_resolve_syms_offsets(path, 3, syms, (unsigned long **) &offsets, STT_FUNC);
424         if (!ASSERT_OK(err, "elf_resolve_syms_offsets"))
425                 return;
426
427         opts.uprobe_multi.path = path;
428         opts.uprobe_multi.offsets = offsets;
429         opts.uprobe_multi.cnt = ARRAY_SIZE(syms);
430         opts.uprobe_multi.pid = child ? child->pid : 0;
431
432         skel = uprobe_multi__open_and_load();
433         if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
434                 goto cleanup;
435
436         opts.kprobe_multi.flags = 0;
437         prog_fd = bpf_program__fd(skel->progs.uprobe);
438         link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
439         if (!ASSERT_GE(link1_fd, 0, "link1_fd"))
440                 goto cleanup;
441
442         opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;
443         prog_fd = bpf_program__fd(skel->progs.uretprobe);
444         link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
445         if (!ASSERT_GE(link2_fd, 0, "link2_fd"))
446                 goto cleanup;
447
448         opts.kprobe_multi.flags = 0;
449         prog_fd = bpf_program__fd(skel->progs.uprobe_sleep);
450         link3_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
451         if (!ASSERT_GE(link3_fd, 0, "link3_fd"))
452                 goto cleanup;
453
454         opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN;
455         prog_fd = bpf_program__fd(skel->progs.uretprobe_sleep);
456         link4_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
457         if (!ASSERT_GE(link4_fd, 0, "link4_fd"))
458                 goto cleanup;
459
460         opts.kprobe_multi.flags = 0;
461         opts.uprobe_multi.pid = 0;
462         prog_fd = bpf_program__fd(skel->progs.uprobe_extra);
463         link_extra_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
464         if (!ASSERT_GE(link_extra_fd, 0, "link_extra_fd"))
465                 goto cleanup;
466
467         uprobe_multi_test_run(skel, child);
468
469 cleanup:
470         if (link1_fd >= 0)
471                 close(link1_fd);
472         if (link2_fd >= 0)
473                 close(link2_fd);
474         if (link3_fd >= 0)
475                 close(link3_fd);
476         if (link4_fd >= 0)
477                 close(link4_fd);
478         if (link_extra_fd >= 0)
479                 close(link_extra_fd);
480
481         uprobe_multi__destroy(skel);
482         free(offsets);
483 }
484
485 static void test_link_api(void)
486 {
487         struct child *child;
488
489         /* no pid filter */
490         __test_link_api(NULL);
491
492         /* pid filter */
493         child = spawn_child();
494         if (!ASSERT_OK_PTR(child, "spawn_child"))
495                 return;
496
497         __test_link_api(child);
498 }
499
500 static void test_bench_attach_uprobe(void)
501 {
502         long attach_start_ns = 0, attach_end_ns = 0;
503         struct uprobe_multi_bench *skel = NULL;
504         long detach_start_ns, detach_end_ns;
505         double attach_delta, detach_delta;
506         int err;
507
508         skel = uprobe_multi_bench__open_and_load();
509         if (!ASSERT_OK_PTR(skel, "uprobe_multi_bench__open_and_load"))
510                 goto cleanup;
511
512         attach_start_ns = get_time_ns();
513
514         err = uprobe_multi_bench__attach(skel);
515         if (!ASSERT_OK(err, "uprobe_multi_bench__attach"))
516                 goto cleanup;
517
518         attach_end_ns = get_time_ns();
519
520         system("./uprobe_multi bench");
521
522         ASSERT_EQ(skel->bss->count, 50000, "uprobes_count");
523
524 cleanup:
525         detach_start_ns = get_time_ns();
526         uprobe_multi_bench__destroy(skel);
527         detach_end_ns = get_time_ns();
528
529         attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
530         detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
531
532         printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
533         printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
534 }
535
536 static void test_bench_attach_usdt(void)
537 {
538         long attach_start_ns = 0, attach_end_ns = 0;
539         struct uprobe_multi_usdt *skel = NULL;
540         long detach_start_ns, detach_end_ns;
541         double attach_delta, detach_delta;
542
543         skel = uprobe_multi_usdt__open_and_load();
544         if (!ASSERT_OK_PTR(skel, "uprobe_multi__open"))
545                 goto cleanup;
546
547         attach_start_ns = get_time_ns();
548
549         skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, -1, "./uprobe_multi",
550                                                      "test", "usdt", NULL);
551         if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt"))
552                 goto cleanup;
553
554         attach_end_ns = get_time_ns();
555
556         system("./uprobe_multi usdt");
557
558         ASSERT_EQ(skel->bss->count, 50000, "usdt_count");
559
560 cleanup:
561         detach_start_ns = get_time_ns();
562         uprobe_multi_usdt__destroy(skel);
563         detach_end_ns = get_time_ns();
564
565         attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0;
566         detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0;
567
568         printf("%s: attached in %7.3lfs\n", __func__, attach_delta);
569         printf("%s: detached in %7.3lfs\n", __func__, detach_delta);
570 }
571
572 void test_uprobe_multi_test(void)
573 {
574         if (test__start_subtest("skel_api"))
575                 test_skel_api();
576         if (test__start_subtest("attach_api_pattern"))
577                 test_attach_api_pattern();
578         if (test__start_subtest("attach_api_syms"))
579                 test_attach_api_syms();
580         if (test__start_subtest("link_api"))
581                 test_link_api();
582         if (test__start_subtest("bench_uprobe"))
583                 test_bench_attach_uprobe();
584         if (test__start_subtest("bench_usdt"))
585                 test_bench_attach_usdt();
586         if (test__start_subtest("attach_api_fails"))
587                 test_attach_api_fails();
588 }
This page took 0.073882 seconds and 4 git commands to generate.