Skip to content

Commit 20380bb

Browse files
AKASHI Takahirowildea01
AKASHI Takahiro
authored andcommitted
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>
1 parent fe13f95 commit 20380bb

File tree

8 files changed

+54
-6
lines changed

8 files changed

+54
-6
lines changed

arch/arm64/include/asm/ftrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ struct dyn_arch_ftrace {
2828

2929
extern unsigned long ftrace_graph_call;
3030

31+
extern void return_to_handler(void);
32+
3133
static inline unsigned long ftrace_call_adjust(unsigned long addr)
3234
{
3335
/*

arch/arm64/include/asm/stacktrace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ struct stackframe {
2222
unsigned long fp;
2323
unsigned long sp;
2424
unsigned long pc;
25+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
26+
unsigned int graph;
27+
#endif
2528
};
2629

2730
extern int unwind_frame(struct task_struct *tsk, struct stackframe *frame);

arch/arm64/kernel/perf_callchain.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,
164164
frame.fp = regs->regs[29];
165165
frame.sp = regs->sp;
166166
frame.pc = regs->pc;
167+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
168+
frame.graph = current->curr_ret_stack;
169+
#endif
167170

168171
walk_stackframe(current, &frame, callchain_trace, entry);
169172
}

arch/arm64/kernel/process.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,9 @@ unsigned long get_wchan(struct task_struct *p)
344344
frame.fp = thread_saved_fp(p);
345345
frame.sp = thread_saved_sp(p);
346346
frame.pc = thread_saved_pc(p);
347+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
348+
frame.graph = p->curr_ret_stack;
349+
#endif
347350
stack_page = (unsigned long)task_stack_page(p);
348351
do {
349352
if (frame.sp < stack_page ||

arch/arm64/kernel/return_address.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ void *return_address(unsigned int level)
4343
frame.fp = (unsigned long)__builtin_frame_address(0);
4444
frame.sp = current_stack_pointer;
4545
frame.pc = (unsigned long)return_address; /* dummy */
46+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
47+
frame.graph = current->curr_ret_stack;
48+
#endif
4649

4750
walk_stackframe(current, &frame, save_return_addr, &data);
4851

arch/arm64/kernel/stacktrace.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
#include <linux/kernel.h>
1919
#include <linux/export.h>
20+
#include <linux/ftrace.h>
2021
#include <linux/sched.h>
2122
#include <linux/stacktrace.h>
2223

@@ -66,6 +67,19 @@ int notrace unwind_frame(struct task_struct *tsk, struct stackframe *frame)
6667
frame->fp = *(unsigned long *)(fp);
6768
frame->pc = *(unsigned long *)(fp + 8);
6869

70+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
71+
if (tsk && tsk->ret_stack &&
72+
(frame->pc == (unsigned long)return_to_handler)) {
73+
/*
74+
* This is a case where function graph tracer has
75+
* modified a return address (LR) in a stack frame
76+
* to hook a function return.
77+
* So replace it to an original value.
78+
*/
79+
frame->pc = tsk->ret_stack[frame->graph--].ret;
80+
}
81+
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
82+
6983
/*
7084
* Check whether we are going to walk through from interrupt stack
7185
* to task stack.
@@ -158,6 +172,9 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
158172
frame.sp = current_stack_pointer;
159173
frame.pc = (unsigned long)save_stack_trace_tsk;
160174
}
175+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
176+
frame.graph = tsk->curr_ret_stack;
177+
#endif
161178

162179
walk_stackframe(tsk, &frame, save_trace, &data);
163180
if (trace->nr_entries < trace->max_entries)

arch/arm64/kernel/time.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ unsigned long profile_pc(struct pt_regs *regs)
5252
frame.fp = regs->regs[29];
5353
frame.sp = regs->sp;
5454
frame.pc = regs->pc;
55+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
56+
frame.graph = -1; /* no task info */
57+
#endif
5558
do {
5659
int ret = unwind_frame(NULL, &frame);
5760
if (ret < 0)

arch/arm64/kernel/traps.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,14 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
147147
{
148148
struct stackframe frame;
149149
unsigned long irq_stack_ptr = IRQ_STACK_PTR(smp_processor_id());
150+
int skip;
150151

151152
pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
152153

153154
if (!tsk)
154155
tsk = current;
155156

156-
if (regs) {
157-
frame.fp = regs->regs[29];
158-
frame.sp = regs->sp;
159-
frame.pc = regs->pc;
160-
} else if (tsk == current) {
157+
if (tsk == current) {
161158
frame.fp = (unsigned long)__builtin_frame_address(0);
162159
frame.sp = current_stack_pointer;
163160
frame.pc = (unsigned long)dump_backtrace;
@@ -169,14 +166,31 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
169166
frame.sp = thread_saved_sp(tsk);
170167
frame.pc = thread_saved_pc(tsk);
171168
}
169+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
170+
frame.graph = tsk->curr_ret_stack;
171+
#endif
172172

173+
skip = !!regs;
173174
pr_emerg("Call trace:\n");
174175
while (1) {
175176
unsigned long where = frame.pc;
176177
unsigned long stack;
177178
int ret;
178179

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+
}
180194
ret = unwind_frame(tsk, &frame);
181195
if (ret < 0)
182196
break;

0 commit comments

Comments
 (0)