[parisc-linux-cvs] linux-2.6 tausq
Randolph Chung
randolph at tausq.org
Sat May 8 22:24:23 MDT 2004
> Log message:
> 2.6.6-rc3-pa11
> - Add kernel unwinding support
> - Use unwinding infrastructure to implement get_wchan
> only tested on 32-bit, 64-bit might need some work still
diff is attached. Please give it a try and let me know how it goes. The
wchan stuff should work now. Eventually we might move traps.c to this
too instead of the custom unwinding code that is there now.
i'm especially interested in results for 64-bit kernels and cases where
the kernel is blocked in a function that is in a loadable module.
there are a lot of asm functions in the kernel that don't have proper
unwinding information, so the unwinding code uses a fallback heuristic.
it seems to work ok, but potentially it can give wrong results and
possibly crash the kernel. i haven't seen that happen yet, but i'll
think about how to do it more safely.
the unwinding code is derived from some code that was originally in the
kdb branch, so there are some comments from 2002 :)
randolph
--
Randolph Chung
Debian GNU/Linux Developer, hppa/ia64 ports
http://www.tausq.org/
Index: arch/parisc/kernel/unwind.c
===================================================================
RCS file: arch/parisc/kernel/unwind.c
diff -N arch/parisc/kernel/unwind.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ b/arch/parisc/kernel/unwind.c 9 May 2004 04:13:00 -0000 1.1
@@ -0,0 +1,286 @@
+/*
+ * Kernel unwinding support
+ *
+ * (c) 2002 Randolph Chung <tausq at debian.org>
+ *
+ * Derived partially from the IA64 implementation. The PA-RISC
+ * Runtime Architecture Document is also a useful reference to
+ * understand what is happening here
+ */
+
+/*
+ * J. David Anglin writes:
+ *
+ * "You have to adjust the current sp to that at the begining of the function.
+ * There can be up to two stack additions to allocate the frame in the
+ * prologue. Similar things happen in the epilogue. In the presence of
+ * interrupts, you have to be concerned about where you are in the function
+ * and what stack adjustments have taken place."
+ *
+ * For now these cases are not handled, but they should be!
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+#include <asm/unwind.h>
+
+/* #define DEBUG 1 */
+#ifdef DEBUG
+#define dbg(x...) printk(x)
+#else
+#define dbg(x...)
+#endif
+
+extern const struct unwind_table_entry __start___unwind[];
+extern const struct unwind_table_entry __stop___unwind[];
+
+static spinlock_t unwind_lock;
+/*
+ * the kernel unwind block is not dynamically allocated so that
+ * we can call unwind_init as early in the bootup process as
+ * possible (before the slab allocator is initialized)
+ */
+static struct unwind_table kernel_unwind_table;
+static struct unwind_table *unwind_tables, *unwind_tables_end;
+
+
+static inline const struct unwind_table_entry *
+find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
+{
+ const struct unwind_table_entry *e = 0;
+ unsigned long lo, hi, mid;
+
+ addr -= table->base_addr;
+
+ for (lo = 0, hi = table->length; lo < hi; )
+ {
+ mid = (lo + hi) / 2;
+ e = &table->table[mid];
+ if (addr < e->region_start)
+ hi = mid;
+ else if (addr > e->region_end)
+ lo = mid + 1;
+ else
+ break;
+ }
+
+ return e;
+}
+
+static inline const struct unwind_table_entry *
+find_unwind_entry(unsigned long addr)
+{
+ struct unwind_table *table = unwind_tables;
+ const struct unwind_table_entry *e = NULL;
+
+ if (addr >= kernel_unwind_table.start &&
+ addr <= kernel_unwind_table.end)
+ e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
+ else
+ for (; table; table = table->next)
+ {
+ if (addr >= table->start &&
+ addr <= table->end)
+ e = find_unwind_entry_in_table(table, addr);
+ if (e)
+ break;
+ }
+
+ return e;
+}
+
+static void
+unwind_table_init(struct unwind_table *table, const char *name,
+ unsigned long base_addr, unsigned long gp,
+ const void *table_start, const void *table_end)
+{
+ const struct unwind_table_entry *start = table_start;
+ const struct unwind_table_entry *end = table_end - 1;
+
+ table->name = name;
+ table->base_addr = base_addr;
+ table->gp = gp;
+ table->start = base_addr + start->region_start;
+ table->end = base_addr + end->region_end;
+ table->table = (struct unwind_table_entry *)table_start;
+ table->length = end - start;
+ table->next = NULL;
+}
+
+void *
+unwind_table_add(const char *name, unsigned long base_addr,
+ unsigned long gp,
+ const void *start, const void *end)
+{
+ struct unwind_table *table;
+ unsigned long flags;
+
+ table = kmalloc(sizeof(struct unwind_table), GFP_USER);
+ if (table == NULL)
+ return 0;
+ unwind_table_init(table, name, base_addr, gp, start, end);
+ spin_lock_irqsave(&unwind_lock, flags);
+ if (unwind_tables)
+ {
+ unwind_tables_end->next = table;
+ unwind_tables_end = table;
+ }
+ else
+ {
+ unwind_tables = unwind_tables_end = table;
+ }
+ spin_unlock_irqrestore(&unwind_lock, flags);
+
+ return table;
+}
+
+/* Called from setup_arch to import the kernel unwind info */
+static int unwind_init(void)
+{
+ long start, stop;
+ register unsigned long gp __asm__ ("r27");
+
+ start = (long)&__start___unwind[0];
+ stop = (long)&__stop___unwind[0];
+
+ printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
+ start, stop,
+ (stop - start) / sizeof(struct unwind_table_entry));
+
+ unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START,
+ gp,
+ &__start___unwind[0], &__stop___unwind[0]);
+#if 0
+ {
+ int i;
+ for (i = 0; i < 10; i++)
+ {
+ printk("region 0x%x-0x%x\n",
+ __start___unwind[i].region_start,
+ __start___unwind[i].region_end);
+ }
+ }
+#endif
+ return 0;
+}
+
+static void unwind_frame_regs(struct unwind_frame_info *info)
+{
+ const struct unwind_table_entry *e;
+ unsigned long npc;
+ unsigned int insn;
+ long frame_size = 0;
+ int looking_for_rp, rpoffset = 0;
+
+ e = find_unwind_entry(info->ip);
+ if (!e) {
+ unsigned long sp = info->sp;
+ extern char _stext[], _etext[];
+
+ dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip);
+ do {
+ info->prev_sp = (sp - 64) & ~63;
+#ifndef __LP64__
+ info->prev_ip = *(unsigned long *)(info->prev_sp - 20);
+#else
+ info->prev_ip = *(unsigned long *)(info->prev_sp - 16);
+#endif
+
+ sp = info->prev_sp;
+ } while (info->prev_ip < (unsigned long)_stext ||
+ info->prev_ip > (unsigned long)_etext);
+ } else {
+
+ dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, Save_RP = %d size = %u\n",
+ e->region_start, e->region_end, e->Save_SP, e->Save_RP, e->Total_frame_size);
+
+ looking_for_rp = e->Save_RP;
+
+ for (npc = e->region_start;
+ (frame_size < (e->Total_frame_size << 3) || looking_for_rp) &&
+ npc < info->ip;
+ npc += 4) {
+
+ insn = *(unsigned int *)npc;
+
+ if ((insn & 0xffffc000) == 0x37de0000 ||
+ (insn & 0xffe00000) == 0x6fc00000)
+ {
+ /* ldo X(sp), sp, or stwm X,D(sp) */
+ frame_size += (insn & 0x1 ? -1 << 13 : 0) |
+ ((insn & 0x3fff) >> 1);
+ }
+ else if ((insn & 0xffe00008) == 0x7ec00008) {
+ /* std,ma X,D(sp) */
+ frame_size += (insn & 0x1 ? -1 << 13 : 0) |
+ (((insn >> 4) & 0x3ff) << 3);
+ }
+ else if (insn == 0x6bc23fd9) {
+ /* stw rp,-20(sp) */
+ rpoffset = 20;
+ looking_for_rp = 0;
+ } else if (insn == 0x0fc212c1) {
+ /* std rp,-16(sr0,sp) */
+ rpoffset = 16;
+ looking_for_rp = 0;
+ }
+ }
+
+ info->prev_sp = info->sp - frame_size;
+ if (rpoffset)
+ info->prev_ip = *(unsigned long *)(info->prev_sp - rpoffset);
+ }
+}
+
+void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
+ struct pt_regs *regs)
+{
+ memset(info, 0, sizeof(struct unwind_frame_info));
+ info->t = t;
+ info->sp = regs->ksp;
+ info->ip = regs->kpc;
+
+ dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", (int)t->pid, info->sp, info->ip);
+}
+
+void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
+{
+ struct pt_regs *regs = &t->thread.regs;
+ unwind_frame_init(info, t, regs);
+}
+
+int unwind_once(struct unwind_frame_info *next_frame)
+{
+ unwind_frame_regs(next_frame);
+
+ if (next_frame->prev_sp == 0 ||
+ next_frame->prev_ip == 0)
+ return -1;
+
+ next_frame->sp = next_frame->prev_sp;
+ next_frame->ip = next_frame->prev_ip;
+ next_frame->prev_sp = 0;
+ next_frame->prev_ip = 0;
+
+ dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", (int)next_frame->t->pid, next_frame->sp, next_frame->ip);
+
+ return 0;
+}
+
+int unwind_to_user(struct unwind_frame_info *info)
+{
+ int ret;
+
+ do {
+ ret = unwind_once(info);
+ } while (!ret && !(info->ip & 3));
+
+ return ret;
+}
+
+module_init(unwind_init);
Index: arch/parisc/kernel/Makefile
===================================================================
RCS file: /var/cvs/linux-2.6/arch/parisc/kernel/Makefile,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -p -r1.3 -r1.4
--- a/arch/parisc/kernel/Makefile 18 Oct 2003 18:31:09 -0000 1.3
+++ b/arch/parisc/kernel/Makefile 9 May 2004 04:13:00 -0000 1.4
@@ -14,7 +14,7 @@ obj-y := cache.o pacache.o setup.o
pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
ptrace.o hardware.o inventory.o drivers.o semaphore.o \
signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
- process.o processor.o pdc_cons.o pdc_chassis.o
+ process.o processor.o pdc_cons.o pdc_chassis.o unwind.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PA11) += pci-dma.o
Index: arch/parisc/kernel/process.c
===================================================================
RCS file: /var/cvs/linux-2.6/arch/parisc/kernel/process.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -p -r1.10 -r1.11
--- a/arch/parisc/kernel/process.c 20 Mar 2004 20:29:24 -0000 1.10
+++ b/arch/parisc/kernel/process.c 9 May 2004 04:13:00 -0000 1.11
@@ -44,6 +44,7 @@
#include <linux/sched.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
+#include <linux/kallsyms.h>
#include <asm/io.h>
#include <asm/offsets.h>
@@ -51,6 +52,7 @@
#include <asm/pdc_chassis.h>
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
+#include <asm/unwind.h>
int hlt_counter;
@@ -367,4 +369,29 @@ asmlinkage int sys_execve(struct pt_regs
out:
return error;
+}
+
+unsigned long
+get_wchan(struct task_struct *p)
+{
+ struct unwind_frame_info info;
+ unsigned long ip;
+ int count = 0;
+ /*
+ * These bracket the sleeping functions..
+ */
+# define first_sched ((unsigned long) scheduling_functions_start_here)
+# define last_sched ((unsigned long) scheduling_functions_end_here)
+
+ unwind_frame_init_from_blocked_task(&info, p);
+ do {
+ if (unwind_once(&info) < 0)
+ return 0;
+ ip = info.ip;
+ if (ip < first_sched || ip >= last_sched)
+ return ip;
+ } while (count++ < 16);
+ return 0;
+# undef first_sched
+# undef last_sched
}
Index: arch/parisc/kernel/vmlinux.lds.S
===================================================================
RCS file: /var/cvs/linux-2.6/arch/parisc/kernel/vmlinux.lds.S,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -p -r1.12 -r1.13
--- a/arch/parisc/kernel/vmlinux.lds.S 25 Apr 2004 14:50:51 -0000 1.12
+++ b/arch/parisc/kernel/vmlinux.lds.S 9 May 2004 04:13:00 -0000 1.13
@@ -57,7 +57,6 @@ SECTIONS
*(.text.do_sigaltstack)
*(.text.do_fork)
*(.text.*)
- *(.PARISC.unwind)
*(.fixup)
*(.lock.text) /* out-of-line lock text */
*(.gnu.warning)
@@ -78,6 +77,10 @@ SECTIONS
__ex_table : { *(__ex_table) }
__stop___ex_table = .;
+ __start___unwind = .; /* unwind info */
+ .PARISC.unwind : { *(.PARISC.unwind) }
+ __stop___unwind = .;
+
.data : { /* Data */
*(.data)
CONSTRUCTORS
Index: include/asm-parisc/unwind.h
===================================================================
RCS file: include/asm-parisc/unwind.h
diff -N include/asm-parisc/unwind.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ b/include/asm-parisc/unwind.h 9 May 2004 04:13:01 -0000 1.1
@@ -0,0 +1,72 @@
+#ifndef _UNWIND_H_
+#define _UNWIND_H_
+
+/* From ABI specifications */
+struct unwind_table_entry {
+ unsigned int region_start;
+ unsigned int region_end;
+ unsigned int Cannot_unwind:1; /* 0 */
+ unsigned int Millicode:1; /* 1 */
+ unsigned int Millicode_save_sr0:1; /* 2 */
+ unsigned int Region_description:2; /* 3..4 */
+ unsigned int reserved1:1; /* 5 */
+ unsigned int Entry_SR:1; /* 6 */
+ unsigned int Entry_FR:4; /* number saved *//* 7..10 */
+ unsigned int Entry_GR:5; /* number saved *//* 11..15 */
+ unsigned int Args_stored:1; /* 16 */
+ unsigned int Variable_Frame:1; /* 17 */
+ unsigned int Separate_Package_Body:1; /* 18 */
+ unsigned int Frame_Extension_Millicode:1; /* 19 */
+ unsigned int Stack_Overflow_Check:1; /* 20 */
+ unsigned int Two_Instruction_SP_Increment:1; /* 21 */
+ unsigned int Ada_Region:1; /* 22 */
+ unsigned int cxx_info:1; /* 23 */
+ unsigned int cxx_try_catch:1; /* 24 */
+ unsigned int sched_entry_seq:1; /* 25 */
+ unsigned int reserved2:1; /* 26 */
+ unsigned int Save_SP:1; /* 27 */
+ unsigned int Save_RP:1; /* 28 */
+ unsigned int Save_MRP_in_frame:1; /* 29 */
+ unsigned int extn_ptr_defined:1; /* 30 */
+ unsigned int Cleanup_defined:1; /* 31 */
+
+ unsigned int MPE_XL_interrupt_marker:1; /* 0 */
+ unsigned int HP_UX_interrupt_marker:1; /* 1 */
+ unsigned int Large_frame:1; /* 2 */
+ unsigned int Pseudo_SP_Set:1; /* 3 */
+ unsigned int reserved4:1; /* 4 */
+ unsigned int Total_frame_size:27; /* 5..31 */
+};
+
+struct unwind_table {
+ struct unwind_table *next;
+ const char *name;
+ unsigned long gp;
+ unsigned long base_addr;
+ unsigned long start;
+ unsigned long end;
+ const struct unwind_table_entry *table;
+ unsigned long length;
+};
+
+struct unwind_frame_info {
+ unsigned long sp;
+ unsigned long ip;
+ struct task_struct *t;
+ /* Eventually we would like to be able to get at any of the registers
+ available; but for now we only try to get the sp and ip for each
+ frame */
+ /* struct pt_regs regs; */
+ unsigned long prev_sp, prev_ip;
+};
+
+void * unwind_table_add(const char *name, unsigned long base_addr,
+ unsigned long gp,
+ const void *start, const void *end);
+void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
+ struct pt_regs *regs);
+void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t);
+int unwind_once(struct unwind_frame_info *info);
+int unwind_to_user(struct unwind_frame_info *info);
+
+#endif
Index: include/asm-parisc/processor.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/asm-parisc/processor.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -p -r1.9 -r1.10
--- a/include/asm-parisc/processor.h 28 Apr 2004 21:12:36 -0000 1.9
+++ b/include/asm-parisc/processor.h 9 May 2004 04:13:01 -0000 1.10
@@ -309,10 +309,7 @@ extern int kernel_thread(int (*fn)(void
extern void map_hpux_gateway_page(struct task_struct *tsk, struct mm_struct *mm);
-static inline unsigned long get_wchan(struct task_struct *p)
-{
- return 0xdeadbeef; /* XXX */
-}
+extern unsigned long get_wchan(struct task_struct *p);
#define KSTK_EIP(tsk) ((tsk)->thread.regs.iaoq[0])
#define KSTK_ESP(tsk) ((tsk)->thread.regs.gr[30])
Index: include/asm-parisc/system.h
===================================================================
RCS file: /var/cvs/linux-2.6/include/asm-parisc/system.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -p -r1.2 -r1.3
--- a/include/asm-parisc/system.h 25 Mar 2004 06:19:07 -0000 1.2
+++ b/include/asm-parisc/system.h 9 May 2004 04:13:01 -0000 1.3
@@ -172,4 +172,6 @@ typedef struct {
#endif
+#define KERNEL_START (0x10100000 - 0x1000)
+
#endif
Index: Makefile
===================================================================
RCS file: /var/cvs/linux-2.6/Makefile,v
retrieving revision 1.190
retrieving revision 1.191
diff -u -p -r1.190 -r1.191
--- a/Makefile 8 May 2004 14:12:45 -0000 1.190
+++ b/Makefile 9 May 2004 04:13:01 -0000 1.191
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 6
-EXTRAVERSION =-rc3-pa10
+EXTRAVERSION =-rc3-pa11
NAME=Zonked Quokka
# *DOCUMENTATION*
More information about the parisc-linux-cvs
mailing list