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