[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: Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Atsushi Nemoto 2006-02-08 01:48:03 +09:00 коммит произвёл Ralf Baechle
Родитель 1bdfd0d963
Коммит 6307751989
1 изменённых файлов: 77 добавлений и 81 удалений

Просмотреть файл

@ -25,6 +25,7 @@
#include <linux/a.out.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/kallsyms.h>
#include <asm/abi.h>
#include <asm/bootinfo.h>
@ -272,46 +273,19 @@ long kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
static struct mips_frame_info {
void *func;
int omit_fp; /* compiled without fno-omit-frame-pointer */
int frame_offset;
unsigned long func_size;
int frame_size;
int pc_offset;
} schedule_frame, mfinfo[] = {
{ schedule, 0 }, /* must be first */
/* 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 },
};
} *schedule_frame, mfinfo[64];
static int mfinfo_num;
static int mips_frame_info_initialized;
static int __init get_frame_info(struct mips_frame_info *info)
{
int i;
void *func = info->func;
union mips_instruction *ip = (union mips_instruction *)func;
info->pc_offset = -1;
info->frame_offset = info->omit_fp ? 0 : -1;
info->frame_size = 0;
for (i = 0; i < 128; i++, ip++) {
/* if jal, jalr, jr, stop. */
if (ip->j_format.opcode == jal_op ||
@ -320,6 +294,23 @@ static int __init get_frame_info(struct mips_frame_info *info)
ip->r_format.func == jr_op)))
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 (
#ifdef CONFIG_32BIT
ip->i_format.opcode == sw_op &&
@ -327,31 +318,20 @@ static int __init get_frame_info(struct mips_frame_info *info)
#ifdef CONFIG_64BIT
ip->i_format.opcode == sd_op &&
#endif
ip->i_format.rs == 29)
{
ip->i_format.rs == 29 &&
ip->i_format.rt == 31) {
/* sw / sd $ra, offset($sp) */
if (ip->i_format.rt == 31) {
if (info->pc_offset != -1)
continue;
info->pc_offset =
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);
info->pc_offset = -1;
info->frame_offset = -1;
return -1;
info->frame_size = 0;
}
return 0;
@ -359,25 +339,36 @@ static int __init get_frame_info(struct mips_frame_info *info)
static int __init frame_info_init(void)
{
int i, found;
for (i = 0; i < ARRAY_SIZE(mfinfo); i++)
if (get_frame_info(&mfinfo[i]))
return -1;
schedule_frame = mfinfo[0];
/* bubble sort */
do {
struct mips_frame_info tmp;
found = 0;
for (i = 1; i < ARRAY_SIZE(mfinfo); i++) {
if (mfinfo[i-1].func > mfinfo[i].func) {
tmp = mfinfo[i];
mfinfo[i] = mfinfo[i-1];
mfinfo[i-1] = tmp;
found = 1;
int i;
#ifdef CONFIG_KALLSYMS
char *modname;
char namebuf[KSYM_NAME_LEN + 1];
unsigned long start, size, ofs;
extern char __sched_text_start[], __sched_text_end[];
extern char __lock_text_start[], __lock_text_end[];
start = (unsigned long)__sched_text_start;
for (i = 0; i < ARRAY_SIZE(mfinfo); i++) {
if (start == (unsigned long)schedule)
schedule_frame = &mfinfo[i];
if (!kallsyms_lookup(start, &size, &ofs, &modname, namebuf))
break;
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);
mips_frame_info_initialized = 1;
#else
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;
}
@ -394,47 +385,52 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
if (t->reg31 == (unsigned long) ret_from_fork)
return t->reg31;
if (schedule_frame.pc_offset < 0)
if (!schedule_frame || schedule_frame->pc_offset < 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 ... */
unsigned long get_wchan(struct task_struct *p)
{
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)
return 0;
stack_page = (unsigned long)task_stack_page(p);
if (!stack_page || !mips_frame_info_initialized)
if (!stack_page || !mfinfo_num)
return 0;
pc = thread_saved_pc(p);
#ifdef CONFIG_KALLSYMS
if (!in_sched_functions(pc))
return pc;
frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
frame = p->thread.reg29 + schedule_frame->frame_size;
do {
int i;
if (frame < stack_page || frame > stack_page + THREAD_SIZE - 32)
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)
break;
}
if (i < 0)
break;
if (mfinfo[i].omit_fp)
break;
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));
#endif
return pc;
}