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 
11 int lets_test_this(int);
12 
13 static volatile int idx = 2;
14 static volatile __u64 bla = 0xFEDCBA9876543210ULL;
15 static volatile short nums[] = {-1, -2, -3, };
16 
17 static volatile struct {
18 	int x;
19 	signed char y;
20 } t1 = { 1, -127 };
21 
22 #define SEC(name) __attribute__((section(name), used))
23 
24 unsigned short test_usdt0_semaphore SEC(".probes");
25 unsigned short test_usdt3_semaphore SEC(".probes");
26 unsigned short test_usdt12_semaphore SEC(".probes");
27 
28 static 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 
43 static 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 
150 cleanup:
151 	test_usdt__destroy(skel);
152 }
153 
154 unsigned short test_usdt_100_semaphore SEC(".probes");
155 unsigned short test_usdt_300_semaphore SEC(".probes");
156 unsigned 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 */
164 static 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  */
179 static 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 
191 static void __always_inline f400(int x __attribute__((unused)))
192 {
193 	static int y;
194 
195 	STAP_PROBE1(test, usdt_400, y++);
196 }
197 
198 /* this time we have 400 different USDT call sites, but they have uniform
199  * argument location, so libbpf's spec string deduplication logic should keep
200  * spec count use very small and so we should be able to attach to all 400
201  * call sites
202  */
203 __weak void trigger_400_usdts(void)
204 {
205 	R100(f400, 0);
206 	R100(f400, 100);
207 	R100(f400, 200);
208 	R100(f400, 300);
209 }
210 
211 static void subtest_multispec_usdt(void)
212 {
213 	LIBBPF_OPTS(bpf_usdt_opts, opts);
214 	struct test_usdt *skel;
215 	struct test_usdt__bss *bss;
216 	int err, i;
217 
218 	skel = test_usdt__open_and_load();
219 	if (!ASSERT_OK_PTR(skel, "skel_open"))
220 		return;
221 
222 	bss = skel->bss;
223 	bss->my_pid = getpid();
224 
225 	err = test_usdt__attach(skel);
226 	if (!ASSERT_OK(err, "skel_attach"))
227 		goto cleanup;
228 
229 	/* usdt_100 is auto-attached and there are 100 inlined call sites,
230 	 * let's validate that all of them are properly attached to and
231 	 * handled from BPF side
232 	 */
233 	trigger_100_usdts();
234 
235 	ASSERT_EQ(bss->usdt_100_called, 100, "usdt_100_called");
236 	ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
237 
238 	/* Stress test free spec ID tracking. By default libbpf allows up to
239 	 * 256 specs to be used, so if we don't return free spec IDs back
240 	 * after few detachments and re-attachments we should run out of
241 	 * available spec IDs.
242 	 */
243 	for (i = 0; i < 2; i++) {
244 		bpf_link__destroy(skel->links.usdt_100);
245 
246 		skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
247 							        "/proc/self/exe",
248 								"test", "usdt_100", NULL);
249 		if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_100_reattach"))
250 			goto cleanup;
251 
252 		bss->usdt_100_sum = 0;
253 		trigger_100_usdts();
254 
255 		ASSERT_EQ(bss->usdt_100_called, (i + 1) * 100 + 100, "usdt_100_called");
256 		ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
257 	}
258 
259 	/* Now let's step it up and try to attach USDT that requires more than
260 	 * 256 attach points with different specs for each.
261 	 * Note that we need trigger_300_usdts() only to actually have 300
262 	 * USDT call sites, we are not going to actually trace them.
263 	 */
264 	trigger_300_usdts();
265 
266 	/* we'll reuse usdt_100 BPF program for usdt_300 test */
267 	bpf_link__destroy(skel->links.usdt_100);
268 	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1, "/proc/self/exe",
269 							"test", "usdt_300", NULL);
270 	err = -errno;
271 	if (!ASSERT_ERR_PTR(skel->links.usdt_100, "usdt_300_bad_attach"))
272 		goto cleanup;
273 	ASSERT_EQ(err, -E2BIG, "usdt_300_attach_err");
274 
275 	/* let's check that there are no "dangling" BPF programs attached due
276 	 * to partial success of the above test:usdt_300 attachment
277 	 */
278 	bss->usdt_100_called = 0;
279 	bss->usdt_100_sum = 0;
280 
281 	f300(777); /* this is 301st instance of usdt_300 */
282 
283 	ASSERT_EQ(bss->usdt_100_called, 0, "usdt_301_called");
284 	ASSERT_EQ(bss->usdt_100_sum, 0, "usdt_301_sum");
285 
286 	/* This time we have USDT with 400 inlined invocations, but arg specs
287 	 * should be the same across all sites, so libbpf will only need to
288 	 * use one spec and thus we'll be able to attach 400 uprobes
289 	 * successfully.
290 	 *
291 	 * Again, we are reusing usdt_100 BPF program.
292 	 */
293 	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
294 							"/proc/self/exe",
295 							"test", "usdt_400", NULL);
296 	if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_400_attach"))
297 		goto cleanup;
298 
299 	trigger_400_usdts();
300 
301 	ASSERT_EQ(bss->usdt_100_called, 400, "usdt_400_called");
302 	ASSERT_EQ(bss->usdt_100_sum, 399 * 400 / 2, "usdt_400_sum");
303 
304 cleanup:
305 	test_usdt__destroy(skel);
306 }
307 
308 static FILE *urand_spawn(int *pid)
309 {
310 	FILE *f;
311 
312 	/* urandom_read's stdout is wired into f */
313 	f = popen("./urandom_read 1 report-pid", "r");
314 	if (!f)
315 		return NULL;
316 
317 	if (fscanf(f, "%d", pid) != 1) {
318 		pclose(f);
319 		return NULL;
320 	}
321 
322 	return f;
323 }
324 
325 static int urand_trigger(FILE **urand_pipe)
326 {
327 	int exit_code;
328 
329 	/* pclose() waits for child process to exit and returns their exit code */
330 	exit_code = pclose(*urand_pipe);
331 	*urand_pipe = NULL;
332 
333 	return exit_code;
334 }
335 
336 static void subtest_urandom_usdt(bool auto_attach)
337 {
338 	struct test_urandom_usdt *skel;
339 	struct test_urandom_usdt__bss *bss;
340 	struct bpf_link *l;
341 	FILE *urand_pipe = NULL;
342 	int err, urand_pid = 0;
343 
344 	skel = test_urandom_usdt__open_and_load();
345 	if (!ASSERT_OK_PTR(skel, "skel_open"))
346 		return;
347 
348 	urand_pipe = urand_spawn(&urand_pid);
349 	if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
350 		goto cleanup;
351 
352 	bss = skel->bss;
353 	bss->urand_pid = urand_pid;
354 
355 	if (auto_attach) {
356 		err = test_urandom_usdt__attach(skel);
357 		if (!ASSERT_OK(err, "skel_auto_attach"))
358 			goto cleanup;
359 	} else {
360 		l = bpf_program__attach_usdt(skel->progs.urand_read_without_sema,
361 					     urand_pid, "./urandom_read",
362 					     "urand", "read_without_sema", NULL);
363 		if (!ASSERT_OK_PTR(l, "urand_without_sema_attach"))
364 			goto cleanup;
365 		skel->links.urand_read_without_sema = l;
366 
367 		l = bpf_program__attach_usdt(skel->progs.urand_read_with_sema,
368 					     urand_pid, "./urandom_read",
369 					     "urand", "read_with_sema", NULL);
370 		if (!ASSERT_OK_PTR(l, "urand_with_sema_attach"))
371 			goto cleanup;
372 		skel->links.urand_read_with_sema = l;
373 
374 		l = bpf_program__attach_usdt(skel->progs.urandlib_read_without_sema,
375 					     urand_pid, "./liburandom_read.so",
376 					     "urandlib", "read_without_sema", NULL);
377 		if (!ASSERT_OK_PTR(l, "urandlib_without_sema_attach"))
378 			goto cleanup;
379 		skel->links.urandlib_read_without_sema = l;
380 
381 		l = bpf_program__attach_usdt(skel->progs.urandlib_read_with_sema,
382 					     urand_pid, "./liburandom_read.so",
383 					     "urandlib", "read_with_sema", NULL);
384 		if (!ASSERT_OK_PTR(l, "urandlib_with_sema_attach"))
385 			goto cleanup;
386 		skel->links.urandlib_read_with_sema = l;
387 
388 	}
389 
390 	/* trigger urandom_read USDTs */
391 	ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");
392 
393 	ASSERT_EQ(bss->urand_read_without_sema_call_cnt, 1, "urand_wo_sema_cnt");
394 	ASSERT_EQ(bss->urand_read_without_sema_buf_sz_sum, 256, "urand_wo_sema_sum");
395 
396 	ASSERT_EQ(bss->urand_read_with_sema_call_cnt, 1, "urand_w_sema_cnt");
397 	ASSERT_EQ(bss->urand_read_with_sema_buf_sz_sum, 256, "urand_w_sema_sum");
398 
399 	ASSERT_EQ(bss->urandlib_read_without_sema_call_cnt, 1, "urandlib_wo_sema_cnt");
400 	ASSERT_EQ(bss->urandlib_read_without_sema_buf_sz_sum, 256, "urandlib_wo_sema_sum");
401 
402 	ASSERT_EQ(bss->urandlib_read_with_sema_call_cnt, 1, "urandlib_w_sema_cnt");
403 	ASSERT_EQ(bss->urandlib_read_with_sema_buf_sz_sum, 256, "urandlib_w_sema_sum");
404 
405 cleanup:
406 	if (urand_pipe)
407 		pclose(urand_pipe);
408 	test_urandom_usdt__destroy(skel);
409 }
410 
411 void test_usdt(void)
412 {
413 	if (test__start_subtest("basic"))
414 		subtest_basic_usdt();
415 	if (test__start_subtest("multispec"))
416 		subtest_multispec_usdt();
417 	if (test__start_subtest("urand_auto_attach"))
418 		subtest_urandom_usdt(true /* auto_attach */);
419 	if (test__start_subtest("urand_pid_attach"))
420 		subtest_urandom_usdt(false /* auto_attach */);
421 }
422