>>>>> On Fri, 02 May 2003 23:04:46 +0900 (JST), Atsushi Nemoto <anemo@mba.ocn.ne.jp> said: anemo> There are some magic constants about stack layout in anemo> thread_saved_pc() and get_wchan(). Also I think get_wchan() anemo> for 32bit kernel is something wrong. (it is not easy to explain anemo> HOW wrong...) anemo> This patch (for 2.4 cvs) get rid of these magic constants by anemo> analyzing kernel codes in runtime. And this is (untested) 2.5 version of the patch. diff -ur linux-mips-2.5-cvs/arch/mips/kernel/process.c linux-2.5.new/arch/mips/kernel/process.c --- linux-mips-2.5-cvs/arch/mips/kernel/process.c Fri Jan 10 04:16:50 2003 +++ linux-2.5.new/arch/mips/kernel/process.c Sat May 3 21:51:51 2003 @@ -18,6 +18,8 @@ #include <linux/sys.h> #include <linux/user.h> #include <linux/a.out.h> +#include <linux/init.h> +#include <linux/completion.h> #include <asm/bootinfo.h> #include <asm/cpu.h> @@ -31,6 +33,7 @@ #include <asm/io.h> #include <asm/elf.h> #include <asm/isadep.h> +#include <asm/inst.h> /* * We use this if we don't have any better idle routine.. @@ -172,6 +175,65 @@ return retval; } +struct mips_frame_info { + int frame_offset; + int pc_offset; +}; +static struct mips_frame_info schedule_frame; +static struct mips_frame_info schedule_timeout_frame; +static struct mips_frame_info sleep_on_frame; +static struct mips_frame_info sleep_on_timeout_frame; +static struct mips_frame_info wait_for_completion_frame; +static int mips_frame_info_initialized; +static int __init get_frame_info(struct mips_frame_info *info, void *func) +{ + int i; + union mips_instruction *ip = (union mips_instruction *)func; + info->pc_offset = -1; + info->frame_offset = -1; + for (i = 0; i < 128; i++, ip++) { + /* if jal, jalr, jr, stop. */ + if (ip->j_format.opcode == jal_op || + (ip->r_format.opcode == spec_op && + (ip->r_format.func == jalr_op || + ip->r_format.func == jr_op))) + break; + if (ip->i_format.opcode == sw_op && + ip->i_format.rs == 29) { + /* sw $ra, offset($sp) */ + if (ip->i_format.rt == 31) { + if (info->pc_offset != -1) + break; + info->pc_offset = + ip->i_format.simmediate / sizeof(long); + } + /* sw $s8, offset($sp) */ + if (ip->i_format.rt == 30) { + if (info->frame_offset != -1) + break; + info->frame_offset = + ip->i_format.simmediate / sizeof(long); + } + } + } + if (info->pc_offset == -1 || info->frame_offset == -1) { + printk("Can't analize prologue code at %p\n", func); + info->pc_offset = -1; + info->frame_offset = -1; + return -1; + } + return 0; +} +void __init frame_info_init(void) +{ + mips_frame_info_initialized = + !get_frame_info(&schedule_frame, schedule) && + !get_frame_info(&schedule_timeout_frame, schedule_timeout) && + !get_frame_info(&sleep_on_frame, sleep_on) && + !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) && + !get_frame_info(&wait_for_completion_frame, wait_for_completion); +} + unsigned long thread_saved_pc(struct thread_struct *t) { extern void ret_from_fork(void); @@ -180,7 +242,9 @@ if (t->reg31 == (unsigned long) ret_from_fork) return t->reg31; - return ((unsigned long *)t->reg29)[13]; + if (schedule_frame.pc_offset < 0) + return 0; + return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; } /* @@ -199,6 +263,8 @@ if (!p || p == current || p->state == TASK_RUNNING) return 0; + if (!mips_frame_info_initialized) + return 0; pc = thread_saved_pc(&p->thread); if (pc < first_sched || pc >= last_sched) { return pc; @@ -212,29 +278,33 @@ goto schedule_timeout_caller; if (pc >= (unsigned long)interruptible_sleep_on) goto schedule_caller; - /* Fall through */ + if (pc >= (unsigned long)wait_for_completion) + goto schedule_caller; + goto schedule_timeout_caller; schedule_caller: - pc = ((unsigned long *)p->thread.reg30)[13]; - + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; + if (pc >= (unsigned long) sleep_on) + pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset]; + else + pc = ((unsigned long *)frame)[wait_for_completion_frame.pc_offset]; return pc; schedule_timeout_caller: /* * The schedule_timeout frame */ - frame = ((unsigned long *)p->thread.reg30)[13]; + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; /* * frame now points to sleep_on_timeout's frame */ - frame = ((unsigned long *)frame)[9]; - pc = ((unsigned long *)frame)[10]; + pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset]; if (pc >= first_sched && pc < last_sched) { - /* schedule_timeout called by interruptible_sleep_on_timeout */ - frame = ((unsigned long *)frame)[9]; - pc = ((unsigned long *)frame)[10]; + /* schedule_timeout called by [interruptible_]sleep_on_timeout */ + frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset]; + pc = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset]; } return pc; diff -ur linux-mips-2.5-cvs/arch/mips/kernel/setup.c linux-2.5.new/arch/mips/kernel/setup.c --- linux-mips-2.5-cvs/arch/mips/kernel/setup.c Sat Apr 12 02:28:23 2003 +++ linux-2.5.new/arch/mips/kernel/setup.c Sat May 3 21:25:54 2003 @@ -493,7 +493,9 @@ void au1000_setup(void); void au1100_setup(void); void au1500_setup(void); + void frame_info_init(void); + frame_info_init(); #ifdef CONFIG_BLK_DEV_FD fd_ops = &no_fd_ops; #endif diff -ur linux-mips-2.5-cvs/arch/mips64/kernel/process.c linux-2.5.new/arch/mips64/kernel/process.c --- linux-mips-2.5-cvs/arch/mips64/kernel/process.c Fri Feb 21 04:47:25 2003 +++ linux-2.5.new/arch/mips64/kernel/process.c Sat May 3 21:51:45 2003 @@ -19,6 +19,8 @@ #include <linux/sys.h> #include <linux/user.h> #include <linux/a.out.h> +#include <linux/init.h> +#include <linux/completion.h> #include <asm/bootinfo.h> #include <asm/pgtable.h> @@ -31,6 +33,7 @@ #include <asm/elf.h> #include <asm/cpu.h> #include <asm/fpu.h> +#include <asm/inst.h> /* * We use this if we don't have any better idle routine.. @@ -163,6 +166,65 @@ return retval; } +struct mips_frame_info { + int frame_offset; + int pc_offset; +}; +static struct mips_frame_info schedule_frame; +static struct mips_frame_info schedule_timeout_frame; +static struct mips_frame_info sleep_on_frame; +static struct mips_frame_info sleep_on_timeout_frame; +static struct mips_frame_info wait_for_completion_frame; +static int mips_frame_info_initialized; +static int __init get_frame_info(struct mips_frame_info *info, void *func) +{ + int i; + union mips_instruction *ip = (union mips_instruction *)func; + info->pc_offset = -1; + info->frame_offset = -1; + for (i = 0; i < 128; i++, ip++) { + /* if jal, jalr, jr, stop. */ + if (ip->j_format.opcode == jal_op || + (ip->r_format.opcode == spec_op && + (ip->r_format.func == jalr_op || + ip->r_format.func == jr_op))) + break; + if (ip->i_format.opcode == sd_op && + ip->i_format.rs == 29) { + /* sd $ra, offset($sp) */ + if (ip->i_format.rt == 31) { + if (info->pc_offset != -1) + break; + info->pc_offset = + ip->i_format.simmediate / sizeof(long); + } + /* sd $s8, offset($sp) */ + if (ip->i_format.rt == 30) { + if (info->frame_offset != -1) + break; + info->frame_offset = + ip->i_format.simmediate / sizeof(long); + } + } + } + if (info->pc_offset == -1 || info->frame_offset == -1) { + printk("Can't analize prologue code at %p\n", func); + info->pc_offset = -1; + info->frame_offset = -1; + return -1; + } + return 0; +} +void __init frame_info_init(void) +{ + mips_frame_info_initialized = + !get_frame_info(&schedule_frame, schedule) && + !get_frame_info(&schedule_timeout_frame, schedule_timeout) && + !get_frame_info(&sleep_on_frame, sleep_on) && + !get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout) && + !get_frame_info(&wait_for_completion_frame, wait_for_completion); +} + /* * Return saved PC of a blocked thread. */ @@ -174,7 +236,9 @@ if (t->reg31 == (unsigned long) ret_from_fork) return t->reg31; - return ((unsigned long*)t->reg29)[11]; + if (schedule_frame.pc_offset < 0) + return 0; + return ((unsigned long *)t->reg29)[schedule_frame.pc_offset]; } /* @@ -193,6 +257,8 @@ if (!p || p == current || p->state == TASK_RUNNING) return 0; + if (!mips_frame_info_initialized) + return 0; pc = thread_saved_pc(&p->thread); if (pc < first_sched || pc >= last_sched) goto out; @@ -205,26 +271,29 @@ goto schedule_timeout_caller; if (pc >= (unsigned long)interruptible_sleep_on) goto schedule_caller; + if (pc >= (unsigned long)wait_for_completion) + goto schedule_caller; goto schedule_timeout_caller; schedule_caller: - frame = ((unsigned long *)p->thread.reg30)[10]; - pc = ((unsigned long *)frame)[7]; + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; + if (pc >= (unsigned long) sleep_on) + pc = ((unsigned long *)frame)[sleep_on_frame.pc_offset]; + else + pc = ((unsigned long *)frame)[wait_for_completion_frame.pc_offset]; goto out; schedule_timeout_caller: /* Must be schedule_timeout ... */ - pc = ((unsigned long *)p->thread.reg30)[11]; - frame = ((unsigned long *)p->thread.reg30)[10]; + frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset]; /* The schedule_timeout frame ... */ - pc = ((unsigned long *)frame)[9]; - frame = ((unsigned long *)frame)[8]; + pc = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset]; if (pc >= first_sched && pc < last_sched) { - /* schedule_timeout called by interruptible_sleep_on_timeout */ - pc = ((unsigned long *)frame)[7]; - frame = ((unsigned long *)frame)[6]; + /* schedule_timeout called by [interruptible_]sleep_on_timeout */ + frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset]; + pc = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset]; } out: diff -ur linux-mips-2.5-cvs/arch/mips64/kernel/setup.c linux-2.5.new/arch/mips64/kernel/setup.c --- linux-mips-2.5-cvs/arch/mips64/kernel/setup.c Wed Apr 9 09:46:47 2003 +++ linux-2.5.new/arch/mips64/kernel/setup.c Sat May 3 21:25:59 2003 @@ -421,7 +421,9 @@ extern void ip32_setup(void); extern void swarm_setup(void); extern void malta_setup(void); + void frame_info_init(void); + frame_info_init(); #ifdef CONFIG_DECSTATION decstation_setup(); #endif --- Atsushi Nemoto