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

selftests/bpf: Add attach bench test

Adding test that reads all functions from ftrace available_filter_functions
file and attach them all through kprobe_multi API.

It also prints stats info with -v option, like on my setup:

test_bench_attach: found 48712 functions
test_bench_attach: attached in 1.069s
test_bench_attach: detached in 0.373s

Acked-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20220510122616.2652285-6-jolsa@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Jiri Olsa and committed by
Alexei Starovoitov
5b6c7e5c 0236fec5

+155
+143
tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
··· 2 2 #include <test_progs.h> 3 3 #include "kprobe_multi.skel.h" 4 4 #include "trace_helpers.h" 5 + #include "kprobe_multi_empty.skel.h" 6 + #include "bpf/libbpf_internal.h" 7 + #include "bpf/hashmap.h" 5 8 6 9 static void kprobe_multi_test_run(struct kprobe_multi *skel, bool test_return) 7 10 { ··· 304 301 kprobe_multi__destroy(skel); 305 302 } 306 303 304 + static inline __u64 get_time_ns(void) 305 + { 306 + struct timespec t; 307 + 308 + clock_gettime(CLOCK_MONOTONIC, &t); 309 + return (__u64) t.tv_sec * 1000000000 + t.tv_nsec; 310 + } 311 + 312 + static size_t symbol_hash(const void *key, void *ctx __maybe_unused) 313 + { 314 + return str_hash((const char *) key); 315 + } 316 + 317 + static bool symbol_equal(const void *key1, const void *key2, void *ctx __maybe_unused) 318 + { 319 + return strcmp((const char *) key1, (const char *) key2) == 0; 320 + } 321 + 322 + static int get_syms(char ***symsp, size_t *cntp) 323 + { 324 + size_t cap = 0, cnt = 0, i; 325 + char *name, **syms = NULL; 326 + struct hashmap *map; 327 + char buf[256]; 328 + FILE *f; 329 + int err; 330 + 331 + /* 332 + * The available_filter_functions contains many duplicates, 333 + * but other than that all symbols are usable in kprobe multi 334 + * interface. 335 + * Filtering out duplicates by using hashmap__add, which won't 336 + * add existing entry. 337 + */ 338 + f = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r"); 339 + if (!f) 340 + return -EINVAL; 341 + 342 + map = hashmap__new(symbol_hash, symbol_equal, NULL); 343 + if (IS_ERR(map)) 344 + goto error; 345 + 346 + while (fgets(buf, sizeof(buf), f)) { 347 + /* skip modules */ 348 + if (strchr(buf, '[')) 349 + continue; 350 + if (sscanf(buf, "%ms$*[^\n]\n", &name) != 1) 351 + continue; 352 + /* 353 + * We attach to almost all kernel functions and some of them 354 + * will cause 'suspicious RCU usage' when fprobe is attached 355 + * to them. Filter out the current culprits - arch_cpu_idle 356 + * and rcu_* functions. 357 + */ 358 + if (!strcmp(name, "arch_cpu_idle")) 359 + continue; 360 + if (!strncmp(name, "rcu_", 4)) 361 + continue; 362 + err = hashmap__add(map, name, NULL); 363 + if (err) { 364 + free(name); 365 + if (err == -EEXIST) 366 + continue; 367 + goto error; 368 + } 369 + err = libbpf_ensure_mem((void **) &syms, &cap, 370 + sizeof(*syms), cnt + 1); 371 + if (err) { 372 + free(name); 373 + goto error; 374 + } 375 + syms[cnt] = name; 376 + cnt++; 377 + } 378 + 379 + *symsp = syms; 380 + *cntp = cnt; 381 + 382 + error: 383 + fclose(f); 384 + hashmap__free(map); 385 + if (err) { 386 + for (i = 0; i < cnt; i++) 387 + free(syms[cnt]); 388 + free(syms); 389 + } 390 + return err; 391 + } 392 + 393 + static void test_bench_attach(void) 394 + { 395 + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); 396 + struct kprobe_multi_empty *skel = NULL; 397 + long attach_start_ns, attach_end_ns; 398 + long detach_start_ns, detach_end_ns; 399 + double attach_delta, detach_delta; 400 + struct bpf_link *link = NULL; 401 + char **syms = NULL; 402 + size_t cnt, i; 403 + 404 + if (!ASSERT_OK(get_syms(&syms, &cnt), "get_syms")) 405 + return; 406 + 407 + skel = kprobe_multi_empty__open_and_load(); 408 + if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load")) 409 + goto cleanup; 410 + 411 + opts.syms = (const char **) syms; 412 + opts.cnt = cnt; 413 + 414 + attach_start_ns = get_time_ns(); 415 + link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe_empty, 416 + NULL, &opts); 417 + attach_end_ns = get_time_ns(); 418 + 419 + if (!ASSERT_OK_PTR(link, "bpf_program__attach_kprobe_multi_opts")) 420 + goto cleanup; 421 + 422 + detach_start_ns = get_time_ns(); 423 + bpf_link__destroy(link); 424 + detach_end_ns = get_time_ns(); 425 + 426 + attach_delta = (attach_end_ns - attach_start_ns) / 1000000000.0; 427 + detach_delta = (detach_end_ns - detach_start_ns) / 1000000000.0; 428 + 429 + printf("%s: found %lu functions\n", __func__, cnt); 430 + printf("%s: attached in %7.3lfs\n", __func__, attach_delta); 431 + printf("%s: detached in %7.3lfs\n", __func__, detach_delta); 432 + 433 + cleanup: 434 + kprobe_multi_empty__destroy(skel); 435 + if (syms) { 436 + for (i = 0; i < cnt; i++) 437 + free(syms[i]); 438 + free(syms); 439 + } 440 + } 441 + 307 442 void test_kprobe_multi_test(void) 308 443 { 309 444 if (!ASSERT_OK(load_kallsyms(), "load_kallsyms")) ··· 461 320 test_attach_api_syms(); 462 321 if (test__start_subtest("attach_api_fails")) 463 322 test_attach_api_fails(); 323 + if (test__start_subtest("bench_attach")) 324 + test_bench_attach(); 464 325 }
+12
tools/testing/selftests/bpf/progs/kprobe_multi_empty.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <bpf/bpf_helpers.h> 4 + #include <bpf/bpf_tracing.h> 5 + 6 + char _license[] SEC("license") = "GPL"; 7 + 8 + SEC("kprobe.multi/") 9 + int test_kprobe_empty(struct pt_regs *ctx) 10 + { 11 + return 0; 12 + }