From 3683f44c42e991d313dc301504ee0fca1aeb8580 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 May 2014 11:03:28 +0100 Subject: ARM: stacktrace: avoid listing stacktrace functions in stacktrace While debugging the FEC ethernet driver using stacktrace, it was noticed that the stacktraces always begin as follows: [] save_stack_trace_tsk+0x0/0x98 [] save_stack_trace+0x24/0x28 ... This is because the stack trace code includes the stack frames for itself. This is incorrect behaviour, and also leads to "skip" doing the wrong thing (which is the number of stack frames to avoid recording.) Perversely, it does the right thing when passed a non-current thread. Fix this by ensuring that we have a known constant number of frames above the main stack trace function, and always skip these. Cc: Signed-off-by: Russell King --- arch/arm/kernel/stacktrace.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'arch/arm/kernel/stacktrace.c') diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index af4e8c8a542..6582c4adc18 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -83,13 +83,16 @@ static int save_trace(struct stackframe *frame, void *d) return trace->nr_entries >= trace->max_entries; } -void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +/* This must be noinline to so that our skip calculation works correctly */ +static noinline void __save_stack_trace(struct task_struct *tsk, + struct stack_trace *trace, unsigned int nosched) { struct stack_trace_data data; struct stackframe frame; data.trace = trace; data.skip = trace->skip; + data.no_sched_functions = nosched; if (tsk != current) { #ifdef CONFIG_SMP @@ -102,7 +105,6 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) trace->entries[trace->nr_entries++] = ULONG_MAX; return; #else - data.no_sched_functions = 1; frame.fp = thread_saved_fp(tsk); frame.sp = thread_saved_sp(tsk); frame.lr = 0; /* recovered from the stack */ @@ -111,11 +113,12 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) } else { register unsigned long current_sp asm ("sp"); - data.no_sched_functions = 0; + /* We don't want this function nor the caller */ + data.skip += 2; frame.fp = (unsigned long)__builtin_frame_address(0); frame.sp = current_sp; frame.lr = (unsigned long)__builtin_return_address(0); - frame.pc = (unsigned long)save_stack_trace_tsk; + frame.pc = (unsigned long)__save_stack_trace; } walk_stackframe(&frame, save_trace, &data); @@ -123,9 +126,14 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) trace->entries[trace->nr_entries++] = ULONG_MAX; } +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +{ + __save_stack_trace(tsk, trace, 1); +} + void save_stack_trace(struct stack_trace *trace) { - save_stack_trace_tsk(current, trace); + __save_stack_trace(current, trace, 0); } EXPORT_SYMBOL_GPL(save_stack_trace); #endif -- cgit v1.2.3-70-g09d2 From 07b403415884e961920f55e6db462dff15d9df5a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 May 2014 16:17:16 +0100 Subject: ARM: stacktrace: include exception PC value in stacktrace output When we unwind through an exception stack, include the saved PC value into the stack trace: this fills in an otherwise missed functions from the trace (as indicated below): [] fec_enet_interrupt+0xa0/0xe8 [] handle_irq_event_percpu+0x68/0x228 [] handle_irq_event+0x4c/0x6c [] handle_fasteoi_irq+0xac/0x198 [] generic_handle_irq+0x4c/0x60 [] handle_IRQ+0x40/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x40/0x50 [] __do_softirq+0xe0/0x2fc <==== [] irq_exit+0xb0/0x100 [] handle_IRQ+0x44/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x40/0x50 [] arch_cpu_idle+0x30/0x38 <==== [] cpu_startup_entry+0xac/0x214 [] rest_init+0x68/0x80 [] start_kernel+0x2fc/0x358 Signed-off-by: Russell King --- arch/arm/kernel/stacktrace.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'arch/arm/kernel/stacktrace.c') diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index 6582c4adc18..5a80ddfe703 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -3,6 +3,7 @@ #include #include +#include #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) /* @@ -61,6 +62,7 @@ EXPORT_SYMBOL(walk_stackframe); #ifdef CONFIG_STACKTRACE struct stack_trace_data { struct stack_trace *trace; + unsigned long last_pc; unsigned int no_sched_functions; unsigned int skip; }; @@ -69,6 +71,7 @@ static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; + struct pt_regs *regs; unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) @@ -80,6 +83,25 @@ static int save_trace(struct stackframe *frame, void *d) trace->entries[trace->nr_entries++] = addr; + if (trace->nr_entries >= trace->max_entries) + return 1; + + /* + * in_exception_text() is designed to test if the PC is one of + * the functions which has an exception stack above it, but + * unfortunately what is in frame->pc is the return LR value, + * not the saved PC value. So, we need to track the previous + * frame PC value when doing this. + */ + addr = data->last_pc; + data->last_pc = frame->pc; + if (!in_exception_text(addr)) + return 0; + + regs = (struct pt_regs *)frame->sp; + + trace->entries[trace->nr_entries++] = regs->ARM_pc; + return trace->nr_entries >= trace->max_entries; } @@ -91,6 +113,7 @@ static noinline void __save_stack_trace(struct task_struct *tsk, struct stackframe frame; data.trace = trace; + data.last_pc = ULONG_MAX; data.skip = trace->skip; data.no_sched_functions = nosched; -- cgit v1.2.3-70-g09d2 From 9c986661638c69772f5479c4715061239ec61b29 Mon Sep 17 00:00:00 2001 From: Lin Yongting Date: Sun, 4 May 2014 16:27:41 +0100 Subject: ARM: 8049/1: ftrace/add save_stack_trace_regs() implementation When configure kprobe events of ftrace with "stacktrace" option enabled in arm, there is no stacktrace was recorded after the kprobe event was triggered. The root cause is no save_stack_trace_regs() function implemented. Implement the save_stack_trace_regs() function in arm, then ftrace will call this architecture-related function to record the stacktrace into ring buffer. After this fix, stacktrace can be recorded, for example: # mount -t debugfs nodev /sys/kernel/debug # echo "p:netrx net_rx_action" >> /sys/kernel/debug/tracing/kprobe_events # echo 1 > /sys/kernel/debug/tracing/events/kprobes/netrx/enable # echo 1 > /sys/kernel/debug/tracing/options/stacktrace # echo 1 > /sys/kernel/debug/tracing/tracing_on # ping 127.0.0.1 -c 1 # echo 0 > /sys/kernel/debug/tracing/tracing_on # cat /sys/kernel/debug/tracing/trace # tracer: nop # # entries-in-buffer/entries-written: 12/12 #P:1 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | <------ missing some entries ----------------> ping-1200 [000] dNs1 667.603250: netrx: (net_rx_action+0x0/0x1f8) ping-1200 [000] dNs1 667.604738: => net_rx_action => do_softirq => local_bh_enable => ip_finish_output => ip_output => ip_local_out => ip_send_skb => ip_push_pending_frames => raw_sendmsg => inet_sendmsg => sock_sendmsg => SyS_sendto => ret_fast_syscall Signed-off-by: Lin Yongting Acked-by: Steven Rostedt Signed-off-by: Russell King --- arch/arm/kernel/stacktrace.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'arch/arm/kernel/stacktrace.c') diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index 5a80ddfe703..f065eb05d25 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -149,6 +149,25 @@ static noinline void __save_stack_trace(struct task_struct *tsk, trace->entries[trace->nr_entries++] = ULONG_MAX; } +void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) +{ + struct stack_trace_data data; + struct stackframe frame; + + data.trace = trace; + data.skip = trace->skip; + data.no_sched_functions = 0; + + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + + walk_stackframe(&frame, save_trace, &data); + if (trace->nr_entries < trace->max_entries) + trace->entries[trace->nr_entries++] = ULONG_MAX; +} + void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { __save_stack_trace(tsk, trace, 1); -- cgit v1.2.3-70-g09d2