xref: /linux/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c (revision bf5802238dc181b1f7375d358af1d01cd72d1c11)
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 }
589