1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3#include <test_progs.h>
4
5#define _SDT_HAS_SEMAPHORES 1
6#include "../sdt.h"
7
8#include "test_usdt.skel.h"
9#include "test_urandom_usdt.skel.h"
10
11int lets_test_this(int);
12
13static volatile int idx = 2;
14static volatile __u64 bla = 0xFEDCBA9876543210ULL;
15static volatile short nums[] = {-1, -2, -3, -4};
16
17static volatile struct {
18	int x;
19	signed char y;
20} t1 = { 1, -127 };
21
22#define SEC(name) __attribute__((section(name), used))
23
24unsigned short test_usdt0_semaphore SEC(".probes");
25unsigned short test_usdt3_semaphore SEC(".probes");
26unsigned short test_usdt12_semaphore SEC(".probes");
27
28static void __always_inline trigger_func(int x) {
29	long y = 42;
30
31	if (test_usdt0_semaphore)
32		STAP_PROBE(test, usdt0);
33	if (test_usdt3_semaphore)
34		STAP_PROBE3(test, usdt3, x, y, &bla);
35	if (test_usdt12_semaphore) {
36		STAP_PROBE12(test, usdt12,
37			     x, x + 1, y, x + y, 5,
38			     y / 7, bla, &bla, -9, nums[x],
39			     nums[idx], t1.y);
40	}
41}
42
43static void subtest_basic_usdt(void)
44{
45	LIBBPF_OPTS(bpf_usdt_opts, opts);
46	struct test_usdt *skel;
47	struct test_usdt__bss *bss;
48	int err;
49
50	skel = test_usdt__open_and_load();
51	if (!ASSERT_OK_PTR(skel, "skel_open"))
52		return;
53
54	bss = skel->bss;
55	bss->my_pid = getpid();
56
57	err = test_usdt__attach(skel);
58	if (!ASSERT_OK(err, "skel_attach"))
59		goto cleanup;
60
61	/* usdt0 won't be auto-attached */
62	opts.usdt_cookie = 0xcafedeadbeeffeed;
63	skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0,
64						     0 /*self*/, "/proc/self/exe",
65						     "test", "usdt0", &opts);
66	if (!ASSERT_OK_PTR(skel->links.usdt0, "usdt0_link"))
67		goto cleanup;
68
69	trigger_func(1);
70
71	ASSERT_EQ(bss->usdt0_called, 1, "usdt0_called");
72	ASSERT_EQ(bss->usdt3_called, 1, "usdt3_called");
73	ASSERT_EQ(bss->usdt12_called, 1, "usdt12_called");
74
75	ASSERT_EQ(bss->usdt0_cookie, 0xcafedeadbeeffeed, "usdt0_cookie");
76	ASSERT_EQ(bss->usdt0_arg_cnt, 0, "usdt0_arg_cnt");
77	ASSERT_EQ(bss->usdt0_arg_ret, -ENOENT, "usdt0_arg_ret");
78
79	/* auto-attached usdt3 gets default zero cookie value */
80	ASSERT_EQ(bss->usdt3_cookie, 0, "usdt3_cookie");
81	ASSERT_EQ(bss->usdt3_arg_cnt, 3, "usdt3_arg_cnt");
82
83	ASSERT_EQ(bss->usdt3_arg_rets[0], 0, "usdt3_arg1_ret");
84	ASSERT_EQ(bss->usdt3_arg_rets[1], 0, "usdt3_arg2_ret");
85	ASSERT_EQ(bss->usdt3_arg_rets[2], 0, "usdt3_arg3_ret");
86	ASSERT_EQ(bss->usdt3_args[0], 1, "usdt3_arg1");
87	ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
88	ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
89
90	/* auto-attached usdt12 gets default zero cookie value */
91	ASSERT_EQ(bss->usdt12_cookie, 0, "usdt12_cookie");
92	ASSERT_EQ(bss->usdt12_arg_cnt, 12, "usdt12_arg_cnt");
93
94	ASSERT_EQ(bss->usdt12_args[0], 1, "usdt12_arg1");
95	ASSERT_EQ(bss->usdt12_args[1], 1 + 1, "usdt12_arg2");
96	ASSERT_EQ(bss->usdt12_args[2], 42, "usdt12_arg3");
97	ASSERT_EQ(bss->usdt12_args[3], 42 + 1, "usdt12_arg4");
98	ASSERT_EQ(bss->usdt12_args[4], 5, "usdt12_arg5");
99	ASSERT_EQ(bss->usdt12_args[5], 42 / 7, "usdt12_arg6");
100	ASSERT_EQ(bss->usdt12_args[6], bla, "usdt12_arg7");
101	ASSERT_EQ(bss->usdt12_args[7], (uintptr_t)&bla, "usdt12_arg8");
102	ASSERT_EQ(bss->usdt12_args[8], -9, "usdt12_arg9");
103	ASSERT_EQ(bss->usdt12_args[9], nums[1], "usdt12_arg10");
104	ASSERT_EQ(bss->usdt12_args[10], nums[idx], "usdt12_arg11");
105	ASSERT_EQ(bss->usdt12_args[11], t1.y, "usdt12_arg12");
106
107	/* trigger_func() is marked __always_inline, so USDT invocations will be
108	 * inlined in two different places, meaning that each USDT will have
109	 * at least 2 different places to be attached to. This verifies that
110	 * bpf_program__attach_usdt() handles this properly and attaches to
111	 * all possible places of USDT invocation.
112	 */
113	trigger_func(2);
114
115	ASSERT_EQ(bss->usdt0_called, 2, "usdt0_called");
116	ASSERT_EQ(bss->usdt3_called, 2, "usdt3_called");
117	ASSERT_EQ(bss->usdt12_called, 2, "usdt12_called");
118
119	/* only check values that depend on trigger_func()'s input value */
120	ASSERT_EQ(bss->usdt3_args[0], 2, "usdt3_arg1");
121
122	ASSERT_EQ(bss->usdt12_args[0], 2, "usdt12_arg1");
123	ASSERT_EQ(bss->usdt12_args[1], 2 + 1, "usdt12_arg2");
124	ASSERT_EQ(bss->usdt12_args[3], 42 + 2, "usdt12_arg4");
125	ASSERT_EQ(bss->usdt12_args[9], nums[2], "usdt12_arg10");
126
127	/* detach and re-attach usdt3 */
128	bpf_link__destroy(skel->links.usdt3);
129
130	opts.usdt_cookie = 0xBADC00C51E;
131	skel->links.usdt3 = bpf_program__attach_usdt(skel->progs.usdt3, -1 /* any pid */,
132						     "/proc/self/exe", "test", "usdt3", &opts);
133	if (!ASSERT_OK_PTR(skel->links.usdt3, "usdt3_reattach"))
134		goto cleanup;
135
136	trigger_func(3);
137
138	ASSERT_EQ(bss->usdt3_called, 3, "usdt3_called");
139	/* this time usdt3 has custom cookie */
140	ASSERT_EQ(bss->usdt3_cookie, 0xBADC00C51E, "usdt3_cookie");
141	ASSERT_EQ(bss->usdt3_arg_cnt, 3, "usdt3_arg_cnt");
142
143	ASSERT_EQ(bss->usdt3_arg_rets[0], 0, "usdt3_arg1_ret");
144	ASSERT_EQ(bss->usdt3_arg_rets[1], 0, "usdt3_arg2_ret");
145	ASSERT_EQ(bss->usdt3_arg_rets[2], 0, "usdt3_arg3_ret");
146	ASSERT_EQ(bss->usdt3_args[0], 3, "usdt3_arg1");
147	ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
148	ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
149
150cleanup:
151	test_usdt__destroy(skel);
152}
153
154unsigned short test_usdt_100_semaphore SEC(".probes");
155unsigned short test_usdt_300_semaphore SEC(".probes");
156unsigned short test_usdt_400_semaphore SEC(".probes");
157
158#define R10(F, X)  F(X+0); F(X+1);F(X+2); F(X+3); F(X+4); \
159		   F(X+5); F(X+6); F(X+7); F(X+8); F(X+9);
160#define R100(F, X) R10(F,X+ 0);R10(F,X+10);R10(F,X+20);R10(F,X+30);R10(F,X+40); \
161		   R10(F,X+50);R10(F,X+60);R10(F,X+70);R10(F,X+80);R10(F,X+90);
162
163/* carefully control that we get exactly 100 inlines by preventing inlining */
164static void __always_inline f100(int x)
165{
166	STAP_PROBE1(test, usdt_100, x);
167}
168
169__weak void trigger_100_usdts(void)
170{
171	R100(f100, 0);
172}
173
174/* we shouldn't be able to attach to test:usdt2_300 USDT as we don't have as
175 * many slots for specs. It's important that each STAP_PROBE2() invocation
176 * (after untolling) gets different arg spec due to compiler inlining i as
177 * a constant
178 */
179static void __always_inline f300(int x)
180{
181	STAP_PROBE1(test, usdt_300, x);
182}
183
184__weak void trigger_300_usdts(void)
185{
186	R100(f300, 0);
187	R100(f300, 100);
188	R100(f300, 200);
189}
190
191static void __always_inline f400(int x __attribute__((unused)))
192{
193	STAP_PROBE1(test, usdt_400, 400);
194}
195
196/* this time we have 400 different USDT call sites, but they have uniform
197 * argument location, so libbpf's spec string deduplication logic should keep
198 * spec count use very small and so we should be able to attach to all 400
199 * call sites
200 */
201__weak void trigger_400_usdts(void)
202{
203	R100(f400, 0);
204	R100(f400, 100);
205	R100(f400, 200);
206	R100(f400, 300);
207}
208
209static void subtest_multispec_usdt(void)
210{
211	LIBBPF_OPTS(bpf_usdt_opts, opts);
212	struct test_usdt *skel;
213	struct test_usdt__bss *bss;
214	int err, i;
215
216	skel = test_usdt__open_and_load();
217	if (!ASSERT_OK_PTR(skel, "skel_open"))
218		return;
219
220	bss = skel->bss;
221	bss->my_pid = getpid();
222
223	err = test_usdt__attach(skel);
224	if (!ASSERT_OK(err, "skel_attach"))
225		goto cleanup;
226
227	/* usdt_100 is auto-attached and there are 100 inlined call sites,
228	 * let's validate that all of them are properly attached to and
229	 * handled from BPF side
230	 */
231	trigger_100_usdts();
232
233	ASSERT_EQ(bss->usdt_100_called, 100, "usdt_100_called");
234	ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
235
236	/* Stress test free spec ID tracking. By default libbpf allows up to
237	 * 256 specs to be used, so if we don't return free spec IDs back
238	 * after few detachments and re-attachments we should run out of
239	 * available spec IDs.
240	 */
241	for (i = 0; i < 2; i++) {
242		bpf_link__destroy(skel->links.usdt_100);
243
244		skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
245							        "/proc/self/exe",
246								"test", "usdt_100", NULL);
247		if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_100_reattach"))
248			goto cleanup;
249
250		bss->usdt_100_sum = 0;
251		trigger_100_usdts();
252
253		ASSERT_EQ(bss->usdt_100_called, (i + 1) * 100 + 100, "usdt_100_called");
254		ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
255	}
256
257	/* Now let's step it up and try to attach USDT that requires more than
258	 * 256 attach points with different specs for each.
259	 * Note that we need trigger_300_usdts() only to actually have 300
260	 * USDT call sites, we are not going to actually trace them.
261	 */
262	trigger_300_usdts();
263
264	/* we'll reuse usdt_100 BPF program for usdt_300 test */
265	bpf_link__destroy(skel->links.usdt_100);
266	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1, "/proc/self/exe",
267							"test", "usdt_300", NULL);
268	err = -errno;
269	if (!ASSERT_ERR_PTR(skel->links.usdt_100, "usdt_300_bad_attach"))
270		goto cleanup;
271	ASSERT_EQ(err, -E2BIG, "usdt_300_attach_err");
272
273	/* let's check that there are no "dangling" BPF programs attached due
274	 * to partial success of the above test:usdt_300 attachment
275	 */
276	bss->usdt_100_called = 0;
277	bss->usdt_100_sum = 0;
278
279	f300(777); /* this is 301st instance of usdt_300 */
280
281	ASSERT_EQ(bss->usdt_100_called, 0, "usdt_301_called");
282	ASSERT_EQ(bss->usdt_100_sum, 0, "usdt_301_sum");
283
284	/* This time we have USDT with 400 inlined invocations, but arg specs
285	 * should be the same across all sites, so libbpf will only need to
286	 * use one spec and thus we'll be able to attach 400 uprobes
287	 * successfully.
288	 *
289	 * Again, we are reusing usdt_100 BPF program.
290	 */
291	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
292							"/proc/self/exe",
293							"test", "usdt_400", NULL);
294	if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_400_attach"))
295		goto cleanup;
296
297	trigger_400_usdts();
298
299	ASSERT_EQ(bss->usdt_100_called, 400, "usdt_400_called");
300	ASSERT_EQ(bss->usdt_100_sum, 400 * 400, "usdt_400_sum");
301
302cleanup:
303	test_usdt__destroy(skel);
304}
305
306static FILE *urand_spawn(int *pid)
307{
308	FILE *f;
309
310	/* urandom_read's stdout is wired into f */
311	f = popen("./urandom_read 1 report-pid", "r");
312	if (!f)
313		return NULL;
314
315	if (fscanf(f, "%d", pid) != 1) {
316		pclose(f);
317		errno = EINVAL;
318		return NULL;
319	}
320
321	return f;
322}
323
324static int urand_trigger(FILE **urand_pipe)
325{
326	int exit_code;
327
328	/* pclose() waits for child process to exit and returns their exit code */
329	exit_code = pclose(*urand_pipe);
330	*urand_pipe = NULL;
331
332	return exit_code;
333}
334
335static void subtest_urandom_usdt(bool auto_attach)
336{
337	struct test_urandom_usdt *skel;
338	struct test_urandom_usdt__bss *bss;
339	struct bpf_link *l;
340	FILE *urand_pipe = NULL;
341	int err, urand_pid = 0;
342
343	skel = test_urandom_usdt__open_and_load();
344	if (!ASSERT_OK_PTR(skel, "skel_open"))
345		return;
346
347	urand_pipe = urand_spawn(&urand_pid);
348	if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
349		goto cleanup;
350
351	bss = skel->bss;
352	bss->urand_pid = urand_pid;
353
354	if (auto_attach) {
355		err = test_urandom_usdt__attach(skel);
356		if (!ASSERT_OK(err, "skel_auto_attach"))
357			goto cleanup;
358	} else {
359		l = bpf_program__attach_usdt(skel->progs.urand_read_without_sema,
360					     urand_pid, "./urandom_read",
361					     "urand", "read_without_sema", NULL);
362		if (!ASSERT_OK_PTR(l, "urand_without_sema_attach"))
363			goto cleanup;
364		skel->links.urand_read_without_sema = l;
365
366		l = bpf_program__attach_usdt(skel->progs.urand_read_with_sema,
367					     urand_pid, "./urandom_read",
368					     "urand", "read_with_sema", NULL);
369		if (!ASSERT_OK_PTR(l, "urand_with_sema_attach"))
370			goto cleanup;
371		skel->links.urand_read_with_sema = l;
372
373		l = bpf_program__attach_usdt(skel->progs.urandlib_read_without_sema,
374					     urand_pid, "./liburandom_read.so",
375					     "urandlib", "read_without_sema", NULL);
376		if (!ASSERT_OK_PTR(l, "urandlib_without_sema_attach"))
377			goto cleanup;
378		skel->links.urandlib_read_without_sema = l;
379
380		l = bpf_program__attach_usdt(skel->progs.urandlib_read_with_sema,
381					     urand_pid, "./liburandom_read.so",
382					     "urandlib", "read_with_sema", NULL);
383		if (!ASSERT_OK_PTR(l, "urandlib_with_sema_attach"))
384			goto cleanup;
385		skel->links.urandlib_read_with_sema = l;
386
387	}
388
389	/* trigger urandom_read USDTs */
390	ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");
391
392	ASSERT_EQ(bss->urand_read_without_sema_call_cnt, 1, "urand_wo_sema_cnt");
393	ASSERT_EQ(bss->urand_read_without_sema_buf_sz_sum, 256, "urand_wo_sema_sum");
394
395	ASSERT_EQ(bss->urand_read_with_sema_call_cnt, 1, "urand_w_sema_cnt");
396	ASSERT_EQ(bss->urand_read_with_sema_buf_sz_sum, 256, "urand_w_sema_sum");
397
398	ASSERT_EQ(bss->urandlib_read_without_sema_call_cnt, 1, "urandlib_wo_sema_cnt");
399	ASSERT_EQ(bss->urandlib_read_without_sema_buf_sz_sum, 256, "urandlib_wo_sema_sum");
400
401	ASSERT_EQ(bss->urandlib_read_with_sema_call_cnt, 1, "urandlib_w_sema_cnt");
402	ASSERT_EQ(bss->urandlib_read_with_sema_buf_sz_sum, 256, "urandlib_w_sema_sum");
403
404cleanup:
405	if (urand_pipe)
406		pclose(urand_pipe);
407	test_urandom_usdt__destroy(skel);
408}
409
410void test_usdt(void)
411{
412	if (test__start_subtest("basic"))
413		subtest_basic_usdt();
414	if (test__start_subtest("multispec"))
415		subtest_multispec_usdt();
416	if (test__start_subtest("urand_auto_attach"))
417		subtest_urandom_usdt(true /* auto_attach */);
418	if (test__start_subtest("urand_pid_attach"))
419		subtest_urandom_usdt(false /* auto_attach */);
420}
421