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