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