diff options
author | Atsushi Nemoto <anemo@mba.ocn.ne.jp> | 2006-07-29 23:27:20 +0900 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2006-09-27 13:37:06 +0100 |
commit | f66686f70a2a61e53ee8c2284f75ca342e4c0dc8 (patch) | |
tree | 881e98d7255770314dde60109a316cf9f84522b2 /arch/mips/kernel | |
parent | 79495d876c2074af5552a0c4b7aea600c2320e83 (diff) | |
download | talos-op-linux-f66686f70a2a61e53ee8c2284f75ca342e4c0dc8.tar.gz talos-op-linux-f66686f70a2a61e53ee8c2284f75ca342e4c0dc8.zip |
[MIPS] dump_stack() based on prologue code analysis
Instead of dump all possible address in the stack, unwind the stack frame
based on prologue code analysis, as like as get_wchan() does. While the
code analysis might fail for some reason, there is a new kernel option
"raw_show_trace" to disable this feature.
Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r-- | arch/mips/kernel/process.c | 66 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 98 |
2 files changed, 141 insertions, 23 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 7ab67f786bfe..8709a46a45c1 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -281,7 +281,7 @@ static struct mips_frame_info { } *schedule_frame, mfinfo[64]; static int mfinfo_num; -static int __init get_frame_info(struct mips_frame_info *info) +static int get_frame_info(struct mips_frame_info *info) { int i; void *func = info->func; @@ -329,14 +329,12 @@ static int __init get_frame_info(struct mips_frame_info *info) ip->i_format.simmediate / sizeof(long); } } - if (info->pc_offset == -1 || info->frame_size == 0) { - if (func == schedule) - printk("Can't analyze prologue code at %p\n", func); - info->pc_offset = -1; - info->frame_size = 0; - } - - return 0; + if (info->frame_size && info->pc_offset >= 0) /* nested */ + return 0; + if (info->pc_offset < 0) /* leaf */ + return 1; + /* prologue seems boggus... */ + return -1; } static int __init frame_info_init(void) @@ -367,8 +365,15 @@ static int __init frame_info_init(void) mfinfo[0].func = schedule; schedule_frame = &mfinfo[0]; #endif - for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) - get_frame_info(&mfinfo[i]); + for (i = 0; i < ARRAY_SIZE(mfinfo) && mfinfo[i].func; i++) { + struct mips_frame_info *info = &mfinfo[i]; + if (get_frame_info(info)) { + /* leaf or unknown */ + if (info->func == schedule) + printk("Can't analyze prologue code at %p\n", + info->func); + } + } mfinfo_num = i; return 0; @@ -427,6 +432,8 @@ unsigned long get_wchan(struct task_struct *p) if (i < 0) break; + if (mfinfo[i].pc_offset < 0) + break; pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; if (!mfinfo[i].frame_size) break; @@ -437,3 +444,40 @@ unsigned long get_wchan(struct task_struct *p) return pc; } +#ifdef CONFIG_KALLSYMS +/* used by show_frametrace() */ +unsigned long unwind_stack(struct task_struct *task, + unsigned long **sp, unsigned long pc) +{ + unsigned long stack_page; + struct mips_frame_info info; + char *modname; + char namebuf[KSYM_NAME_LEN + 1]; + unsigned long size, ofs; + + stack_page = (unsigned long)task_stack_page(task); + if (!stack_page) + return 0; + + if (!kallsyms_lookup(pc, &size, &ofs, &modname, namebuf)) + return 0; + if (ofs == 0) + return 0; + + info.func = (void *)(pc - ofs); + info.func_size = ofs; /* analyze from start to ofs */ + if (get_frame_info(&info)) { + /* leaf or unknown */ + *sp += info.frame_size / sizeof(long); + return 0; + } + if ((unsigned long)*sp < stack_page || + (unsigned long)*sp + info.frame_size / sizeof(long) > + stack_page + THREAD_SIZE - 32) + return 0; + + pc = (*sp)[info.pc_offset]; + *sp += info.frame_size / sizeof(long); + return pc; +} +#endif diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index c6f70467e750..7aa9dfc57b81 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -98,24 +98,53 @@ static void show_trace(unsigned long *stack) printk("\n"); } +#ifdef CONFIG_KALLSYMS +static int raw_show_trace; +static int __init set_raw_show_trace(char *str) +{ + raw_show_trace = 1; + return 1; +} +__setup("raw_show_trace", set_raw_show_trace); + +extern unsigned long unwind_stack(struct task_struct *task, + unsigned long **sp, unsigned long pc); +static void show_frametrace(struct task_struct *task, struct pt_regs *regs) +{ + const int field = 2 * sizeof(unsigned long); + unsigned long *stack = (long *)regs->regs[29]; + unsigned long pc = regs->cp0_epc; + int top = 1; + + if (raw_show_trace || !__kernel_text_address(pc)) { + show_trace(stack); + return; + } + printk("Call Trace:\n"); + while (__kernel_text_address(pc)) { + printk(" [<%0*lx>] ", field, pc); + print_symbol("%s\n", pc); + pc = unwind_stack(task, &stack, pc); + if (top && pc == 0) + pc = regs->regs[31]; /* leaf? */ + top = 0; + } + printk("\n"); +} +#else +#define show_frametrace(task, r) show_trace((long *)(r)->regs[29]); +#endif + /* * This routine abuses get_user()/put_user() to reference pointers * with at least a bit of error checking ... */ -void show_stack(struct task_struct *task, unsigned long *sp) +static void show_stacktrace(struct task_struct *task, struct pt_regs *regs) { const int field = 2 * sizeof(unsigned long); long stackdata; int i; - unsigned long *stack; - - if (!sp) { - if (task && task != current) - sp = (unsigned long *) task->thread.reg29; - else - sp = (unsigned long *) &sp; - } - stack = sp; + unsigned long *sp = (unsigned long *)regs->regs[29]; printk("Stack :"); i = 0; @@ -136,7 +165,44 @@ void show_stack(struct task_struct *task, unsigned long *sp) i++; } printk("\n"); - show_trace(stack); + show_frametrace(task, regs); +} + +static noinline void prepare_frametrace(struct pt_regs *regs) +{ + __asm__ __volatile__( + "1: la $2, 1b\n\t" +#ifdef CONFIG_64BIT + "sd $2, %0\n\t" + "sd $29, %1\n\t" + "sd $31, %2\n\t" +#else + "sw $2, %0\n\t" + "sw $29, %1\n\t" + "sw $31, %2\n\t" +#endif + : "=m" (regs->cp0_epc), + "=m" (regs->regs[29]), "=m" (regs->regs[31]) + : : "memory"); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + struct pt_regs regs; + if (sp) { + regs.regs[29] = (unsigned long)sp; + regs.regs[31] = 0; + regs.cp0_epc = 0; + } else { + if (task && task != current) { + regs.regs[29] = task->thread.reg29; + regs.regs[31] = 0; + regs.cp0_epc = task->thread.reg31; + } else { + prepare_frametrace(®s); + } + } + show_stacktrace(task, ®s); } /* @@ -146,6 +212,14 @@ void dump_stack(void) { unsigned long stack; +#ifdef CONFIG_KALLSYMS + if (!raw_show_trace) { + struct pt_regs regs; + prepare_frametrace(®s); + show_frametrace(current, ®s); + return; + } +#endif show_trace(&stack); } @@ -265,7 +339,7 @@ void show_registers(struct pt_regs *regs) print_modules(); printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n", current->comm, current->pid, current_thread_info(), current); - show_stack(current, (long *) regs->regs[29]); + show_stacktrace(current, regs); show_code((unsigned int *) regs->cp0_epc); printk("\n"); } |