Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Enrich subtest_basic_usdt case in selftests to cover SIB handling logic

When using GCC on x86-64 to compile an usdt prog with -O1 or higher
optimization, the compiler will generate SIB addressing mode for global
array, e.g. "1@-96(%rbp,%rax,8)".

In this patch:
- enrich subtest_basic_usdt test case to cover SIB addressing usdt argument spec
handling logic

Signed-off-by: Jiawei Zhao <phoenix500526@163.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250827053128.1301287-3-phoenix500526@163.com

authored by

Jiawei Zhao and committed by
Andrii Nakryiko
69424097 758acb9c

+112 -2
+81 -2
tools/testing/selftests/bpf/prog_tests/usdt.c
··· 40 40 } 41 41 } 42 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 + 43 102 static void subtest_basic_usdt(void) 44 103 { 45 104 LIBBPF_OPTS(bpf_usdt_opts, opts); 46 105 struct test_usdt *skel; 47 106 struct test_usdt__bss *bss; 48 107 int err, i; 108 + const __u64 expected_cookie = 0xcafedeadbeeffeed; 49 109 50 110 skel = test_usdt__open_and_load(); 51 111 if (!ASSERT_OK_PTR(skel, "skel_open")) ··· 119 59 goto cleanup; 120 60 121 61 /* usdt0 won't be auto-attached */ 122 - opts.usdt_cookie = 0xcafedeadbeeffeed; 62 + opts.usdt_cookie = expected_cookie; 123 63 skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, 124 64 0 /*self*/, "/proc/self/exe", 125 65 "test", "usdt0", &opts); 126 66 if (!ASSERT_OK_PTR(skel->links.usdt0, "usdt0_link")) 127 67 goto cleanup; 68 + 69 + #if defined(__x86_64__) || defined(__i386__) 70 + opts.usdt_cookie = expected_cookie; 71 + skel->links.usdt_sib = bpf_program__attach_usdt(skel->progs.usdt_sib, 72 + 0 /*self*/, "/proc/self/exe", 73 + "test", "usdt_sib", &opts); 74 + if (!ASSERT_OK_PTR(skel->links.usdt_sib, "usdt_sib_link")) 75 + goto cleanup; 76 + #endif 128 77 129 78 trigger_func(1); 130 79 ··· 141 72 ASSERT_EQ(bss->usdt3_called, 1, "usdt3_called"); 142 73 ASSERT_EQ(bss->usdt12_called, 1, "usdt12_called"); 143 74 144 - ASSERT_EQ(bss->usdt0_cookie, 0xcafedeadbeeffeed, "usdt0_cookie"); 75 + ASSERT_EQ(bss->usdt0_cookie, expected_cookie, "usdt0_cookie"); 145 76 ASSERT_EQ(bss->usdt0_arg_cnt, 0, "usdt0_arg_cnt"); 146 77 ASSERT_EQ(bss->usdt0_arg_ret, -ENOENT, "usdt0_arg_ret"); 147 78 ASSERT_EQ(bss->usdt0_arg_size, -ENOENT, "usdt0_arg_size"); ··· 224 155 ASSERT_EQ(bss->usdt3_args[0], 3, "usdt3_arg1"); 225 156 ASSERT_EQ(bss->usdt3_args[1], 42, "usdt3_arg2"); 226 157 ASSERT_EQ(bss->usdt3_args[2], (uintptr_t)&bla, "usdt3_arg3"); 158 + 159 + #if defined(__x86_64__) || defined(__i386__) 160 + trigger_sib_spec(); 161 + ASSERT_EQ(bss->usdt_sib_called, 1, "usdt_sib_called"); 162 + ASSERT_EQ(bss->usdt_sib_cookie, expected_cookie, "usdt_sib_cookie"); 163 + ASSERT_EQ(bss->usdt_sib_arg_cnt, 1, "usdt_sib_arg_cnt"); 164 + ASSERT_EQ(bss->usdt_sib_arg, nums[0], "usdt_sib_arg"); 165 + ASSERT_EQ(bss->usdt_sib_arg_ret, 0, "usdt_sib_arg_ret"); 166 + ASSERT_EQ(bss->usdt_sib_arg_size, sizeof(nums[0]), "usdt_sib_arg_size"); 167 + #endif 227 168 228 169 cleanup: 229 170 test_usdt__destroy(skel);
+31
tools/testing/selftests/bpf/progs/test_usdt.c
··· 107 107 return 0; 108 108 } 109 109 110 + int usdt_sib_called; 111 + u64 usdt_sib_cookie; 112 + int usdt_sib_arg_cnt; 113 + int usdt_sib_arg_ret; 114 + short usdt_sib_arg; 115 + int usdt_sib_arg_size; 116 + 117 + /* 118 + * usdt_sib is only tested on x86-related architectures, so it requires 119 + * manual attach since auto-attach will panic tests under other architectures 120 + */ 121 + SEC("usdt") 122 + int usdt_sib(struct pt_regs *ctx) 123 + { 124 + long tmp; 125 + 126 + if (my_pid != (bpf_get_current_pid_tgid() >> 32)) 127 + return 0; 128 + 129 + __sync_fetch_and_add(&usdt_sib_called, 1); 130 + 131 + usdt_sib_cookie = bpf_usdt_cookie(ctx); 132 + usdt_sib_arg_cnt = bpf_usdt_arg_cnt(ctx); 133 + 134 + usdt_sib_arg_ret = bpf_usdt_arg(ctx, 0, &tmp); 135 + usdt_sib_arg = (short)tmp; 136 + usdt_sib_arg_size = bpf_usdt_arg_size(ctx, 0); 137 + 138 + return 0; 139 + } 140 + 110 141 char _license[] SEC("license") = "GPL";