Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ARM] 5382/1: unwind: Reorganise the stacktrace support

This patch changes the walk_stacktrace and its callers for easier
integration of stack unwinding. The arch/arm/kernel/stacktrace.h file is
also moved to arch/arm/include/asm/stacktrace.h.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Catalin Marinas and committed by
Russell King
2d7c11bf 67a94c23

+109 -59
+15
arch/arm/include/asm/stacktrace.h
··· 1 + #ifndef __ASM_STACKTRACE_H 2 + #define __ASM_STACKTRACE_H 3 + 4 + struct stackframe { 5 + unsigned long fp; 6 + unsigned long sp; 7 + unsigned long lr; 8 + unsigned long pc; 9 + }; 10 + 11 + extern int unwind_frame(struct stackframe *frame); 12 + extern void walk_stackframe(struct stackframe *frame, 13 + int (*fn)(struct stackframe *, void *), void *data); 14 + 15 + #endif /* __ASM_STACKTRACE_H */
+2
arch/arm/include/asm/thread_info.h
··· 99 99 100 100 #define thread_saved_pc(tsk) \ 101 101 ((unsigned long)(task_thread_info(tsk)->cpu_context.pc)) 102 + #define thread_saved_sp(tsk) \ 103 + ((unsigned long)(task_thread_info(tsk)->cpu_context.sp)) 102 104 #define thread_saved_fp(tsk) \ 103 105 ((unsigned long)(task_thread_info(tsk)->cpu_context.fp)) 104 106
+10 -11
arch/arm/kernel/process.c
··· 34 34 #include <asm/processor.h> 35 35 #include <asm/system.h> 36 36 #include <asm/thread_notify.h> 37 + #include <asm/stacktrace.h> 37 38 #include <asm/mach/time.h> 38 39 39 40 static const char *processor_modes[] = { ··· 373 372 374 373 unsigned long get_wchan(struct task_struct *p) 375 374 { 376 - unsigned long fp, lr; 377 - unsigned long stack_start, stack_end; 375 + struct stackframe frame; 378 376 int count = 0; 379 377 if (!p || p == current || p->state == TASK_RUNNING) 380 378 return 0; 381 379 382 - stack_start = (unsigned long)end_of_stack(p); 383 - stack_end = (unsigned long)task_stack_page(p) + THREAD_SIZE; 384 - 385 - fp = thread_saved_fp(p); 380 + frame.fp = thread_saved_fp(p); 381 + frame.sp = thread_saved_sp(p); 382 + frame.lr = 0; /* recovered from the stack */ 383 + frame.pc = thread_saved_pc(p); 386 384 do { 387 - if (fp < stack_start || fp > stack_end) 385 + int ret = unwind_frame(&frame); 386 + if (ret < 0) 388 387 return 0; 389 - lr = ((unsigned long *)fp)[-1]; 390 - if (!in_sched_functions(lr)) 391 - return lr; 392 - fp = *(unsigned long *) (fp - 12); 388 + if (!in_sched_functions(frame.pc)) 389 + return frame.pc; 393 390 } while (count ++ < 16); 394 391 return 0; 395 392 }
+59 -27
arch/arm/kernel/stacktrace.c
··· 2 2 #include <linux/sched.h> 3 3 #include <linux/stacktrace.h> 4 4 5 - #include "stacktrace.h" 5 + #include <asm/stacktrace.h> 6 6 7 - int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, 8 - int (*fn)(struct stackframe *, void *), void *data) 7 + #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 8 + /* 9 + * Unwind the current stack frame and store the new register values in the 10 + * structure passed as argument. Unwinding is equivalent to a function return, 11 + * hence the new PC value rather than LR should be used for backtrace. 12 + * 13 + * With framepointer enabled, a simple function prologue looks like this: 14 + * mov ip, sp 15 + * stmdb sp!, {fp, ip, lr, pc} 16 + * sub fp, ip, #4 17 + * 18 + * A simple function epilogue looks like this: 19 + * ldm sp, {fp, sp, pc} 20 + * 21 + * Note that with framepointer enabled, even the leaf functions have the same 22 + * prologue and epilogue, therefore we can ignore the LR value in this case. 23 + */ 24 + int unwind_frame(struct stackframe *frame) 9 25 { 10 - struct stackframe *frame; 26 + unsigned long high, low; 27 + unsigned long fp = frame->fp; 11 28 12 - do { 13 - /* 14 - * Check current frame pointer is within bounds 15 - */ 16 - if (fp < (low + 12) || fp + 4 >= high) 17 - break; 29 + /* only go to a higher address on the stack */ 30 + low = frame->sp; 31 + high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; 18 32 19 - frame = (struct stackframe *)(fp - 12); 33 + /* check current frame pointer is within bounds */ 34 + if (fp < (low + 12) || fp + 4 >= high) 35 + return -EINVAL; 36 + 37 + /* restore the registers from the stack frame */ 38 + frame->fp = *(unsigned long *)(fp - 12); 39 + frame->sp = *(unsigned long *)(fp - 8); 40 + frame->pc = *(unsigned long *)(fp - 4); 41 + 42 + return 0; 43 + } 44 + #endif 45 + 46 + void walk_stackframe(struct stackframe *frame, 47 + int (*fn)(struct stackframe *, void *), void *data) 48 + { 49 + while (1) { 50 + int ret; 20 51 21 52 if (fn(frame, data)) 22 53 break; 23 - 24 - /* 25 - * Update the low bound - the next frame must always 26 - * be at a higher address than the current frame. 27 - */ 28 - low = fp + 4; 29 - fp = frame->fp; 30 - } while (fp); 31 - 32 - return 0; 54 + ret = unwind_frame(frame); 55 + if (ret < 0) 56 + break; 57 + } 33 58 } 34 59 EXPORT_SYMBOL(walk_stackframe); 35 60 ··· 69 44 { 70 45 struct stack_trace_data *data = d; 71 46 struct stack_trace *trace = data->trace; 72 - unsigned long addr = frame->lr; 47 + unsigned long addr = frame->pc; 73 48 74 49 if (data->no_sched_functions && in_sched_functions(addr)) 75 50 return 0; ··· 86 61 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 87 62 { 88 63 struct stack_trace_data data; 89 - unsigned long fp, base; 64 + struct stackframe frame; 90 65 91 66 data.trace = trace; 92 67 data.skip = trace->skip; 93 - base = (unsigned long)task_stack_page(tsk); 94 68 95 69 if (tsk != current) { 96 70 #ifdef CONFIG_SMP ··· 100 76 BUG(); 101 77 #else 102 78 data.no_sched_functions = 1; 103 - fp = thread_saved_fp(tsk); 79 + frame.fp = thread_saved_fp(tsk); 80 + frame.sp = thread_saved_sp(tsk); 81 + frame.lr = 0; /* recovered from the stack */ 82 + frame.pc = thread_saved_pc(tsk); 104 83 #endif 105 84 } else { 85 + register unsigned long current_sp asm ("sp"); 86 + 106 87 data.no_sched_functions = 0; 107 - asm("mov %0, fp" : "=r" (fp)); 88 + frame.fp = (unsigned long)__builtin_frame_address(0); 89 + frame.sp = current_sp; 90 + frame.lr = (unsigned long)__builtin_return_address(0); 91 + frame.pc = (unsigned long)save_stack_trace_tsk; 108 92 } 109 93 110 - walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data); 94 + walk_stackframe(&frame, save_trace, &data); 111 95 if (trace->nr_entries < trace->max_entries) 112 96 trace->entries[trace->nr_entries++] = ULONG_MAX; 113 97 }
-9
arch/arm/kernel/stacktrace.h
··· 1 - struct stackframe { 2 - unsigned long fp; 3 - unsigned long sp; 4 - unsigned long lr; 5 - unsigned long pc; 6 - }; 7 - 8 - int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high, 9 - int (*fn)(struct stackframe *, void *), void *data);
+15 -6
arch/arm/kernel/time.c
··· 33 33 34 34 #include <asm/leds.h> 35 35 #include <asm/thread_info.h> 36 + #include <asm/stacktrace.h> 36 37 #include <asm/mach/time.h> 37 38 38 39 /* ··· 56 55 #ifdef CONFIG_SMP 57 56 unsigned long profile_pc(struct pt_regs *regs) 58 57 { 59 - unsigned long fp, pc = instruction_pointer(regs); 58 + struct stackframe frame; 60 59 61 - if (in_lock_functions(pc)) { 62 - fp = regs->ARM_fp; 63 - pc = ((unsigned long *)fp)[-1]; 64 - } 60 + if (!in_lock_functions(regs->ARM_pc)) 61 + return regs->ARM_pc; 65 62 66 - return pc; 63 + frame.fp = regs->ARM_fp; 64 + frame.sp = regs->ARM_sp; 65 + frame.lr = regs->ARM_lr; 66 + frame.pc = regs->ARM_pc; 67 + do { 68 + int ret = unwind_frame(&frame); 69 + if (ret < 0) 70 + return 0; 71 + } while (in_lock_functions(frame.pc)); 72 + 73 + return frame.pc; 67 74 } 68 75 EXPORT_SYMBOL(profile_pc); 69 76 #endif
+8 -6
arch/arm/oprofile/backtrace.c
··· 18 18 #include <linux/mm.h> 19 19 #include <linux/uaccess.h> 20 20 #include <asm/ptrace.h> 21 - 22 - #include "../kernel/stacktrace.h" 21 + #include <asm/stacktrace.h> 23 22 24 23 static int report_trace(struct stackframe *frame, void *d) 25 24 { 26 25 unsigned int *depth = d; 27 26 28 27 if (*depth) { 29 - oprofile_add_trace(frame->lr); 28 + oprofile_add_trace(frame->pc); 30 29 (*depth)--; 31 30 } 32 31 ··· 69 70 struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; 70 71 71 72 if (!user_mode(regs)) { 72 - unsigned long base = ((unsigned long)regs) & ~(THREAD_SIZE - 1); 73 - walk_stackframe(regs->ARM_fp, base, base + THREAD_SIZE, 74 - report_trace, &depth); 73 + struct stackframe frame; 74 + frame.fp = regs->ARM_fp; 75 + frame.sp = regs->ARM_sp; 76 + frame.lr = regs->ARM_lr; 77 + frame.pc = regs->ARM_pc; 78 + walk_stackframe(&frame, report_trace, &depth); 75 79 return; 76 80 } 77 81