Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions arch/x86/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2789,13 +2789,13 @@ perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *re
return;
}

if (perf_callchain_store(entry, regs->ip))
return;

if (perf_hw_regs(regs))
if (perf_hw_regs(regs)) {
if (perf_callchain_store(entry, regs->ip))
return;
unwind_start(&state, current, regs, NULL);
else
} else {
unwind_start(&state, current, NULL, (void *)regs->sp);
}

for (; !unwind_done(&state); unwind_next_frame(&state)) {
addr = unwind_get_return_address(&state);
Expand Down
5 changes: 5 additions & 0 deletions arch/x86/include/asm/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
return &arch_ftrace_regs(fregs)->regs;
}

#define arch_ftrace_partial_regs(regs) do { \
regs->flags &= ~X86_EFLAGS_FIXED; \
regs->cs = __KERNEL_CS; \
} while (0)

#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \
(_regs)->ip = arch_ftrace_regs(fregs)->regs.ip; \
(_regs)->sp = arch_ftrace_regs(fregs)->regs.sp; \
Expand Down
8 changes: 7 additions & 1 deletion arch/x86/kernel/ftrace_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,17 @@ SYM_CODE_START(return_to_handler)
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR

/* Restore return_to_handler value that got eaten by previous ret instruction. */
subq $8, %rsp
UNWIND_HINT_FUNC

/* Save ftrace_regs for function exit context */
subq $(FRAME_SIZE), %rsp

movq %rax, RAX(%rsp)
movq %rdx, RDX(%rsp)
movq %rbp, RBP(%rsp)
movq %rsp, RSP(%rsp)
movq %rsp, %rdi

call ftrace_return_to_handler
Expand All @@ -368,7 +373,8 @@ SYM_CODE_START(return_to_handler)
movq RDX(%rsp), %rdx
movq RAX(%rsp), %rax

addq $(FRAME_SIZE), %rsp
addq $(FRAME_SIZE) + 8, %rsp

/*
* Jump back to the old return address. This cannot be JMP_NOSPEC rdi
* since IBT would demand that contain ENDBR, which simply isn't so for
Expand Down
10 changes: 9 additions & 1 deletion include/linux/ftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ static __always_inline struct pt_regs *ftrace_get_regs(struct ftrace_regs *fregs
#if !defined(CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS) || \
defined(CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS)

#ifndef arch_ftrace_partial_regs
#define arch_ftrace_partial_regs(regs) do {} while (0)
#endif

static __always_inline struct pt_regs *
ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
{
Expand All @@ -202,7 +206,11 @@ ftrace_partial_regs(struct ftrace_regs *fregs, struct pt_regs *regs)
* Since arch_ftrace_get_regs() will check some members and may return
* NULL, we can not use it.
*/
return &arch_ftrace_regs(fregs)->regs;
regs = &arch_ftrace_regs(fregs)->regs;

/* Allow arch specific updates to regs. */
arch_ftrace_partial_regs(regs);
return regs;
}

#endif /* !CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS || CONFIG_HAVE_FTRACE_REGS_HAVING_PT_REGS */
Expand Down
150 changes: 150 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/stacktrace_ips.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// 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();

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"));

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(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();
}
#else
static void __test_stacktrace_ips(void)
{
test__skip();
}
#endif

void test_stacktrace_ips(void)
{
__test_stacktrace_ips();
}
49 changes: 49 additions & 0 deletions tools/testing/selftests/bpf/progs/stacktrace_ips.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Facebook

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

#ifndef PERF_MAX_STACK_DEPTH
#define PERF_MAX_STACK_DEPTH 127
#endif

typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH];

struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(max_entries, 16384);
__type(key, __u32);
__type(value, stack_trace_t);
} stackmap SEC(".maps");

extern bool CONFIG_UNWINDER_ORC __kconfig __weak;

/*
* This function is here to have CONFIG_UNWINDER_ORC
* used and added to object BTF.
*/
int unused(void)
{
return CONFIG_UNWINDER_ORC ? 0 : 1;
}

__u32 stack_key;

SEC("kprobe.multi")
int kprobe_multi_test(struct pt_regs *ctx)
{
stack_key = bpf_get_stackid(ctx, &stackmap, 0);
return 0;
}

SEC("raw_tp/bpf_testmod_test_read")
int rawtp_test(void *ctx)
{
/* Skip ebpf program entry in the stack. */
stack_key = bpf_get_stackid(ctx, &stackmap, 0);
return 0;
}

char _license[] SEC("license") = "GPL";
26 changes: 26 additions & 0 deletions tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,30 @@ noinline int bpf_testmod_fentry_test11(u64 a, void *b, short c, int d,
return a + (long)b + c + d + (long)e + f + g + h + i + j + k;
}

noinline void bpf_testmod_stacktrace_test(void)
{
/* used for stacktrace test as attach function */
asm volatile ("");
}

noinline void bpf_testmod_stacktrace_test_3(void)
{
bpf_testmod_stacktrace_test();
asm volatile ("");
}

noinline void bpf_testmod_stacktrace_test_2(void)
{
bpf_testmod_stacktrace_test_3();
asm volatile ("");
}

noinline void bpf_testmod_stacktrace_test_1(void)
{
bpf_testmod_stacktrace_test_2();
asm volatile ("");
}

int bpf_testmod_fentry_ok;

noinline ssize_t
Expand Down Expand Up @@ -497,6 +521,8 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,
21, 22, 23, 24, 25, 26) != 231)
goto out;

bpf_testmod_stacktrace_test_1();

bpf_testmod_fentry_ok = 1;
out:
return -EIO; /* always fail */
Expand Down
Loading