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 /arch/mips/kernel/process.c | |
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>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r-- | arch/mips/kernel/process.c | 35 |
1 files changed, 22 insertions, 13 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 |