1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <unistd.h> 4 #include <test_progs.h> 5 #include "uprobe_multi.skel.h" 6 #include "uprobe_multi_bench.skel.h" 7 #include "uprobe_multi_usdt.skel.h" 8 #include "bpf/libbpf_internal.h" 9 #include "testing_helpers.h" 10 11 static char test_data[] = "test_data"; 12 13 noinline void uprobe_multi_func_1(void) 14 { 15 asm volatile (""); 16 } 17 18 noinline void uprobe_multi_func_2(void) 19 { 20 asm volatile (""); 21 } 22 23 noinline void uprobe_multi_func_3(void) 24 { 25 asm volatile (""); 26 } 27 28 struct child { 29 int go[2]; 30 int pid; 31 }; 32 33 static void release_child(struct child *child) 34 { 35 int child_status; 36 37 if (!child) 38 return; 39 close(child->go[1]); 40 close(child->go[0]); 41 if (child->pid > 0) 42 waitpid(child->pid, &child_status, 0); 43 } 44 45 static void kick_child(struct child *child) 46 { 47 char c = 1; 48 49 if (child) { 50 write(child->go[1], &c, 1); 51 release_child(child); 52 } 53 fflush(NULL); 54 } 55 56 static struct child *spawn_child(void) 57 { 58 static struct child child; 59 int err; 60 int c; 61 62 /* pipe to notify child to execute the trigger functions */ 63 if (pipe(child.go)) 64 return NULL; 65 66 child.pid = fork(); 67 if (child.pid < 0) { 68 release_child(&child); 69 errno = EINVAL; 70 return NULL; 71 } 72 73 /* child */ 74 if (child.pid == 0) { 75 close(child.go[1]); 76 77 /* wait for parent's kick */ 78 err = read(child.go[0], &c, 1); 79 if (err != 1) 80 exit(err); 81 82 uprobe_multi_func_1(); 83 uprobe_multi_func_2(); 84 uprobe_multi_func_3(); 85 86 exit(errno); 87 } 88 89 return &child; 90 } 91 92 static void uprobe_multi_test_run(struct uprobe_multi *skel, struct child *child) 93 { 94 skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1; 95 skel->bss->uprobe_multi_func_2_addr = (__u64) uprobe_multi_func_2; 96 skel->bss->uprobe_multi_func_3_addr = (__u64) uprobe_multi_func_3; 97 98 skel->bss->user_ptr = test_data; 99 100 /* 101 * Disable pid check in bpf program if we are pid filter test, 102 * because the probe should be executed only by child->pid 103 * passed at the probe attach. 104 */ 105 skel->bss->pid = child ? 0 : getpid(); 106 107 if (child) 108 kick_child(child); 109 110 /* trigger all probes */ 111 uprobe_multi_func_1(); 112 uprobe_multi_func_2(); 113 uprobe_multi_func_3(); 114 115 /* 116 * There are 2 entry and 2 exit probe called for each uprobe_multi_func_[123] 117 * function and each slepable probe (6) increments uprobe_multi_sleep_result. 118 */ 119 ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 2, "uprobe_multi_func_1_result"); 120 ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 2, "uprobe_multi_func_2_result"); 121 ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 2, "uprobe_multi_func_3_result"); 122 123 ASSERT_EQ(skel->bss->uretprobe_multi_func_1_result, 2, "uretprobe_multi_func_1_result"); 124 ASSERT_EQ(skel->bss->uretprobe_multi_func_2_result, 2, "uretprobe_multi_func_2_result"); 125 ASSERT_EQ(skel->bss->uretprobe_multi_func_3_result, 2, "uretprobe_multi_func_3_result"); 126 127 ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 6, "uprobe_multi_sleep_result"); 128 129 if (child) 130 ASSERT_EQ(skel->bss->child_pid, child->pid, "uprobe_multi_child_pid"); 131 } 132 133 static void test_skel_api(void) 134 { 135 struct uprobe_multi *skel = NULL; 136 int err; 137 138 skel = uprobe_multi__open_and_load(); 139 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 140 goto cleanup; 141 142 err = uprobe_multi__attach(skel); 143 if (!ASSERT_OK(err, "uprobe_multi__attach")) 144 goto cleanup; 145 146 uprobe_multi_test_run(skel, NULL); 147 148 cleanup: 149 uprobe_multi__destroy(skel); 150 } 151 152 static void 153 __test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts, 154 struct child *child) 155 { 156 pid_t pid = child ? child->pid : -1; 157 struct uprobe_multi *skel = NULL; 158 159 skel = uprobe_multi__open_and_load(); 160 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 161 goto cleanup; 162 163 opts->retprobe = false; 164 skel->links.uprobe = bpf_program__attach_uprobe_multi(skel->progs.uprobe, pid, 165 binary, pattern, opts); 166 if (!ASSERT_OK_PTR(skel->links.uprobe, "bpf_program__attach_uprobe_multi")) 167 goto cleanup; 168 169 opts->retprobe = true; 170 skel->links.uretprobe = bpf_program__attach_uprobe_multi(skel->progs.uretprobe, pid, 171 binary, pattern, opts); 172 if (!ASSERT_OK_PTR(skel->links.uretprobe, "bpf_program__attach_uprobe_multi")) 173 goto cleanup; 174 175 opts->retprobe = false; 176 skel->links.uprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uprobe_sleep, pid, 177 binary, pattern, opts); 178 if (!ASSERT_OK_PTR(skel->links.uprobe_sleep, "bpf_program__attach_uprobe_multi")) 179 goto cleanup; 180 181 opts->retprobe = true; 182 skel->links.uretprobe_sleep = bpf_program__attach_uprobe_multi(skel->progs.uretprobe_sleep, 183 pid, binary, pattern, opts); 184 if (!ASSERT_OK_PTR(skel->links.uretprobe_sleep, "bpf_program__attach_uprobe_multi")) 185 goto cleanup; 186 187 opts->retprobe = false; 188 skel->links.uprobe_extra = bpf_program__attach_uprobe_multi(skel->progs.uprobe_extra, -1, 189 binary, pattern, opts); 190 if (!ASSERT_OK_PTR(skel->links.uprobe_extra, "bpf_program__attach_uprobe_multi")) 191 goto cleanup; 192 193 uprobe_multi_test_run(skel, child); 194 195 cleanup: 196 uprobe_multi__destroy(skel); 197 } 198 199 static void 200 test_attach_api(const char *binary, const char *pattern, struct bpf_uprobe_multi_opts *opts) 201 { 202 struct child *child; 203 204 /* no pid filter */ 205 __test_attach_api(binary, pattern, opts, NULL); 206 207 /* pid filter */ 208 child = spawn_child(); 209 if (!ASSERT_OK_PTR(child, "spawn_child")) 210 return; 211 212 __test_attach_api(binary, pattern, opts, child); 213 } 214 215 static void test_attach_api_pattern(void) 216 { 217 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); 218 219 test_attach_api("/proc/self/exe", "uprobe_multi_func_*", &opts); 220 test_attach_api("/proc/self/exe", "uprobe_multi_func_?", &opts); 221 } 222 223 static void test_attach_api_syms(void) 224 { 225 LIBBPF_OPTS(bpf_uprobe_multi_opts, opts); 226 const char *syms[3] = { 227 "uprobe_multi_func_1", 228 "uprobe_multi_func_2", 229 "uprobe_multi_func_3", 230 }; 231 232 opts.syms = syms; 233 opts.cnt = ARRAY_SIZE(syms); 234 test_attach_api("/proc/self/exe", NULL, &opts); 235 } 236 237 static void __test_link_api(struct child *child) 238 { 239 int prog_fd, link1_fd = -1, link2_fd = -1, link3_fd = -1, link4_fd = -1; 240 LIBBPF_OPTS(bpf_link_create_opts, opts); 241 const char *path = "/proc/self/exe"; 242 struct uprobe_multi *skel = NULL; 243 unsigned long *offsets = NULL; 244 const char *syms[3] = { 245 "uprobe_multi_func_1", 246 "uprobe_multi_func_2", 247 "uprobe_multi_func_3", 248 }; 249 int link_extra_fd = -1; 250 int err; 251 252 err = elf_resolve_syms_offsets(path, 3, syms, (unsigned long **) &offsets); 253 if (!ASSERT_OK(err, "elf_resolve_syms_offsets")) 254 return; 255 256 opts.uprobe_multi.path = path; 257 opts.uprobe_multi.offsets = offsets; 258 opts.uprobe_multi.cnt = ARRAY_SIZE(syms); 259 opts.uprobe_multi.pid = child ? child->pid : 0; 260 261 skel = uprobe_multi__open_and_load(); 262 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load")) 263 goto cleanup; 264 265 opts.kprobe_multi.flags = 0; 266 prog_fd = bpf_program__fd(skel->progs.uprobe); 267 link1_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 268 if (!ASSERT_GE(link1_fd, 0, "link1_fd")) 269 goto cleanup; 270 271 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN; 272 prog_fd = bpf_program__fd(skel->progs.uretprobe); 273 link2_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 274 if (!ASSERT_GE(link2_fd, 0, "link2_fd")) 275 goto cleanup; 276 277 opts.kprobe_multi.flags = 0; 278 prog_fd = bpf_program__fd(skel->progs.uprobe_sleep); 279 link3_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 280 if (!ASSERT_GE(link3_fd, 0, "link3_fd")) 281 goto cleanup; 282 283 opts.kprobe_multi.flags = BPF_F_UPROBE_MULTI_RETURN; 284 prog_fd = bpf_program__fd(skel->progs.uretprobe_sleep); 285 link4_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 286 if (!ASSERT_GE(link4_fd, 0, "link4_fd")) 287 goto cleanup; 288 289 opts.kprobe_multi.flags = 0; 290 opts.uprobe_multi.pid = 0; 291 prog_fd = bpf_program__fd(skel->progs.uprobe_extra); 292 link_extra_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts); 293 if (!ASSERT_GE(link_extra_fd, 0, "link_extra_fd")) 294 goto cleanup; 295 296 uprobe_multi_test_run(skel, child); 297 298 cleanup: 299 if (link1_fd >= 0) 300 close(link1_fd); 301 if (link2_fd >= 0) 302 close(link2_fd); 303 if (link3_fd >= 0) 304 close(link3_fd); 305 if (link4_fd >= 0) 306 close(link4_fd); 307 if (link_extra_fd >= 0) 308 close(link_extra_fd); 309 310 uprobe_multi__destroy(skel); 311 free(offsets); 312 } 313 314 void test_link_api(void) 315 { 316 struct child *child; 317 318 /* no pid filter */ 319 __test_link_api(NULL); 320 321 /* pid filter */ 322 child = spawn_child(); 323 if (!ASSERT_OK_PTR(child, "spawn_child")) 324 return; 325 326 __test_link_api(child); 327 } 328 329 static void test_bench_attach_uprobe(void) 330 { 331 long attach_start_ns = 0, attach_end_ns = 0; 332 struct uprobe_multi_bench *skel = NULL; 333 long detach_start_ns, detach_end_ns; 334 double attach_delta, detach_delta; 335 int err; 336 337 skel = uprobe_multi_bench__open_and_load(); 338 if (!ASSERT_OK_PTR(skel, "uprobe_multi_bench__open_and_load")) 339 goto cleanup; 340 341 attach_start_ns = get_time_ns(); 342 343 err = uprobe_multi_bench__attach(skel); 344 if (!ASSERT_OK(err, "uprobe_multi_bench__attach")) 345 goto cleanup; 346 347 attach_end_ns = get_time_ns(); 348 349 system("./uprobe_multi bench"); 350 351 ASSERT_EQ(skel->bss->count, 50000, "uprobes_count"); 352 353 cleanup: 354 detach_start_ns = get_time_ns(); 355 uprobe_multi_bench__destroy(skel); 356 detach_end_ns = get_time_ns(); 357 358 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0; 359 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0; 360 361 printf("%s: attached in %7.3lfs\n", __func__, attach_delta); 362 printf("%s: detached in %7.3lfs\n", __func__, detach_delta); 363 } 364 365 static void test_bench_attach_usdt(void) 366 { 367 long attach_start_ns = 0, attach_end_ns = 0; 368 struct uprobe_multi_usdt *skel = NULL; 369 long detach_start_ns, detach_end_ns; 370 double attach_delta, detach_delta; 371 372 skel = uprobe_multi_usdt__open_and_load(); 373 if (!ASSERT_OK_PTR(skel, "uprobe_multi__open")) 374 goto cleanup; 375 376 attach_start_ns = get_time_ns(); 377 378 skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0, -1, "./uprobe_multi", 379 "test", "usdt", NULL); 380 if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt")) 381 goto cleanup; 382 383 attach_end_ns = get_time_ns(); 384 385 system("./uprobe_multi usdt"); 386 387 ASSERT_EQ(skel->bss->count, 50000, "usdt_count"); 388 389 cleanup: 390 detach_start_ns = get_time_ns(); 391 uprobe_multi_usdt__destroy(skel); 392 detach_end_ns = get_time_ns(); 393 394 attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0; 395 detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0; 396 397 printf("%s: attached in %7.3lfs\n", __func__, attach_delta); 398 printf("%s: detached in %7.3lfs\n", __func__, detach_delta); 399 } 400 401 void test_uprobe_multi_test(void) 402 { 403 if (test__start_subtest("skel_api")) 404 test_skel_api(); 405 if (test__start_subtest("attach_api_pattern")) 406 test_attach_api_pattern(); 407 if (test__start_subtest("attach_api_syms")) 408 test_attach_api_syms(); 409 if (test__start_subtest("link_api")) 410 test_link_api(); 411 if (test__start_subtest("bench_uprobe")) 412 test_bench_attach_uprobe(); 413 if (test__start_subtest("bench_usdt")) 414 test_bench_attach_usdt(); 415 } 416