Commit 63077519 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle

[MIPS] Rewrite get_wchan and its helper functions using kallsyms_lookup.

    
Implement get_wchan() and frame_info_init() using kallsyms_lookup().
This fixes problem with static sched/lock functions and mfinfo[]
maintenance issue.  If CONFIG_KALLSYMS was disabled, get_wchan() just
returns thread_saved_pc() value.
    
Also unwind stackframe based on "addiu sp,-imm" analysis instead of
frame pointer.  This fixes problem with functions compiled without
-fomit-frame-pointer.
Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 1bdfd0d9
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/a.out.h> #include <linux/a.out.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/kallsyms.h>
#include <asm/abi.h> #include <asm/abi.h>
#include <asm/bootinfo.h> #include <asm/bootinfo.h>
...@@ -272,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) ...@@ -272,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
static struct mips_frame_info { static struct mips_frame_info {
void *func; void *func;
int omit_fp; /* compiled without fno-omit-frame-pointer */ unsigned long func_size;
int frame_offset; int frame_size;
int pc_offset; int pc_offset;
} schedule_frame, mfinfo[] = { } *schedule_frame, mfinfo[64];
{ schedule, 0 }, /* must be first */ static int mfinfo_num;
/* arch/mips/kernel/semaphore.c */
{ __down, 1 },
{ __down_interruptible, 1 },
/* kernel/sched.c */
#ifdef CONFIG_PREEMPT
{ preempt_schedule, 0 },
#endif
{ wait_for_completion, 0 },
{ interruptible_sleep_on, 0 },
{ interruptible_sleep_on_timeout, 0 },
{ sleep_on, 0 },
{ sleep_on_timeout, 0 },
{ yield, 0 },
{ io_schedule, 0 },
{ io_schedule_timeout, 0 },
#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT)
{ __preempt_spin_lock, 0 },
{ __preempt_write_lock, 0 },
#endif
/* kernel/timer.c */
{ schedule_timeout, 1 },
/* { nanosleep_restart, 1 }, */
/* lib/rwsem-spinlock.c */
{ __down_read, 1 },
{ __down_write, 1 },
};
static int mips_frame_info_initialized;
static int __init get_frame_info(struct mips_frame_info *info) static int __init get_frame_info(struct mips_frame_info *info)
{ {
int i; int i;
void *func = info->func; void *func = info->func;
union mips_instruction *ip = (union mips_instruction *)func; union mips_instruction *ip = (union mips_instruction *)func;
info->pc_offset = -1; info->pc_offset = -1;
info->frame_offset = info->omit_fp ? 0 : -1; info->frame_size = 0;
for (i = 0; i < 128; i++, ip++) { for (i = 0; i < 128; i++, ip++) {
/* if jal, jalr, jr, stop. */ /* if jal, jalr, jr, stop. */
if (ip->j_format.opcode == jal_op || if (ip->j_format.opcode == jal_op ||
...@@ -320,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info) ...@@ -320,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info)
ip->r_format.func == jr_op))) ip->r_format.func == jr_op)))
break; break;
if (info->func_size && i >= info->func_size / 4)
break;
if (
#ifdef CONFIG_32BIT
ip->i_format.opcode == addiu_op &&
#endif
#ifdef CONFIG_64BIT
ip->i_format.opcode == daddiu_op &&
#endif
ip->i_format.rs == 29 &&
ip->i_format.rt == 29) {
/* addiu/daddiu sp,sp,-imm */
if (info->frame_size)
continue;
info->frame_size = - ip->i_format.simmediate;
}
if ( if (
#ifdef CONFIG_32BIT #ifdef CONFIG_32BIT
ip->i_format.opcode == sw_op && ip->i_format.opcode == sw_op &&
...@@ -327,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info) ...@@ -327,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info)
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
ip->i_format.opcode == sd_op && ip->i_format.opcode == sd_op &&
#endif #endif
ip->i_format.rs == 29) ip->i_format.rs == 29 &&
{ ip->i_format.rt == 31) {
/* sw / sd $ra, offset($sp) */ /* sw / sd $ra, offset($sp) */
if (ip->i_format.rt == 31) {
if (info->pc_offset != -1) if (info->pc_offset != -1)
continue; continue;
info->pc_offset = info->pc_offset =
ip->i_format.simmediate / sizeof(long); ip->i_format.simmediate / sizeof(long);
} }
/* sw / sd $s8, offset($sp) */
if (ip->i_format.rt == 30) {
//#if 0 /* gcc 3.4 does aggressive optimization... */
if (info->frame_offset != -1)
continue;
//#endif
info->frame_offset =
ip->i_format.simmediate / sizeof(long);
}
}
} }
if (info->pc_offset == -1 || info->frame_offset == -1) { if (info->pc_offset == -1 || info->frame_size == 0) {
if (func == schedule)
printk("Can't analyze prologue code at %p\n", func); printk("Can't analyze prologue code at %p\n", func);
info->pc_offset = -1; info->pc_offset = -1;
info->frame_offset = -1; info->frame_size = 0;
return -1;
} }
return 0; return 0;
...@@ -359,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info) ...@@ -359,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info)
static int __init frame_info_init(void) static int __init frame_info_init(void)
{ {
int i, found; int i;
for (i = 0; i < ARRAY_SIZE(mfinfo); i++) #ifdef CONFIG_KALLSYMS
if (get_frame_info(&mfinfo[i])) char *modname;
return -1; char namebuf[KSYM_NAME_LEN + 1];
schedule_frame = mfinfo[0]; unsigned long start, size, ofs;
/* bubble sort */ extern char __sched_text_start[], __sched_text_end[];
do { extern char __lock_text_start[], __lock_text_end[];
struct mips_frame_info tmp;
found = 0; start = (unsigned long)__sched_text_start;
for (i = 1; i < ARRAY_SIZE(mfinfo); i++) { for (i = 0; i < ARRAY_SIZE(mfinfo); i++) {
if (mfinfo[i-1].func > mfinfo[i].func) { if (start == (unsigned long)schedule)
tmp = mfinfo[i]; schedule_frame = &mfinfo[i];
mfinfo[i] = mfinfo[i-1]; if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf))
mfinfo[i-1] = tmp; break;
found = 1; mfinfo[i].func = (void *)(start + ofs);
} mfinfo[i].func_size = size;
start += size - ofs;
if (start >= (unsigned long)__lock_text_end)
break;
if (start == (unsigned long)__sched_text_end)
start = (unsigned long)__lock_text_start;
} }
} while (found); #else
mips_frame_info_initialized = 1; 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]);
mfinfo_num = i;
return 0; return 0;
} }
...@@ -394,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk) ...@@ -394,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
if (t->reg31 == (unsigned long) ret_from_fork) if (t->reg31 == (unsigned long) ret_from_fork)
return t->reg31; return t->reg31;
if (schedule_frame.pc_offset < 0) if (!schedule_frame || schedule_frame->pc_offset < 0)
return 0; return 0;
return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; return ((unsigned long *)t->reg29)[schedule_frame->pc_offset];
} }
/* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */ /* get_wchan - a maintenance nightmare^W^Wpain in the ass ... */
unsigned long get_wchan(struct task_struct *p) unsigned long get_wchan(struct task_struct *p)
{ {
unsigned long stack_page; unsigned long stack_page;
unsigned long frame, pc; unsigned long pc;
#ifdef CONFIG_KALLSYMS
unsigned long frame;
#endif
if (!p || p == current || p->state == TASK_RUNNING) if (!p || p == current || p->state == TASK_RUNNING)
return 0; return 0;
stack_page = (unsigned long)task_stack_page(p); stack_page = (unsigned long)task_stack_page(p);
if (!stack_page || !mips_frame_info_initialized) if (!stack_page || !mfinfo_num)
return 0; return 0;
pc = thread_saved_pc(p); pc = thread_saved_pc(p);
#ifdef CONFIG_KALLSYMS
if (!in_sched_functions(pc)) if (!in_sched_functions(pc))
return pc; return pc;
frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; frame = p->thread.reg29 + schedule_frame->frame_size;
do { do {
int i; int i;
if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32) if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32)
return 0; return 0;
for (i = ARRAY_SIZE(mfinfo) - 1; i >= 0; i--) { for (i = mfinfo_num - 1; i >= 0; i--) {
if (pc >= (unsigned long) mfinfo[i].func) if (pc >= (unsigned long) mfinfo[i].func)
break; break;
} }
if (i < 0) if (i < 0)
break; break;
if (mfinfo[i].omit_fp)
break;
pc = ((unsigned long *)frame)[mfinfo[i].pc_offset]; pc = ((unsigned long *)frame)[mfinfo[i].pc_offset];
frame = ((unsigned long *)frame)[mfinfo[i].frame_offset]; if (!mfinfo[i].frame_size)
break;
frame += mfinfo[i].frame_size;
} while (in_sched_functions(pc)); } while (in_sched_functions(pc));
#endif
return pc; return pc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment