421 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */3#include <test_progs.h>4 5#define _SDT_HAS_SEMAPHORES 16#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, -4};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 be108 * inlined in two different places, meaning that each USDT will have109 * at least 2 different places to be attached to. This verifies that110 * bpf_program__attach_usdt() handles this properly and attaches to111 * 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 as175 * many slots for specs. It's important that each STAP_PROBE2() invocation176 * (after untolling) gets different arg spec due to compiler inlining i as177 * a constant178 */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 uniform197 * argument location, so libbpf's spec string deduplication logic should keep198 * spec count use very small and so we should be able to attach to all 400199 * call sites200 */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 and229 * handled from BPF side230 */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 to237 * 256 specs to be used, so if we don't return free spec IDs back238 * after few detachments and re-attachments we should run out of239 * 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 than258 * 256 attach points with different specs for each.259 * Note that we need trigger_300_usdts() only to actually have 300260 * 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 due274 * to partial success of the above test:usdt_300 attachment275 */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 specs285 * should be the same across all sites, so libbpf will only need to286 * use one spec and thus we'll be able to attach 400 uprobes287 * 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 errno = EINVAL;318 return NULL;319 }320 321 return f;322}323 324static int urand_trigger(FILE **urand_pipe)325{326 int exit_code;327 328 /* pclose() waits for child process to exit and returns their exit code */329 exit_code = pclose(*urand_pipe);330 *urand_pipe = NULL;331 332 return exit_code;333}334 335static void subtest_urandom_usdt(bool auto_attach)336{337 struct test_urandom_usdt *skel;338 struct test_urandom_usdt__bss *bss;339 struct bpf_link *l;340 FILE *urand_pipe = NULL;341 int err, urand_pid = 0;342 343 skel = test_urandom_usdt__open_and_load();344 if (!ASSERT_OK_PTR(skel, "skel_open"))345 return;346 347 urand_pipe = urand_spawn(&urand_pid);348 if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))349 goto cleanup;350 351 bss = skel->bss;352 bss->urand_pid = urand_pid;353 354 if (auto_attach) {355 err = test_urandom_usdt__attach(skel);356 if (!ASSERT_OK(err, "skel_auto_attach"))357 goto cleanup;358 } else {359 l = bpf_program__attach_usdt(skel->progs.urand_read_without_sema,360 urand_pid, "./urandom_read",361 "urand", "read_without_sema", NULL);362 if (!ASSERT_OK_PTR(l, "urand_without_sema_attach"))363 goto cleanup;364 skel->links.urand_read_without_sema = l;365 366 l = bpf_program__attach_usdt(skel->progs.urand_read_with_sema,367 urand_pid, "./urandom_read",368 "urand", "read_with_sema", NULL);369 if (!ASSERT_OK_PTR(l, "urand_with_sema_attach"))370 goto cleanup;371 skel->links.urand_read_with_sema = l;372 373 l = bpf_program__attach_usdt(skel->progs.urandlib_read_without_sema,374 urand_pid, "./liburandom_read.so",375 "urandlib", "read_without_sema", NULL);376 if (!ASSERT_OK_PTR(l, "urandlib_without_sema_attach"))377 goto cleanup;378 skel->links.urandlib_read_without_sema = l;379 380 l = bpf_program__attach_usdt(skel->progs.urandlib_read_with_sema,381 urand_pid, "./liburandom_read.so",382 "urandlib", "read_with_sema", NULL);383 if (!ASSERT_OK_PTR(l, "urandlib_with_sema_attach"))384 goto cleanup;385 skel->links.urandlib_read_with_sema = l;386 387 }388 389 /* trigger urandom_read USDTs */390 ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");391 392 ASSERT_EQ(bss->urand_read_without_sema_call_cnt, 1, "urand_wo_sema_cnt");393 ASSERT_EQ(bss->urand_read_without_sema_buf_sz_sum, 256, "urand_wo_sema_sum");394 395 ASSERT_EQ(bss->urand_read_with_sema_call_cnt, 1, "urand_w_sema_cnt");396 ASSERT_EQ(bss->urand_read_with_sema_buf_sz_sum, 256, "urand_w_sema_sum");397 398 ASSERT_EQ(bss->urandlib_read_without_sema_call_cnt, 1, "urandlib_wo_sema_cnt");399 ASSERT_EQ(bss->urandlib_read_without_sema_buf_sz_sum, 256, "urandlib_wo_sema_sum");400 401 ASSERT_EQ(bss->urandlib_read_with_sema_call_cnt, 1, "urandlib_w_sema_cnt");402 ASSERT_EQ(bss->urandlib_read_with_sema_buf_sz_sum, 256, "urandlib_w_sema_sum");403 404cleanup:405 if (urand_pipe)406 pclose(urand_pipe);407 test_urandom_usdt__destroy(skel);408}409 410void test_usdt(void)411{412 if (test__start_subtest("basic"))413 subtest_basic_usdt();414 if (test__start_subtest("multispec"))415 subtest_multispec_usdt();416 if (test__start_subtest("urand_auto_attach"))417 subtest_urandom_usdt(true /* auto_attach */);418 if (test__start_subtest("urand_pid_attach"))419 subtest_urandom_usdt(false /* auto_attach */);420}421