diff options
author | Gary King <gking@nvidia.com> | 2009-12-07 16:18:05 -0800 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2009-12-07 16:18:05 -0800 |
commit | bb11d46b72a4a2b53f890faa970a380cf0084bf2 (patch) | |
tree | 2d9d2955e069fed69febbe567a835fcc48ec6ec6 /arch/arm/kernel/stacktrace.c | |
parent | e1b5e49debba7174e7b9c48195de8abfd54911dd (diff) | |
parent | 74ece4056571443eef30d4dff62180944b5a39d8 (diff) |
Merge commit 'arm/2.6.28-arm' into android-tegra-2.6.29
Conflicts:
MAINTAINERS
arch/arm/Kconfig
arch/arm/Makefile
arch/arm/boot/compressed/head.S
arch/arm/common/Makefile
arch/arm/configs/realview-smp_defconfig
arch/arm/configs/realview_defconfig
arch/arm/configs/versatile_defconfig
arch/arm/include/asm/elf.h
arch/arm/include/asm/uaccess.h
arch/arm/kernel/module.c
arch/arm/kernel/signal.c
arch/arm/mach-realview/Kconfig
arch/arm/mach-realview/Makefile
arch/arm/mach-realview/core.c
arch/arm/mach-realview/core.h
arch/arm/mach-realview/include/mach/board-pba8.h
arch/arm/mach-realview/include/mach/debug-macro.S
arch/arm/mach-realview/include/mach/hardware.h
arch/arm/mach-realview/include/mach/irqs.h
arch/arm/mach-realview/include/mach/memory.h
arch/arm/mach-realview/include/mach/uncompress.h
arch/arm/mach-realview/localtimer.c
arch/arm/mach-realview/platsmp.c
arch/arm/mach-realview/realview_eb.c
arch/arm/mach-realview/realview_pb1176.c
arch/arm/mach-realview/realview_pb11mp.c
arch/arm/mach-realview/realview_pba8.c
arch/arm/mm/Kconfig
arch/arm/mm/copypage-v6.c
arch/arm/mm/dma-mapping.c
arch/arm/mm/proc-v7.S
arch/arm/oprofile/op_model_mpcore.c
arch/arm/tools/mach-types
arch/arm/vfp/vfpmodule.c
drivers/mtd/maps/integrator-flash.c
drivers/net/smsc911x.c
drivers/net/smsc911x.h
Diffstat (limited to 'arch/arm/kernel/stacktrace.c')
-rw-r--r-- | arch/arm/kernel/stacktrace.c | 88 |
1 files changed, 60 insertions, 28 deletions
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index fc650f64df43..b371c54e548a 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c @@ -2,35 +2,60 @@ #include <linux/sched.h> #include <linux/stacktrace.h> -#include "stacktrace.h" - -int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, - int (*fn)(struct stackframe *, void *), void *data) +#include <asm/stacktrace.h> + +#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) +/* + * Unwind the current stack frame and store the new register values in the + * structure passed as argument. Unwinding is equivalent to a function return, + * hence the new PC value rather than LR should be used for backtrace. + * + * With framepointer enabled, a simple function prologue looks like this: + * mov ip, sp + * stmdb sp!, {fp, ip, lr, pc} + * sub fp, ip, #4 + * + * A simple function epilogue looks like this: + * ldm sp, {fp, sp, pc} + * + * Note that with framepointer enabled, even the leaf functions have the same + * prologue and epilogue, therefore we can ignore the LR value in this case. + */ +int unwind_frame(struct stackframe *frame) { - struct stackframe *frame; + unsigned long high, low; + unsigned long fp = frame->fp; - do { - /* - * Check current frame pointer is within bounds - */ - if (fp < (low + 12) || fp + 4 >= high) - break; + /* only go to a higher address on the stack */ + low = frame->sp; + high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; - frame = (struct stackframe *)(fp - 12); + /* check current frame pointer is within bounds */ + if (fp < (low + 12) || fp + 4 >= high) + return -EINVAL; - if (fn(frame, data)) - break; - - /* - * Update the low bound - the next frame must always - * be at a higher address than the current frame. - */ - low = fp + 4; - fp = frame->fp; - } while (fp); + /* restore the registers from the stack frame */ + frame->fp = *(unsigned long *)(fp - 12); + frame->sp = *(unsigned long *)(fp - 8); + frame->pc = *(unsigned long *)(fp - 4); return 0; } +#endif + +void walk_stackframe(struct stackframe *frame, + int (*fn)(struct stackframe *, void *), void *data) +{ + while (1) { + int ret; + + if (fn(frame, data)) + break; + ret = unwind_frame(frame); + if (ret < 0) + break; + } +} EXPORT_SYMBOL(walk_stackframe); #ifdef CONFIG_STACKTRACE @@ -44,7 +69,7 @@ static int save_trace(struct stackframe *frame, void *d) { struct stack_trace_data *data = d; struct stack_trace *trace = data->trace; - unsigned long addr = frame->lr; + unsigned long addr = frame->pc; if (data->no_sched_functions && in_sched_functions(addr)) return 0; @@ -61,11 +86,10 @@ static int save_trace(struct stackframe *frame, void *d) void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) { struct stack_trace_data data; - unsigned long fp, base; + struct stackframe frame; data.trace = trace; data.skip = trace->skip; - base = (unsigned long)task_stack_page(tsk); if (tsk != current) { #ifdef CONFIG_SMP @@ -76,14 +100,22 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) BUG(); #else data.no_sched_functions = 1; - fp = thread_saved_fp(tsk); + frame.fp = thread_saved_fp(tsk); + frame.sp = thread_saved_sp(tsk); + frame.lr = 0; /* recovered from the stack */ + frame.pc = thread_saved_pc(tsk); #endif } else { + register unsigned long current_sp asm ("sp"); + data.no_sched_functions = 0; - asm("mov %0, fp" : "=r" (fp)); + 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; } - walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data); + walk_stackframe(&frame, save_trace, &data); if (trace->nr_entries < trace->max_entries) trace->entries[trace->nr_entries++] = ULONG_MAX; } |