diff options
author | AKASHI Takahiro <takahiro.akashi@linaro.org> | 2015-12-15 03:33:41 -0500 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2015-12-21 12:26:02 -0500 |
commit | 20380bb390a443b2c5c8800cec59743faf8151b4 (patch) | |
tree | 3f9d14ec8f265c8969f322a8cd239f3e946e18bb /arch/arm64/kernel/traps.c | |
parent | fe13f95b720075327a761fe6ddb45b0c90cab504 (diff) |
arm64: ftrace: fix a stack tracer's output under function graph tracer
Function graph tracer modifies a return address (LR) in a stack frame
to hook a function return. This will result in many useless entries
(return_to_handler) showing up in
a) a stack tracer's output
b) perf call graph (with perf record -g)
c) dump_backtrace (at panic et al.)
For example, in case of a),
$ echo function_graph > /sys/kernel/debug/tracing/current_tracer
$ echo 1 > /proc/sys/kernel/stack_trace_enabled
$ cat /sys/kernel/debug/tracing/stack_trace
Depth Size Location (54 entries)
----- ---- --------
0) 4504 16 gic_raise_softirq+0x28/0x150
1) 4488 80 smp_cross_call+0x38/0xb8
2) 4408 48 return_to_handler+0x0/0x40
3) 4360 32 return_to_handler+0x0/0x40
...
In case of b),
$ echo function_graph > /sys/kernel/debug/tracing/current_tracer
$ perf record -e mem:XXX:x -ag -- sleep 10
$ perf report
...
| | |--0.22%-- 0x550f8
| | | 0x10888
| | | el0_svc_naked
| | | sys_openat
| | | return_to_handler
| | | return_to_handler
...
In case of c),
$ echo function_graph > /sys/kernel/debug/tracing/current_tracer
$ echo c > /proc/sysrq-trigger
...
Call trace:
[<ffffffc00044d3ac>] sysrq_handle_crash+0x24/0x30
[<ffffffc000092250>] return_to_handler+0x0/0x40
[<ffffffc000092250>] return_to_handler+0x0/0x40
...
This patch replaces such entries with real addresses preserved in
current->ret_stack[] at unwind_frame(). This way, we can cover all
the cases.
Reviewed-by: Jungseok Lee <jungseoklee85@gmail.com>
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
[will: fixed minor context changes conflicting with irq stack bits]
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm64/kernel/traps.c')
-rw-r--r-- | arch/arm64/kernel/traps.c | 26 |
1 files changed, 20 insertions, 6 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 937008523fa5..bdc293f6adc4 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c | |||
@@ -147,17 +147,14 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | |||
147 | { | 147 | { |
148 | struct stackframe frame; | 148 | struct stackframe frame; |
149 | unsigned long irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id()); | 149 | unsigned long irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id()); |
150 | int skip; | ||
150 | 151 | ||
151 | pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); | 152 | pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); |
152 | 153 | ||
153 | if (!tsk) | 154 | if (!tsk) |
154 | tsk = current; | 155 | tsk = current; |
155 | 156 | ||
156 | if (regs) { | 157 | if (tsk == current) { |
157 | frame.fp = regs->regs[29]; | ||
158 | frame.sp = regs->sp; | ||
159 | frame.pc = regs->pc; | ||
160 | } else if (tsk == current) { | ||
161 | frame.fp = (unsigned long)__builtin_frame_address(0); | 158 | frame.fp = (unsigned long)__builtin_frame_address(0); |
162 | frame.sp = current_stack_pointer; | 159 | frame.sp = current_stack_pointer; |
163 | frame.pc = (unsigned long)dump_backtrace; | 160 | frame.pc = (unsigned long)dump_backtrace; |
@@ -169,14 +166,31 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | |||
169 | frame.sp = thread_saved_sp(tsk); | 166 | frame.sp = thread_saved_sp(tsk); |
170 | frame.pc = thread_saved_pc(tsk); | 167 | frame.pc = thread_saved_pc(tsk); |
171 | } | 168 | } |
169 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
170 | frame.graph = tsk->curr_ret_stack; | ||
171 | #endif | ||
172 | 172 | ||
173 | skip = !!regs; | ||
173 | pr_emerg("Call trace:\n"); | 174 | pr_emerg("Call trace:\n"); |
174 | while (1) { | 175 | while (1) { |
175 | unsigned long where = frame.pc; | 176 | unsigned long where = frame.pc; |
176 | unsigned long stack; | 177 | unsigned long stack; |
177 | int ret; | 178 | int ret; |
178 | 179 | ||
179 | dump_backtrace_entry(where); | 180 | /* skip until specified stack frame */ |
181 | if (!skip) { | ||
182 | dump_backtrace_entry(where); | ||
183 | } else if (frame.fp == regs->regs[29]) { | ||
184 | skip = 0; | ||
185 | /* | ||
186 | * Mostly, this is the case where this function is | ||
187 | * called in panic/abort. As exception handler's | ||
188 | * stack frame does not contain the corresponding pc | ||
189 | * at which an exception has taken place, use regs->pc | ||
190 | * instead. | ||
191 | */ | ||
192 | dump_backtrace_entry(regs->pc); | ||
193 | } | ||
180 | ret = unwind_frame(tsk, &frame); | 194 | ret = unwind_frame(tsk, &frame); |
181 | if (ret < 0) | 195 | if (ret < 0) |
182 | break; | 196 | break; |