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