| // SPDX-License-Identifier: GPL-2.0 | |
| #include <test_progs.h> | |
| #include "stacktrace_ips.skel.h" | |
| #ifdef __x86_64__ | |
| static int check_stacktrace_ips(int fd, __u32 key, int cnt, ...) | |
| { | |
| __u64 ips[PERF_MAX_STACK_DEPTH]; | |
| struct ksyms *ksyms = NULL; | |
| int i, err = 0; | |
| va_list args; | |
| /* sorted by addr */ | |
| ksyms = load_kallsyms_local(); | |
| if (!ASSERT_OK_PTR(ksyms, "load_kallsyms_local")) | |
| return -1; | |
| /* unlikely, but... */ | |
| if (!ASSERT_LT(cnt, PERF_MAX_STACK_DEPTH, "check_max")) | |
| return -1; | |
| err = bpf_map_lookup_elem(fd, &key, ips); | |
| if (err) | |
| goto out; | |
| /* | |
| * Compare all symbols provided via arguments with stacktrace ips, | |
| * and their related symbol addresses.t | |
| */ | |
| va_start(args, cnt); | |
| for (i = 0; i < cnt; i++) { | |
| unsigned long val; | |
| struct ksym *ksym; | |
| val = va_arg(args, unsigned long); | |
| ksym = ksym_search_local(ksyms, ips[i]); | |
| if (!ASSERT_OK_PTR(ksym, "ksym_search_local")) | |
| break; | |
| ASSERT_EQ(ksym->addr, val, "stack_cmp"); | |
| } | |
| va_end(args); | |
| out: | |
| free_kallsyms_local(ksyms); | |
| return err; | |
| } | |
| static void test_stacktrace_ips_kprobe_multi(bool retprobe) | |
| { | |
| LIBBPF_OPTS(bpf_kprobe_multi_opts, opts, | |
| .retprobe = retprobe | |
| ); | |
| LIBBPF_OPTS(bpf_test_run_opts, topts); | |
| struct stacktrace_ips *skel; | |
| skel = stacktrace_ips__open_and_load(); | |
| if (!ASSERT_OK_PTR(skel, "stacktrace_ips__open_and_load")) | |
| return; | |
| if (!skel->kconfig->CONFIG_UNWINDER_ORC) { | |
| test__skip(); | |
| goto cleanup; | |
| } | |
| skel->links.kprobe_multi_test = bpf_program__attach_kprobe_multi_opts( | |
| skel->progs.kprobe_multi_test, | |
| "bpf_testmod_stacktrace_test", &opts); | |
| if (!ASSERT_OK_PTR(skel->links.kprobe_multi_test, "bpf_program__attach_kprobe_multi_opts")) | |
| goto cleanup; | |
| trigger_module_test_read(1); | |
| load_kallsyms(); | |
| if (retprobe) { | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 4, | |
| ksym_get_addr("bpf_testmod_stacktrace_test_3"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_2"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_1"), | |
| ksym_get_addr("bpf_testmod_test_read")); | |
| } else { | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 5, | |
| ksym_get_addr("bpf_testmod_stacktrace_test"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_3"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_2"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_1"), | |
| ksym_get_addr("bpf_testmod_test_read")); | |
| } | |
| cleanup: | |
| stacktrace_ips__destroy(skel); | |
| } | |
| static void test_stacktrace_ips_raw_tp(void) | |
| { | |
| __u32 info_len = sizeof(struct bpf_prog_info); | |
| LIBBPF_OPTS(bpf_test_run_opts, topts); | |
| struct bpf_prog_info info = {}; | |
| struct stacktrace_ips *skel; | |
| __u64 bpf_prog_ksym = 0; | |
| int err; | |
| skel = stacktrace_ips__open_and_load(); | |
| if (!ASSERT_OK_PTR(skel, "stacktrace_ips__open_and_load")) | |
| return; | |
| if (!skel->kconfig->CONFIG_UNWINDER_ORC) { | |
| test__skip(); | |
| goto cleanup; | |
| } | |
| skel->links.rawtp_test = bpf_program__attach_raw_tracepoint( | |
| skel->progs.rawtp_test, | |
| "bpf_testmod_test_read"); | |
| if (!ASSERT_OK_PTR(skel->links.rawtp_test, "bpf_program__attach_raw_tracepoint")) | |
| goto cleanup; | |
| /* get bpf program address */ | |
| info.jited_ksyms = ptr_to_u64(&bpf_prog_ksym); | |
| info.nr_jited_ksyms = 1; | |
| err = bpf_prog_get_info_by_fd(bpf_program__fd(skel->progs.rawtp_test), | |
| &info, &info_len); | |
| if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd")) | |
| goto cleanup; | |
| trigger_module_test_read(1); | |
| load_kallsyms(); | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 2, | |
| bpf_prog_ksym, | |
| ksym_get_addr("bpf_trace_run2")); | |
| cleanup: | |
| stacktrace_ips__destroy(skel); | |
| } | |
| static void test_stacktrace_ips_kprobe(bool retprobe) | |
| { | |
| LIBBPF_OPTS(bpf_kprobe_opts, opts, | |
| .retprobe = retprobe | |
| ); | |
| LIBBPF_OPTS(bpf_test_run_opts, topts); | |
| struct stacktrace_ips *skel; | |
| skel = stacktrace_ips__open_and_load(); | |
| if (!ASSERT_OK_PTR(skel, "stacktrace_ips__open_and_load")) | |
| return; | |
| if (!skel->kconfig->CONFIG_UNWINDER_ORC) { | |
| test__skip(); | |
| goto cleanup; | |
| } | |
| skel->links.kprobe_test = bpf_program__attach_kprobe_opts( | |
| skel->progs.kprobe_test, | |
| "bpf_testmod_stacktrace_test", &opts); | |
| if (!ASSERT_OK_PTR(skel->links.kprobe_test, "bpf_program__attach_kprobe_opts")) | |
| goto cleanup; | |
| trigger_module_test_read(1); | |
| load_kallsyms(); | |
| if (retprobe) { | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 4, | |
| ksym_get_addr("bpf_testmod_stacktrace_test_3"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_2"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_1"), | |
| ksym_get_addr("bpf_testmod_test_read")); | |
| } else { | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 5, | |
| ksym_get_addr("bpf_testmod_stacktrace_test"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_3"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_2"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_1"), | |
| ksym_get_addr("bpf_testmod_test_read")); | |
| } | |
| cleanup: | |
| stacktrace_ips__destroy(skel); | |
| } | |
| static void test_stacktrace_ips_trampoline(bool retprobe) | |
| { | |
| LIBBPF_OPTS(bpf_test_run_opts, topts); | |
| struct stacktrace_ips *skel; | |
| skel = stacktrace_ips__open_and_load(); | |
| if (!ASSERT_OK_PTR(skel, "stacktrace_ips__open_and_load")) | |
| return; | |
| if (!skel->kconfig->CONFIG_UNWINDER_ORC) { | |
| test__skip(); | |
| goto cleanup; | |
| } | |
| if (retprobe) { | |
| skel->links.fexit_test = bpf_program__attach_trace(skel->progs.fexit_test); | |
| if (!ASSERT_OK_PTR(skel->links.fexit_test, "bpf_program__attach_trace")) | |
| goto cleanup; | |
| } else { | |
| skel->links.fentry_test = bpf_program__attach_trace(skel->progs.fentry_test); | |
| if (!ASSERT_OK_PTR(skel->links.fentry_test, "bpf_program__attach_trace")) | |
| goto cleanup; | |
| } | |
| trigger_module_test_read(1); | |
| load_kallsyms(); | |
| if (retprobe) { | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 4, | |
| ksym_get_addr("bpf_testmod_stacktrace_test_3"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_2"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_1"), | |
| ksym_get_addr("bpf_testmod_test_read")); | |
| } else { | |
| check_stacktrace_ips(bpf_map__fd(skel->maps.stackmap), skel->bss->stack_key, 5, | |
| ksym_get_addr("bpf_testmod_stacktrace_test"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_3"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_2"), | |
| ksym_get_addr("bpf_testmod_stacktrace_test_1"), | |
| ksym_get_addr("bpf_testmod_test_read")); | |
| } | |
| cleanup: | |
| stacktrace_ips__destroy(skel); | |
| } | |
| static void __test_stacktrace_ips(void) | |
| { | |
| if (test__start_subtest("kprobe_multi")) | |
| test_stacktrace_ips_kprobe_multi(false); | |
| if (test__start_subtest("kretprobe_multi")) | |
| test_stacktrace_ips_kprobe_multi(true); | |
| if (test__start_subtest("raw_tp")) | |
| test_stacktrace_ips_raw_tp(); | |
| if (test__start_subtest("kprobe")) | |
| test_stacktrace_ips_kprobe(false); | |
| if (test__start_subtest("kretprobe")) | |
| test_stacktrace_ips_kprobe(true); | |
| if (test__start_subtest("fentry")) | |
| test_stacktrace_ips_trampoline(false); | |
| if (test__start_subtest("fexit")) | |
| test_stacktrace_ips_trampoline(true); | |
| } | |
| #else | |
| static void __test_stacktrace_ips(void) | |
| { | |
| test__skip(); | |
| } | |
| #endif | |
| void test_stacktrace_ips(void) | |
| { | |
| __test_stacktrace_ips(); | |
| } |