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
11static char test_data[] = "test_data";
12
13noinline void uprobe_multi_func_1(void)
14{
15	asm volatile ("");
16}
17
18noinline void uprobe_multi_func_2(void)
19{
20	asm volatile ("");
21}
22
23noinline void uprobe_multi_func_3(void)
24{
25	asm volatile ("");
26}
27
28struct child {
29	int go[2];
30	int pid;
31};
32
33static 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
45static 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
56static 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
92static 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
133static 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
148cleanup:
149	uprobe_multi__destroy(skel);
150}
151
152static 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
195cleanup:
196	uprobe_multi__destroy(skel);
197}
198
199static void
200test_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
215static 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
223static 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
237static 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
402cleanup:
403	if (link_fd >= 0)
404		close(link_fd);
405	uprobe_multi__destroy(skel);
406}
407
408static 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
469cleanup:
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
485static 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
500static 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
524cleanup:
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
536static 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
560cleanup:
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
572void 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