summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c66
1 files changed, 55 insertions, 11 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