summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel
diff options
context:
space:
mode:
authorAtsushi Nemoto <anemo@mba.ocn.ne.jp>2006-07-29 23:27:20 +0900
committerRalf Baechle <ralf@linux-mips.org>2006-09-27 13:37:06 +0100
commitf66686f70a2a61e53ee8c2284f75ca342e4c0dc8 (patch)
tree881e98d7255770314dde60109a316cf9f84522b2 /arch/mips/kernel
parent79495d876c2074af5552a0c4b7aea600c2320e83 (diff)
downloadtalos-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.c66
-rw-r--r--arch/mips/kernel/traps.c98
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(&regs);
+ }
+ }
+ show_stacktrace(task, &regs);
}
/*
@@ -146,6 +212,14 @@ void dump_stack(void)
{
unsigned long stack;
+#ifdef CONFIG_KALLSYMS
+ if (!raw_show_trace) {
+ struct pt_regs regs;
+ prepare_frametrace(&regs);
+ show_frametrace(current, &regs);
+ 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");
}
OpenPOWER on IntegriCloud