diff options
author | Mao Han <han_mao@c-sky.com> | 2019-08-29 02:57:00 -0400 |
---|---|---|
committer | Paul Walmsley <paul.walmsley@sifive.com> | 2019-09-04 15:43:00 -0400 |
commit | dbeb90b0c1eb86a9b963b929d3c937afb7dadfa3 (patch) | |
tree | 75c68accfe0d5c2cefacd0ce511e6efdbbecd196 | |
parent | 909548d6c5789dbd6cd4c7a6b32fcf0ae974f5ee (diff) |
riscv: Add perf callchain support
This patch add support for perf callchain sampling on riscv platforms.
The return address of leaf function is retrieved from pt_regs as
it is not saved in the outmost frame.
Signed-off-by: Mao Han <han_mao@c-sky.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Greentime Hu <green.hu@gmail.com>
Cc: Palmer Dabbelt <palmer@sifive.com>
Cc: linux-riscv <linux-riscv@lists.infradead.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Guo Ren <guoren@kernel.org>
Tested-by: Greentime Hu <greentime.hu@sifive.com>
[paul.walmsley@sifive.com: fixed some 'checkpatch.pl --strict' issues;
fixed patch description spelling]
Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
-rw-r--r-- | arch/riscv/Makefile | 3 | ||||
-rw-r--r-- | arch/riscv/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/riscv/kernel/perf_callchain.c | 94 | ||||
-rw-r--r-- | arch/riscv/kernel/stacktrace.c | 4 |
4 files changed, 101 insertions, 3 deletions
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index a73659e30f8d..4f0a3d2018d2 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile | |||
@@ -54,6 +54,9 @@ endif | |||
54 | ifeq ($(CONFIG_MODULE_SECTIONS),y) | 54 | ifeq ($(CONFIG_MODULE_SECTIONS),y) |
55 | KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/riscv/kernel/module.lds | 55 | KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/riscv/kernel/module.lds |
56 | endif | 56 | endif |
57 | ifeq ($(CONFIG_PERF_EVENTS),y) | ||
58 | KBUILD_CFLAGS += -fno-omit-frame-pointer | ||
59 | endif | ||
57 | 60 | ||
58 | KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax) | 61 | KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax) |
59 | 62 | ||
diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 2420d37d96de..b1bea89fc814 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile | |||
@@ -38,6 +38,7 @@ obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o | |||
38 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o | 38 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o |
39 | obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o | 39 | obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o |
40 | 40 | ||
41 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o | 41 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o |
42 | obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o | ||
42 | 43 | ||
43 | clean: | 44 | clean: |
diff --git a/arch/riscv/kernel/perf_callchain.c b/arch/riscv/kernel/perf_callchain.c new file mode 100644 index 000000000000..8d2804f05cf9 --- /dev/null +++ b/arch/riscv/kernel/perf_callchain.c | |||
@@ -0,0 +1,94 @@ | |||
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 | /* Kernel callchain */ | ||
8 | struct stackframe { | ||
9 | unsigned long fp; | ||
10 | unsigned long ra; | ||
11 | }; | ||
12 | |||
13 | /* | ||
14 | * Get the return address for a single stackframe and return a pointer to the | ||
15 | * next frame tail. | ||
16 | */ | ||
17 | static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry, | ||
18 | unsigned long fp, unsigned long reg_ra) | ||
19 | { | ||
20 | struct stackframe buftail; | ||
21 | unsigned long ra = 0; | ||
22 | unsigned long *user_frame_tail = | ||
23 | (unsigned long *)(fp - sizeof(struct stackframe)); | ||
24 | |||
25 | /* Check accessibility of one struct frame_tail beyond */ | ||
26 | if (!access_ok(user_frame_tail, sizeof(buftail))) | ||
27 | return 0; | ||
28 | if (__copy_from_user_inatomic(&buftail, user_frame_tail, | ||
29 | sizeof(buftail))) | ||
30 | return 0; | ||
31 | |||
32 | if (reg_ra != 0) | ||
33 | ra = reg_ra; | ||
34 | else | ||
35 | ra = buftail.ra; | ||
36 | |||
37 | fp = buftail.fp; | ||
38 | if (ra != 0) | ||
39 | perf_callchain_store(entry, ra); | ||
40 | else | ||
41 | return 0; | ||
42 | |||
43 | return fp; | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * This will be called when the target is in user mode | ||
48 | * This function will only be called when we use | ||
49 | * "PERF_SAMPLE_CALLCHAIN" in | ||
50 | * kernel/events/core.c:perf_prepare_sample() | ||
51 | * | ||
52 | * How to trigger perf_callchain_[user/kernel] : | ||
53 | * $ perf record -e cpu-clock --call-graph fp ./program | ||
54 | * $ perf report --call-graph | ||
55 | * | ||
56 | * On RISC-V platform, the program being sampled and the C library | ||
57 | * need to be compiled with -fno-omit-frame-pointer, otherwise | ||
58 | * the user stack will not contain function frame. | ||
59 | */ | ||
60 | void perf_callchain_user(struct perf_callchain_entry_ctx *entry, | ||
61 | struct pt_regs *regs) | ||
62 | { | ||
63 | unsigned long fp = 0; | ||
64 | |||
65 | /* RISC-V does not support perf in guest mode. */ | ||
66 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) | ||
67 | return; | ||
68 | |||
69 | fp = regs->s0; | ||
70 | perf_callchain_store(entry, regs->sepc); | ||
71 | |||
72 | fp = user_backtrace(entry, fp, regs->ra); | ||
73 | while (fp && !(fp & 0x3) && entry->nr < entry->max_stack) | ||
74 | fp = user_backtrace(entry, fp, 0); | ||
75 | } | ||
76 | |||
77 | bool fill_callchain(unsigned long pc, void *entry) | ||
78 | { | ||
79 | return perf_callchain_store(entry, pc); | ||
80 | } | ||
81 | |||
82 | void notrace walk_stackframe(struct task_struct *task, | ||
83 | struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg); | ||
84 | void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, | ||
85 | struct pt_regs *regs) | ||
86 | { | ||
87 | /* RISC-V does not support perf in guest mode. */ | ||
88 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
89 | pr_warn("RISC-V does not support perf in guest mode!"); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | walk_stackframe(NULL, regs, fill_callchain, entry); | ||
94 | } | ||
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index f15642715d1a..0940681d2f68 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c | |||
@@ -19,8 +19,8 @@ struct stackframe { | |||
19 | unsigned long ra; | 19 | unsigned long ra; |
20 | }; | 20 | }; |
21 | 21 | ||
22 | static void notrace walk_stackframe(struct task_struct *task, | 22 | void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, |
23 | struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg) | 23 | bool (*fn)(unsigned long, void *), void *arg) |
24 | { | 24 | { |
25 | unsigned long fp, sp, pc; | 25 | unsigned long fp, sp, pc; |
26 | 26 | ||