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 | /* |