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