Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
0003 #include <test_progs.h>
0004 
0005 #define _SDT_HAS_SEMAPHORES 1
0006 #include "../sdt.h"
0007 
0008 #include "test_usdt.skel.h"
0009 #include "test_urandom_usdt.skel.h"
0010 
0011 int lets_test_this(int);
0012 
0013 static volatile int idx = 2;
0014 static volatile __u64 bla = 0xFEDCBA9876543210ULL;
0015 static volatile short nums[] = {-1, -2, -3, -4};
0016 
0017 static volatile struct {
0018     int x;
0019     signed char y;
0020 } t1 = { 1, -127 };
0021 
0022 #define SEC(name) __attribute__((section(name), used))
0023 
0024 unsigned short test_usdt0_semaphore SEC(".probes");
0025 unsigned short test_usdt3_semaphore SEC(".probes");
0026 unsigned short test_usdt12_semaphore SEC(".probes");
0027 
0028 static void __always_inline trigger_func(int x) {
0029     long y = 42;
0030 
0031     if (test_usdt0_semaphore)
0032         STAP_PROBE(test, usdt0);
0033     if (test_usdt3_semaphore)
0034         STAP_PROBE3(test, usdt3, x, y, &bla);
0035     if (test_usdt12_semaphore) {
0036         STAP_PROBE12(test, usdt12,
0037                  x, x + 1, y, x + y, 5,
0038                  y / 7, bla, &bla, -9, nums[x],
0039                  nums[idx], t1.y);
0040     }
0041 }
0042 
0043 static void subtest_basic_usdt(void)
0044 {
0045     LIBBPF_OPTS(bpf_usdt_opts, opts);
0046     struct test_usdt *skel;
0047     struct test_usdt__bss *bss;
0048     int err;
0049 
0050     skel = test_usdt__open_and_load();
0051     if (!ASSERT_OK_PTR(skel, "skel_open"))
0052         return;
0053 
0054     bss = skel->bss;
0055     bss->my_pid = getpid();
0056 
0057     err = test_usdt__attach(skel);
0058     if (!ASSERT_OK(err, "skel_attach"))
0059         goto cleanup;
0060 
0061     /* usdt0 won't be auto-attached */
0062     opts.usdt_cookie = 0xcafedeadbeeffeed;
0063     skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0,
0064                              0 /*self*/, "/proc/self/exe",
0065                              "test", "usdt0", &opts);
0066     if (!ASSERT_OK_PTR(skel->links.usdt0, "usdt0_link"))
0067         goto cleanup;
0068 
0069     trigger_func(1);
0070 
0071     ASSERT_EQ(bss->usdt0_called, 1, "usdt0_called");
0072     ASSERT_EQ(bss->usdt3_called, 1, "usdt3_called");
0073     ASSERT_EQ(bss->usdt12_called, 1, "usdt12_called");
0074 
0075     ASSERT_EQ(bss->usdt0_cookie, 0xcafedeadbeeffeed, "usdt0_cookie");
0076     ASSERT_EQ(bss->usdt0_arg_cnt, 0, "usdt0_arg_cnt");
0077     ASSERT_EQ(bss->usdt0_arg_ret, -ENOENT, "usdt0_arg_ret");
0078 
0079     /* auto-attached usdt3 gets default zero cookie value */
0080     ASSERT_EQ(bss->usdt3_cookie, 0, "usdt3_cookie");
0081     ASSERT_EQ(bss->usdt3_arg_cnt, 3, "usdt3_arg_cnt");
0082 
0083     ASSERT_EQ(bss->usdt3_arg_rets[0], 0, "usdt3_arg1_ret");
0084     ASSERT_EQ(bss->usdt3_arg_rets[1], 0, "usdt3_arg2_ret");
0085     ASSERT_EQ(bss->usdt3_arg_rets[2], 0, "usdt3_arg3_ret");
0086     ASSERT_EQ(bss->usdt3_args[0], 1, "usdt3_arg1");
0087     ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
0088     ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
0089 
0090     /* auto-attached usdt12 gets default zero cookie value */
0091     ASSERT_EQ(bss->usdt12_cookie, 0, "usdt12_cookie");
0092     ASSERT_EQ(bss->usdt12_arg_cnt, 12, "usdt12_arg_cnt");
0093 
0094     ASSERT_EQ(bss->usdt12_args[0], 1, "usdt12_arg1");
0095     ASSERT_EQ(bss->usdt12_args[1], 1 + 1, "usdt12_arg2");
0096     ASSERT_EQ(bss->usdt12_args[2], 42, "usdt12_arg3");
0097     ASSERT_EQ(bss->usdt12_args[3], 42 + 1, "usdt12_arg4");
0098     ASSERT_EQ(bss->usdt12_args[4], 5, "usdt12_arg5");
0099     ASSERT_EQ(bss->usdt12_args[5], 42 / 7, "usdt12_arg6");
0100     ASSERT_EQ(bss->usdt12_args[6], bla, "usdt12_arg7");
0101     ASSERT_EQ(bss->usdt12_args[7], (uintptr_t)&bla, "usdt12_arg8");
0102     ASSERT_EQ(bss->usdt12_args[8], -9, "usdt12_arg9");
0103     ASSERT_EQ(bss->usdt12_args[9], nums[1], "usdt12_arg10");
0104     ASSERT_EQ(bss->usdt12_args[10], nums[idx], "usdt12_arg11");
0105     ASSERT_EQ(bss->usdt12_args[11], t1.y, "usdt12_arg12");
0106 
0107     /* trigger_func() is marked __always_inline, so USDT invocations will be
0108      * inlined in two different places, meaning that each USDT will have
0109      * at least 2 different places to be attached to. This verifies that
0110      * bpf_program__attach_usdt() handles this properly and attaches to
0111      * all possible places of USDT invocation.
0112      */
0113     trigger_func(2);
0114 
0115     ASSERT_EQ(bss->usdt0_called, 2, "usdt0_called");
0116     ASSERT_EQ(bss->usdt3_called, 2, "usdt3_called");
0117     ASSERT_EQ(bss->usdt12_called, 2, "usdt12_called");
0118 
0119     /* only check values that depend on trigger_func()'s input value */
0120     ASSERT_EQ(bss->usdt3_args[0], 2, "usdt3_arg1");
0121 
0122     ASSERT_EQ(bss->usdt12_args[0], 2, "usdt12_arg1");
0123     ASSERT_EQ(bss->usdt12_args[1], 2 + 1, "usdt12_arg2");
0124     ASSERT_EQ(bss->usdt12_args[3], 42 + 2, "usdt12_arg4");
0125     ASSERT_EQ(bss->usdt12_args[9], nums[2], "usdt12_arg10");
0126 
0127     /* detach and re-attach usdt3 */
0128     bpf_link__destroy(skel->links.usdt3);
0129 
0130     opts.usdt_cookie = 0xBADC00C51E;
0131     skel->links.usdt3 = bpf_program__attach_usdt(skel->progs.usdt3, -1 /* any pid */,
0132                              "/proc/self/exe", "test", "usdt3", &opts);
0133     if (!ASSERT_OK_PTR(skel->links.usdt3, "usdt3_reattach"))
0134         goto cleanup;
0135 
0136     trigger_func(3);
0137 
0138     ASSERT_EQ(bss->usdt3_called, 3, "usdt3_called");
0139     /* this time usdt3 has custom cookie */
0140     ASSERT_EQ(bss->usdt3_cookie, 0xBADC00C51E, "usdt3_cookie");
0141     ASSERT_EQ(bss->usdt3_arg_cnt, 3, "usdt3_arg_cnt");
0142 
0143     ASSERT_EQ(bss->usdt3_arg_rets[0], 0, "usdt3_arg1_ret");
0144     ASSERT_EQ(bss->usdt3_arg_rets[1], 0, "usdt3_arg2_ret");
0145     ASSERT_EQ(bss->usdt3_arg_rets[2], 0, "usdt3_arg3_ret");
0146     ASSERT_EQ(bss->usdt3_args[0], 3, "usdt3_arg1");
0147     ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2");
0148     ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3");
0149 
0150 cleanup:
0151     test_usdt__destroy(skel);
0152 }
0153 
0154 unsigned short test_usdt_100_semaphore SEC(".probes");
0155 unsigned short test_usdt_300_semaphore SEC(".probes");
0156 unsigned short test_usdt_400_semaphore SEC(".probes");
0157 
0158 #define R10(F, X)  F(X+0); F(X+1);F(X+2); F(X+3); F(X+4); \
0159            F(X+5); F(X+6); F(X+7); F(X+8); F(X+9);
0160 #define R100(F, X) R10(F,X+ 0);R10(F,X+10);R10(F,X+20);R10(F,X+30);R10(F,X+40); \
0161            R10(F,X+50);R10(F,X+60);R10(F,X+70);R10(F,X+80);R10(F,X+90);
0162 
0163 /* carefully control that we get exactly 100 inlines by preventing inlining */
0164 static void __always_inline f100(int x)
0165 {
0166     STAP_PROBE1(test, usdt_100, x);
0167 }
0168 
0169 __weak void trigger_100_usdts(void)
0170 {
0171     R100(f100, 0);
0172 }
0173 
0174 /* we shouldn't be able to attach to test:usdt2_300 USDT as we don't have as
0175  * many slots for specs. It's important that each STAP_PROBE2() invocation
0176  * (after untolling) gets different arg spec due to compiler inlining i as
0177  * a constant
0178  */
0179 static void __always_inline f300(int x)
0180 {
0181     STAP_PROBE1(test, usdt_300, x);
0182 }
0183 
0184 __weak void trigger_300_usdts(void)
0185 {
0186     R100(f300, 0);
0187     R100(f300, 100);
0188     R100(f300, 200);
0189 }
0190 
0191 static void __always_inline f400(int x __attribute__((unused)))
0192 {
0193     STAP_PROBE1(test, usdt_400, 400);
0194 }
0195 
0196 /* this time we have 400 different USDT call sites, but they have uniform
0197  * argument location, so libbpf's spec string deduplication logic should keep
0198  * spec count use very small and so we should be able to attach to all 400
0199  * call sites
0200  */
0201 __weak void trigger_400_usdts(void)
0202 {
0203     R100(f400, 0);
0204     R100(f400, 100);
0205     R100(f400, 200);
0206     R100(f400, 300);
0207 }
0208 
0209 static void subtest_multispec_usdt(void)
0210 {
0211     LIBBPF_OPTS(bpf_usdt_opts, opts);
0212     struct test_usdt *skel;
0213     struct test_usdt__bss *bss;
0214     int err, i;
0215 
0216     skel = test_usdt__open_and_load();
0217     if (!ASSERT_OK_PTR(skel, "skel_open"))
0218         return;
0219 
0220     bss = skel->bss;
0221     bss->my_pid = getpid();
0222 
0223     err = test_usdt__attach(skel);
0224     if (!ASSERT_OK(err, "skel_attach"))
0225         goto cleanup;
0226 
0227     /* usdt_100 is auto-attached and there are 100 inlined call sites,
0228      * let's validate that all of them are properly attached to and
0229      * handled from BPF side
0230      */
0231     trigger_100_usdts();
0232 
0233     ASSERT_EQ(bss->usdt_100_called, 100, "usdt_100_called");
0234     ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
0235 
0236     /* Stress test free spec ID tracking. By default libbpf allows up to
0237      * 256 specs to be used, so if we don't return free spec IDs back
0238      * after few detachments and re-attachments we should run out of
0239      * available spec IDs.
0240      */
0241     for (i = 0; i < 2; i++) {
0242         bpf_link__destroy(skel->links.usdt_100);
0243 
0244         skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
0245                                     "/proc/self/exe",
0246                                 "test", "usdt_100", NULL);
0247         if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_100_reattach"))
0248             goto cleanup;
0249 
0250         bss->usdt_100_sum = 0;
0251         trigger_100_usdts();
0252 
0253         ASSERT_EQ(bss->usdt_100_called, (i + 1) * 100 + 100, "usdt_100_called");
0254         ASSERT_EQ(bss->usdt_100_sum, 99 * 100 / 2, "usdt_100_sum");
0255     }
0256 
0257     /* Now let's step it up and try to attach USDT that requires more than
0258      * 256 attach points with different specs for each.
0259      * Note that we need trigger_300_usdts() only to actually have 300
0260      * USDT call sites, we are not going to actually trace them.
0261      */
0262     trigger_300_usdts();
0263 
0264     /* we'll reuse usdt_100 BPF program for usdt_300 test */
0265     bpf_link__destroy(skel->links.usdt_100);
0266     skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1, "/proc/self/exe",
0267                             "test", "usdt_300", NULL);
0268     err = -errno;
0269     if (!ASSERT_ERR_PTR(skel->links.usdt_100, "usdt_300_bad_attach"))
0270         goto cleanup;
0271     ASSERT_EQ(err, -E2BIG, "usdt_300_attach_err");
0272 
0273     /* let's check that there are no "dangling" BPF programs attached due
0274      * to partial success of the above test:usdt_300 attachment
0275      */
0276     bss->usdt_100_called = 0;
0277     bss->usdt_100_sum = 0;
0278 
0279     f300(777); /* this is 301st instance of usdt_300 */
0280 
0281     ASSERT_EQ(bss->usdt_100_called, 0, "usdt_301_called");
0282     ASSERT_EQ(bss->usdt_100_sum, 0, "usdt_301_sum");
0283 
0284     /* This time we have USDT with 400 inlined invocations, but arg specs
0285      * should be the same across all sites, so libbpf will only need to
0286      * use one spec and thus we'll be able to attach 400 uprobes
0287      * successfully.
0288      *
0289      * Again, we are reusing usdt_100 BPF program.
0290      */
0291     skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1,
0292                             "/proc/self/exe",
0293                             "test", "usdt_400", NULL);
0294     if (!ASSERT_OK_PTR(skel->links.usdt_100, "usdt_400_attach"))
0295         goto cleanup;
0296 
0297     trigger_400_usdts();
0298 
0299     ASSERT_EQ(bss->usdt_100_called, 400, "usdt_400_called");
0300     ASSERT_EQ(bss->usdt_100_sum, 400 * 400, "usdt_400_sum");
0301 
0302 cleanup:
0303     test_usdt__destroy(skel);
0304 }
0305 
0306 static FILE *urand_spawn(int *pid)
0307 {
0308     FILE *f;
0309 
0310     /* urandom_read's stdout is wired into f */
0311     f = popen("./urandom_read 1 report-pid", "r");
0312     if (!f)
0313         return NULL;
0314 
0315     if (fscanf(f, "%d", pid) != 1) {
0316         pclose(f);
0317         return NULL;
0318     }
0319 
0320     return f;
0321 }
0322 
0323 static int urand_trigger(FILE **urand_pipe)
0324 {
0325     int exit_code;
0326 
0327     /* pclose() waits for child process to exit and returns their exit code */
0328     exit_code = pclose(*urand_pipe);
0329     *urand_pipe = NULL;
0330 
0331     return exit_code;
0332 }
0333 
0334 static void subtest_urandom_usdt(bool auto_attach)
0335 {
0336     struct test_urandom_usdt *skel;
0337     struct test_urandom_usdt__bss *bss;
0338     struct bpf_link *l;
0339     FILE *urand_pipe = NULL;
0340     int err, urand_pid = 0;
0341 
0342     skel = test_urandom_usdt__open_and_load();
0343     if (!ASSERT_OK_PTR(skel, "skel_open"))
0344         return;
0345 
0346     urand_pipe = urand_spawn(&urand_pid);
0347     if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
0348         goto cleanup;
0349 
0350     bss = skel->bss;
0351     bss->urand_pid = urand_pid;
0352 
0353     if (auto_attach) {
0354         err = test_urandom_usdt__attach(skel);
0355         if (!ASSERT_OK(err, "skel_auto_attach"))
0356             goto cleanup;
0357     } else {
0358         l = bpf_program__attach_usdt(skel->progs.urand_read_without_sema,
0359                          urand_pid, "./urandom_read",
0360                          "urand", "read_without_sema", NULL);
0361         if (!ASSERT_OK_PTR(l, "urand_without_sema_attach"))
0362             goto cleanup;
0363         skel->links.urand_read_without_sema = l;
0364 
0365         l = bpf_program__attach_usdt(skel->progs.urand_read_with_sema,
0366                          urand_pid, "./urandom_read",
0367                          "urand", "read_with_sema", NULL);
0368         if (!ASSERT_OK_PTR(l, "urand_with_sema_attach"))
0369             goto cleanup;
0370         skel->links.urand_read_with_sema = l;
0371 
0372         l = bpf_program__attach_usdt(skel->progs.urandlib_read_without_sema,
0373                          urand_pid, "./liburandom_read.so",
0374                          "urandlib", "read_without_sema", NULL);
0375         if (!ASSERT_OK_PTR(l, "urandlib_without_sema_attach"))
0376             goto cleanup;
0377         skel->links.urandlib_read_without_sema = l;
0378 
0379         l = bpf_program__attach_usdt(skel->progs.urandlib_read_with_sema,
0380                          urand_pid, "./liburandom_read.so",
0381                          "urandlib", "read_with_sema", NULL);
0382         if (!ASSERT_OK_PTR(l, "urandlib_with_sema_attach"))
0383             goto cleanup;
0384         skel->links.urandlib_read_with_sema = l;
0385 
0386     }
0387 
0388     /* trigger urandom_read USDTs */
0389     ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");
0390 
0391     ASSERT_EQ(bss->urand_read_without_sema_call_cnt, 1, "urand_wo_sema_cnt");
0392     ASSERT_EQ(bss->urand_read_without_sema_buf_sz_sum, 256, "urand_wo_sema_sum");
0393 
0394     ASSERT_EQ(bss->urand_read_with_sema_call_cnt, 1, "urand_w_sema_cnt");
0395     ASSERT_EQ(bss->urand_read_with_sema_buf_sz_sum, 256, "urand_w_sema_sum");
0396 
0397     ASSERT_EQ(bss->urandlib_read_without_sema_call_cnt, 1, "urandlib_wo_sema_cnt");
0398     ASSERT_EQ(bss->urandlib_read_without_sema_buf_sz_sum, 256, "urandlib_wo_sema_sum");
0399 
0400     ASSERT_EQ(bss->urandlib_read_with_sema_call_cnt, 1, "urandlib_w_sema_cnt");
0401     ASSERT_EQ(bss->urandlib_read_with_sema_buf_sz_sum, 256, "urandlib_w_sema_sum");
0402 
0403 cleanup:
0404     if (urand_pipe)
0405         pclose(urand_pipe);
0406     test_urandom_usdt__destroy(skel);
0407 }
0408 
0409 void test_usdt(void)
0410 {
0411     if (test__start_subtest("basic"))
0412         subtest_basic_usdt();
0413     if (test__start_subtest("multispec"))
0414         subtest_multispec_usdt();
0415     if (test__start_subtest("urand_auto_attach"))
0416         subtest_urandom_usdt(true /* auto_attach */);
0417     if (test__start_subtest("urand_pid_attach"))
0418         subtest_urandom_usdt(false /* auto_attach */);
0419 }