diff options
| author | Franck Bui-Huu <vagabon.xyz@gmail.com> | 2006-08-03 03:29:21 -0400 |
|---|---|---|
| committer | Ralf Baechle <ralf@linux-mips.org> | 2006-09-27 08:37:29 -0400 |
| commit | 4d157d5eac29d7d5559fdcabf20f3961bc5cb3e7 (patch) | |
| tree | 82662abbe473e5bd0be973e2a8ceb8b63082da55 | |
| parent | 0cceb4aa9acf6192a5f02134e764b1feeea8b9de (diff) | |
[MIPS] Improve unwind_stack()
This patch allows unwind_stack() to return ra for leaf function.
But it tries to detects cases where get_frame_info() wrongly
consider nested function as a leaf one.
It also pass 'unsinged long *sp' instead of 'unsigned long **sp'
as second parameter. The code looks cleaner.
Signed-off-by: Franck Bui-Huu <vagabon.xyz@gmail.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
| -rw-r--r-- | arch/mips/kernel/process.c | 35 | ||||
| -rw-r--r-- | arch/mips/kernel/traps.c | 24 |
2 files changed, 34 insertions, 25 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 309bfa4a1520..951bf9ca3ce9 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c | |||
| @@ -448,15 +448,16 @@ unsigned long get_wchan(struct task_struct *p) | |||
| 448 | } | 448 | } |
| 449 | 449 | ||
| 450 | #ifdef CONFIG_KALLSYMS | 450 | #ifdef CONFIG_KALLSYMS |
| 451 | /* used by show_frametrace() */ | 451 | /* used by show_backtrace() */ |
| 452 | unsigned long unwind_stack(struct task_struct *task, | 452 | unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, |
| 453 | unsigned long **sp, unsigned long pc) | 453 | unsigned long pc, unsigned long ra) |
| 454 | { | 454 | { |
| 455 | unsigned long stack_page; | 455 | unsigned long stack_page; |
| 456 | struct mips_frame_info info; | 456 | struct mips_frame_info info; |
| 457 | char *modname; | 457 | char *modname; |
| 458 | char namebuf[KSYM_NAME_LEN + 1]; | 458 | char namebuf[KSYM_NAME_LEN + 1]; |
| 459 | unsigned long size, ofs; | 459 | unsigned long size, ofs; |
| 460 | int leaf; | ||
| 460 | 461 | ||
| 461 | stack_page = (unsigned long)task_stack_page(task); | 462 | stack_page = (unsigned long)task_stack_page(task); |
| 462 | if (!stack_page) | 463 | if (!stack_page) |
| @@ -469,18 +470,26 @@ unsigned long unwind_stack(struct task_struct *task, | |||
| 469 | 470 | ||
| 470 | info.func = (void *)(pc - ofs); | 471 | info.func = (void *)(pc - ofs); |
| 471 | info.func_size = ofs; /* analyze from start to ofs */ | 472 | info.func_size = ofs; /* analyze from start to ofs */ |
| 472 | if (get_frame_info(&info)) { | 473 | leaf = get_frame_info(&info); |
| 473 | /* leaf or unknown */ | 474 | if (leaf < 0) |
| 474 | *sp += info.frame_size / sizeof(long); | ||
| 475 | return 0; | 475 | return 0; |
| 476 | } | 476 | |
| 477 | if ((unsigned long)*sp < stack_page || | 477 | if (*sp < stack_page || |
| 478 | (unsigned long)*sp + info.frame_size / sizeof(long) > | 478 | *sp + info.frame_size > stack_page + THREAD_SIZE - 32) |
| 479 | stack_page + THREAD_SIZE - 32) | ||
| 480 | return 0; | 479 | return 0; |
| 481 | 480 | ||
| 482 | pc = (*sp)[info.pc_offset]; | 481 | if (leaf) |
| 483 | *sp += info.frame_size / sizeof(long); | 482 | /* |
| 484 | return pc; | 483 | * For some extreme cases, get_frame_info() can |
| 484 | * consider wrongly a nested function as a leaf | ||
| 485 | * one. In that cases avoid to return always the | ||
| 486 | * same value. | ||
| 487 | */ | ||
| 488 | pc = pc != ra ? ra : 0; | ||
| 489 | else | ||
| 490 | pc = ((unsigned long *)(*sp))[info.pc_offset]; | ||
| 491 | |||
| 492 | *sp += info.frame_size; | ||
| 493 | return __kernel_text_address(pc) ? pc : 0; | ||
| 485 | } | 494 | } |
| 486 | #endif | 495 | #endif |
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 303f00843021..ab77034921c4 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
| @@ -74,8 +74,9 @@ void (*board_ejtag_handler_setup)(void); | |||
| 74 | void (*board_bind_eic_interrupt)(int irq, int regset); | 74 | void (*board_bind_eic_interrupt)(int irq, int regset); |
| 75 | 75 | ||
| 76 | 76 | ||
| 77 | static void show_raw_backtrace(unsigned long *sp) | 77 | static void show_raw_backtrace(unsigned long reg29) |
| 78 | { | 78 | { |
| 79 | unsigned long *sp = (unsigned long *)reg29; | ||
| 79 | unsigned long addr; | 80 | unsigned long addr; |
| 80 | 81 | ||
| 81 | printk("Call Trace:"); | 82 | printk("Call Trace:"); |
| @@ -99,30 +100,29 @@ static int __init set_raw_show_trace(char *str) | |||
| 99 | } | 100 | } |
| 100 | __setup("raw_show_trace", set_raw_show_trace); | 101 | __setup("raw_show_trace", set_raw_show_trace); |
| 101 | 102 | ||
| 102 | extern unsigned long unwind_stack(struct task_struct *task, | 103 | extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp, |
| 103 | unsigned long **sp, unsigned long pc); | 104 | unsigned long pc, unsigned long ra); |
| 105 | |||
| 104 | static void show_backtrace(struct task_struct *task, struct pt_regs *regs) | 106 | static void show_backtrace(struct task_struct *task, struct pt_regs *regs) |
| 105 | { | 107 | { |
| 106 | unsigned long *sp = (long *)regs->regs[29]; | 108 | unsigned long sp = regs->regs[29]; |
| 109 | unsigned long ra = regs->regs[31]; | ||
| 107 | unsigned long pc = regs->cp0_epc; | 110 | unsigned long pc = regs->cp0_epc; |
| 108 | int top = 1; | ||
| 109 | 111 | ||
| 110 | if (raw_show_trace || !__kernel_text_address(pc)) { | 112 | if (raw_show_trace || !__kernel_text_address(pc)) { |
| 111 | show_raw_backtrace(sp); | 113 | show_raw_backtrace(sp); |
| 112 | return; | 114 | return; |
| 113 | } | 115 | } |
| 114 | printk("Call Trace:\n"); | 116 | printk("Call Trace:\n"); |
| 115 | while (__kernel_text_address(pc)) { | 117 | do { |
| 116 | print_ip_sym(pc); | 118 | print_ip_sym(pc); |
| 117 | pc = unwind_stack(task, &sp, pc); | 119 | pc = unwind_stack(task, &sp, pc, ra); |
| 118 | if (top && pc == 0) | 120 | ra = 0; |
| 119 | pc = regs->regs[31]; /* leaf? */ | 121 | } while (pc); |
| 120 | top = 0; | ||
| 121 | } | ||
| 122 | printk("\n"); | 122 | printk("\n"); |
| 123 | } | 123 | } |
| 124 | #else | 124 | #else |
| 125 | #define show_backtrace(task, r) show_raw_backtrace((long *)(r)->regs[29]); | 125 | #define show_backtrace(task, r) show_raw_backtrace((r)->regs[29]); |
| 126 | #endif | 126 | #endif |
| 127 | 127 | ||
| 128 | /* | 128 | /* |
