From f16fb1ecc5a1cb2f7cc595179d1fe55e711e599f Mon Sep 17 00:00:00 2001
From: Russell King <rmk@dyn-67.arm.linux.org.uk>
Date: Sat, 28 Apr 2007 09:59:37 +0100
Subject: [ARM] Add stacktrace support and make oprofile use it

Add support for stacktrace.  Use the new stacktrace code with
oprofile instead of it's version; there's no point having
multiple versions of stacktracing in the kernel.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/kernel/stacktrace.c | 73 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)
 create mode 100644 arch/arm/kernel/stacktrace.c

(limited to 'arch/arm/kernel/stacktrace.c')

diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
new file mode 100644
index 000000000000..77ef35efaa8d
--- /dev/null
+++ b/arch/arm/kernel/stacktrace.c
@@ -0,0 +1,73 @@
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+
+#include "stacktrace.h"
+
+int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high,
+		    int (*fn)(struct stackframe *, void *), void *data)
+{
+	struct stackframe *frame;
+
+	do {
+		/*
+		 * Check current frame pointer is within bounds
+		 */
+		if ((fp - 12) < low || fp + 4 >= high)
+			break;
+
+		frame = (struct stackframe *)(fp - 12);
+
+		if (fn(frame, data))
+			break;
+
+		/*
+		 * Update the low bound - the next frame must always
+		 * be at a higher address than the current frame.
+		 */
+		low = fp + 4;
+		fp = frame->fp;
+	} while (fp);
+
+	return 0;
+}
+
+#ifdef CONFIG_STACKTRACE
+struct stack_trace_data {
+	struct stack_trace *trace;
+	unsigned int skip;
+};
+
+static int save_trace(struct stackframe *frame, void *d)
+{
+	struct stack_trace_data *data = d;
+	struct stack_trace *trace = data->trace;
+
+	if (data->skip) {
+		data->skip--;
+		return 0;
+	}
+
+	trace->entries[trace->nr_entries++] = frame->lr;
+
+	return trace->nr_entries >= trace->max_entries;
+}
+
+void save_stack_trace(struct stack_trace *trace, struct task_struct *task)
+{
+	struct stack_trace_data data;
+	unsigned long fp, base;
+
+	data.trace = trace;
+	data.skip = trace->skip;
+
+	if (task) {
+		base = (unsigned long)task_stack_page(task);
+		fp = 0; /* FIXME */
+	} else {
+		base = (unsigned long)task_stack_page(current);
+		asm("mov %0, fp" : "=r" (fp));
+	}
+
+	walk_stackframe(fp, base, base + THREAD_SIZE, save_trace, &data);
+}
+#endif
-- 
cgit v1.2.3