diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 14:03:14 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 14:03:14 -0700 | 
| commit | 0fe41b8982001cd14ee2c77cd776735a5024e98b (patch) | |
| tree | 83e65d595c413d55259ea14fb97748ce5efe5707 /arch/arm/kernel/stacktrace.c | |
| parent | eedf2c5296a8dfaaf9aec1a938c1d3bd73159a30 (diff) | |
| parent | 9759d22c8348343b0da4e25d6150c41712686c14 (diff) | |
Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
* 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm: (422 commits)
  [ARM] 5435/1: fix compile warning in sanity_check_meminfo()
  [ARM] 5434/1: ARM: OMAP: Fix mailbox compile for 24xx
  [ARM] pxa: fix the bad assumption that PCMCIA sockets always start with 0
  [ARM] pxa: fix Colibri PXA300 and PXA320 LCD backlight pins
  imxfb: Fix TFT mode
  i.MX21/27: remove ifdef CONFIG_FB_IMX
  imxfb: add clock support
  mxc: add arch_reset() function
  clkdev: add possibility to get a clock based on the device name
  i.MX1: remove fb support from mach-imx
  [ARM] pxa: build arch/arm/plat-pxa/mfp.c only when PXA3xx or ARCH_MMP defined
  Gemini: Add support for Teltonika RUT100
  Gemini: gpiolib based GPIO support v2
  MAINTAINERS: add myself as Gemini architecture maintainer
  ARM: Add Gemini architecture v3
  [ARM] OMAP: Fix compile for omap2_init_common_hw()
  MAINTAINERS: Add myself as Faraday ARM core variant maintainer
  ARM: Add support for FA526 v2
  [ARM] acorn,ebsa110,footbridge,integrator,sa1100: Convert asm/io.h to linux/io.h
  [ARM] collie: fix two minor formatting nits
  ...
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..9f444e5cc165 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; - -	do { -		/* -		 * Check current frame pointer is within bounds -		 */ -		if (fp < (low + 12) || fp + 4 >= high) -			break; +	unsigned long high, low; +	unsigned long fp = frame->fp; -		frame = (struct stackframe *)(fp - 12); +	/* only go to a higher address on the stack */ +	low = frame->sp; +	high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; -		if (fn(frame, data)) -			break; +	/* check current frame pointer is within bounds */ +	if (fp < (low + 12) || fp + 4 >= high) +		return -EINVAL; -		/* -		 * 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;  } | 
