1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */ |
3 | |
4 | #include <linux/perf_event.h> |
5 | #include <linux/uaccess.h> |
6 | |
7 | #include <asm/stacktrace.h> |
8 | |
9 | /* |
10 | * Get the return address for a single stackframe and return a pointer to the |
11 | * next frame tail. |
12 | */ |
13 | static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry, |
14 | unsigned long fp, unsigned long reg_ra) |
15 | { |
16 | struct stackframe buftail; |
17 | unsigned long ra = 0; |
18 | unsigned long __user *user_frame_tail = |
19 | (unsigned long __user *)(fp - sizeof(struct stackframe)); |
20 | |
21 | /* Check accessibility of one struct frame_tail beyond */ |
22 | if (!access_ok(user_frame_tail, sizeof(buftail))) |
23 | return 0; |
24 | if (__copy_from_user_inatomic(to: &buftail, from: user_frame_tail, |
25 | n: sizeof(buftail))) |
26 | return 0; |
27 | |
28 | if (reg_ra != 0) |
29 | ra = reg_ra; |
30 | else |
31 | ra = buftail.ra; |
32 | |
33 | fp = buftail.fp; |
34 | if (ra != 0) |
35 | perf_callchain_store(ctx: entry, ip: ra); |
36 | else |
37 | return 0; |
38 | |
39 | return fp; |
40 | } |
41 | |
42 | /* |
43 | * This will be called when the target is in user mode |
44 | * This function will only be called when we use |
45 | * "PERF_SAMPLE_CALLCHAIN" in |
46 | * kernel/events/core.c:perf_prepare_sample() |
47 | * |
48 | * How to trigger perf_callchain_[user/kernel] : |
49 | * $ perf record -e cpu-clock --call-graph fp ./program |
50 | * $ perf report --call-graph |
51 | * |
52 | * On RISC-V platform, the program being sampled and the C library |
53 | * need to be compiled with -fno-omit-frame-pointer, otherwise |
54 | * the user stack will not contain function frame. |
55 | */ |
56 | void perf_callchain_user(struct perf_callchain_entry_ctx *entry, |
57 | struct pt_regs *regs) |
58 | { |
59 | unsigned long fp = 0; |
60 | |
61 | fp = regs->s0; |
62 | perf_callchain_store(ctx: entry, ip: regs->epc); |
63 | |
64 | fp = user_backtrace(entry, fp, reg_ra: regs->ra); |
65 | while (fp && !(fp & 0x3) && entry->nr < entry->max_stack) |
66 | fp = user_backtrace(entry, fp, reg_ra: 0); |
67 | } |
68 | |
69 | static bool fill_callchain(void *entry, unsigned long pc) |
70 | { |
71 | return perf_callchain_store(ctx: entry, ip: pc) == 0; |
72 | } |
73 | |
74 | void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, |
75 | struct pt_regs *regs) |
76 | { |
77 | walk_stackframe(NULL, regs, fill_callchain, entry); |
78 | } |
79 | |