[MIPS] Rewrite get_wchan and its helper functions using kallsyms_lookup.
authorAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Tue, 7 Feb 2006 16:48:03 +0000 (01:48 +0900)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 14 Feb 2006 19:13:24 +0000 (19:13 +0000)
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>
arch/mips/kernel/process.c

index 5232fc752935385a7346c7d9702eebd425106c85..092679c2dca9b83bd3e66503d83413daf29853d7 100644 (file)
@@ -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)
+                               continue;
+                       info->pc_offset =
+                               ip->i_format.simmediate / sizeof(long);
                }
        }
-       if (info->pc_offset == -1 || info->frame_offset == -1) {
-               printk("Can't analyze prologue code at %p\n", func);
+       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;
-                       }
-               }
-       } while (found);
-       mips_frame_info_initialized = 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;
+       }
+#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;
 }