This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / arch / mips / kernel / stacktrace.c
diff --git a/arch/mips/kernel/stacktrace.c b/arch/mips/kernel/stacktrace.c
new file mode 100644 (file)
index 0000000..a586aba
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * arch/mips/kernel/stacktrace.c
+ *
+ * Stack trace management functions
+ *
+ *  Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ */
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <asm/stacktrace.h>
+
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer:
+ */
+static void save_raw_context_stack(struct stack_trace *trace,
+       unsigned long reg29)
+{
+       unsigned long *sp = (unsigned long *)reg29;
+       unsigned long addr;
+
+       while (!kstack_end(sp)) {
+               addr = *sp++;
+               if (__kernel_text_address(addr)) {
+                       if (trace->skip > 0)
+                               trace->skip--;
+                       else
+                               trace->entries[trace->nr_entries++] = addr;
+                       if (trace->nr_entries >= trace->max_entries)
+                               break;
+               }
+       }
+}
+
+static void save_context_stack(struct stack_trace *trace,
+       struct task_struct *task, struct pt_regs *regs)
+{
+       unsigned long sp = regs->regs[29];
+#ifdef CONFIG_KALLSYMS
+       unsigned long ra = regs->regs[31];
+       unsigned long pc = regs->cp0_epc;
+
+       if (raw_show_trace || !__kernel_text_address(pc)) {
+               unsigned long stack_page =
+                       (unsigned long)task_stack_page(task);
+               if (stack_page && sp >= stack_page &&
+                   sp <= stack_page + THREAD_SIZE - 32)
+                       save_raw_context_stack(trace, sp);
+               return;
+       }
+       do {
+               if (trace->skip > 0)
+                       trace->skip--;
+               else
+                       trace->entries[trace->nr_entries++] = pc;
+               if (trace->nr_entries >= trace->max_entries)
+                       break;
+               pc = unwind_stack(task, &sp, pc, &ra);
+       } while (pc);
+#else
+       save_raw_context_stack(trace, sp);
+#endif
+}
+
+/*
+ * Save stack-backtrace addresses into a stack_trace buffer.
+ */
+void save_stack_trace(struct stack_trace *trace, struct task_struct *task)
+{
+       struct pt_regs dummyregs;
+       struct pt_regs *regs = &dummyregs;
+
+       WARN_ON(trace->nr_entries || !trace->max_entries);
+
+       if (task && task != current) {
+               regs->regs[29] = task->thread.reg29;
+               regs->regs[31] = 0;
+               regs->cp0_epc = task->thread.reg31;
+       } else {
+               if (!task)
+                       task = current;
+               prepare_frametrace(regs);
+       }
+
+       save_context_stack(trace, task, regs);
+}