summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorMatt Fleming <matt@console-pimps.org>2009-08-07 16:11:19 +0100
committerPaul Mundt <lethal@linux-sh.org>2009-08-13 11:50:08 +0900
commit4e14dfc722b8e9e07a355f97aa60a3d9f0739071 (patch)
tree0d5b9f6ecb99a050e2ddb839aead6a1d235c26cb /arch
parent922b0dc59bb43f7ff3bb8b9558ffeb3ad6af528e (diff)
sh: Use the generalized stacktrace ops
Copy the stacktrace ops code from x86 and provide a central function for use by functions that need to dump a callstack. Signed-off-by: Matt Fleming <matt@console-pimps.org> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/sh/include/asm/stacktrace.h25
-rw-r--r--arch/sh/kernel/Makefile_322
-rw-r--r--arch/sh/kernel/dumpstack.c128
-rw-r--r--arch/sh/kernel/stacktrace.c87
-rw-r--r--arch/sh/kernel/traps_32.c24
-rw-r--r--arch/sh/oprofile/backtrace.c56
6 files changed, 251 insertions, 71 deletions
diff --git a/arch/sh/include/asm/stacktrace.h b/arch/sh/include/asm/stacktrace.h
new file mode 100644
index 000000000000..797018213718
--- /dev/null
+++ b/arch/sh/include/asm/stacktrace.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 Matt Fleming
+ *
+ * Based on:
+ * The x86 implementation - arch/x86/include/asm/stacktrace.h
+ */
+#ifndef _ASM_SH_STACKTRACE_H
+#define _ASM_SH_STACKTRACE_H
+
+/* Generic stack tracer with callbacks */
+
+struct stacktrace_ops {
+ void (*warning)(void *data, char *msg);
+ /* msg must contain %s for the symbol */
+ void (*warning_symbol)(void *data, char *msg, unsigned long symbol);
+ void (*address)(void *data, unsigned long address, int reliable);
+ /* On negative return stop dumping */
+ int (*stack)(void *data, char *name);
+};
+
+void dump_trace(struct task_struct *tsk, struct pt_regs *regs,
+ unsigned long *stack,
+ const struct stacktrace_ops *ops, void *data);
+
+#endif /* _ASM_SH_STACKTRACE_H */
diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32
index 94ed99b68002..6b32de701a7e 100644
--- a/arch/sh/kernel/Makefile_32
+++ b/arch/sh/kernel/Makefile_32
@@ -9,7 +9,7 @@ ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_ftrace.o = -pg
endif
-obj-y := debugtraps.o idle.o io.o io_generic.o irq.o \
+obj-y := debugtraps.o dumpstack.o idle.o io.o io_generic.o irq.o \
machvec.o process_32.o ptrace_32.o setup.o signal_32.o \
sys_sh.o sys_sh32.o syscalls_32.o time.o topology.o \
traps.o traps_32.o
diff --git a/arch/sh/kernel/dumpstack.c b/arch/sh/kernel/dumpstack.c
new file mode 100644
index 000000000000..6ab996fc6121
--- /dev/null
+++ b/arch/sh/kernel/dumpstack.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2009 Matt Fleming
+ */
+#include <linux/kallsyms.h>
+#include <linux/ftrace.h>
+#include <linux/debug_locks.h>
+
+#include <asm/stacktrace.h>
+
+void printk_address(unsigned long address, int reliable)
+{
+ printk(" [<%p>] %s%pS\n", (void *) address,
+ reliable ? "" : "? ", (void *) address);
+}
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+static void
+print_ftrace_graph_addr(unsigned long addr, void *data,
+ const struct stacktrace_ops *ops,
+ struct thread_info *tinfo, int *graph)
+{
+ struct task_struct *task = tinfo->task;
+ unsigned long ret_addr;
+ int index = task->curr_ret_stack;
+
+ if (addr != (unsigned long)return_to_handler)
+ return;
+
+ if (!task->ret_stack || index < *graph)
+ return;
+
+ index -= *graph;
+ ret_addr = task->ret_stack[index].ret;
+
+ ops->address(data, ret_addr, 1);
+
+ (*graph)++;
+}
+#else
+static inline void
+print_ftrace_graph_addr(unsigned long addr, void *data,
+ const struct stacktrace_ops *ops,
+ struct thread_info *tinfo, int *graph)
+{ }
+#endif
+
+/*
+ * Unwind the call stack and pass information to the stacktrace_ops
+ * functions.
+ */
+void dump_trace(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *sp, const struct stacktrace_ops *ops,
+ void *data)
+{
+ struct thread_info *context;
+ int graph = 0;
+
+ context = (struct thread_info *)
+ ((unsigned long)sp & (~(THREAD_SIZE - 1)));
+
+ while (!kstack_end(sp)) {
+ unsigned long addr = *sp++;
+
+ if (__kernel_text_address(addr)) {
+ ops->address(data, addr, 0);
+
+ print_ftrace_graph_addr(addr, data, ops,
+ context, &graph);
+ }
+ }
+}
+EXPORT_SYMBOL(dump_trace);
+
+
+static void
+print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+ printk(data);
+ print_symbol(msg, symbol);
+ printk("\n");
+}
+
+static void print_trace_warning(void *data, char *msg)
+{
+ printk("%s%s\n", (char *)data, msg);
+}
+
+static int print_trace_stack(void *data, char *name)
+{
+ printk("%s <%s> ", (char *)data, name);
+ return 0;
+}
+
+/*
+ * Print one address/symbol entries per line.
+ */
+static void print_trace_address(void *data, unsigned long addr, int reliable)
+{
+ printk(data);
+ printk_address(addr, reliable);
+}
+
+static const struct stacktrace_ops print_trace_ops = {
+ .warning = print_trace_warning,
+ .warning_symbol = print_trace_warning_symbol,
+ .stack = print_trace_stack,
+ .address = print_trace_address,
+};
+
+void show_trace(struct task_struct *tsk, unsigned long *sp,
+ struct pt_regs *regs)
+{
+ if (regs && user_mode(regs))
+ return;
+
+ printk("\nCall trace:\n");
+
+ dump_trace(tsk, regs, sp, &print_trace_ops, "");
+
+ printk("\n");
+
+ if (!tsk)
+ tsk = current;
+
+ debug_show_held_locks(tsk);
+}
diff --git a/arch/sh/kernel/stacktrace.c b/arch/sh/kernel/stacktrace.c
index 1a2a5eb76e41..6c24a400b05e 100644
--- a/arch/sh/kernel/stacktrace.c
+++ b/arch/sh/kernel/stacktrace.c
@@ -14,46 +14,81 @@
#include <linux/thread_info.h>
#include <linux/module.h>
#include <asm/ptrace.h>
+#include <asm/stacktrace.h>
+
+static void save_stack_warning(void *data, char *msg)
+{
+}
+
+static void
+save_stack_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+}
+
+static int save_stack_stack(void *data, char *name)
+{
+ return 0;
+}
/*
* Save stack-backtrace addresses into a stack_trace buffer.
*/
+static void save_stack_address(void *data, unsigned long addr, int reliable)
+{
+ struct stack_trace *trace = data;
+
+ if (trace->skip > 0) {
+ trace->skip--;
+ return;
+ }
+
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = addr;
+}
+
+static const struct stacktrace_ops save_stack_ops = {
+ .warning = save_stack_warning,
+ .warning_symbol = save_stack_warning_symbol,
+ .stack = save_stack_stack,
+ .address = save_stack_address,
+};
+
void save_stack_trace(struct stack_trace *trace)
{
unsigned long *sp = (unsigned long *)current_stack_pointer;
- while (!kstack_end(sp)) {
- unsigned long 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;
- }
- }
+ dump_trace(current, NULL, sp, &save_stack_ops, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
+static void
+save_stack_address_nosched(void *data, unsigned long addr, int reliable)
+{
+ struct stack_trace *trace = (struct stack_trace *)data;
+
+ if (in_sched_functions(addr))
+ return;
+
+ if (trace->skip > 0) {
+ trace->skip--;
+ return;
+ }
+
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = addr;
+}
+
+static const struct stacktrace_ops save_stack_ops_nosched = {
+ .warning = save_stack_warning,
+ .warning_symbol = save_stack_warning_symbol,
+ .stack = save_stack_stack,
+ .address = save_stack_address_nosched,
+};
+
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
unsigned long *sp = (unsigned long *)tsk->thread.sp;
- while (!kstack_end(sp)) {
- unsigned long addr = *sp++;
-
- if (__kernel_text_address(addr)) {
- if (in_sched_functions(addr))
- break;
- if (trace->skip > 0)
- trace->skip--;
- else
- trace->entries[trace->nr_entries++] = addr;
- if (trace->nr_entries >= trace->max_entries)
- break;
- }
- }
+ dump_trace(current, NULL, sp, &save_stack_ops_nosched, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c
index 2b772776fcda..563426487c6b 100644
--- a/arch/sh/kernel/traps_32.c
+++ b/arch/sh/kernel/traps_32.c
@@ -858,30 +858,6 @@ void __init trap_init(void)
per_cpu_trap_init();
}
-void show_trace(struct task_struct *tsk, unsigned long *sp,
- struct pt_regs *regs)
-{
- unsigned long addr;
-
- if (regs && user_mode(regs))
- return;
-
- printk("\nCall trace:\n");
-
- while (!kstack_end(sp)) {
- addr = *sp++;
- if (kernel_text_address(addr))
- print_ip_sym(addr);
- }
-
- printk("\n");
-
- if (!tsk)
- tsk = current;
-
- debug_show_held_locks(tsk);
-}
-
void show_stack(struct task_struct *tsk, unsigned long *sp)
{
unsigned long stack;
diff --git a/arch/sh/oprofile/backtrace.c b/arch/sh/oprofile/backtrace.c
index 9499a2914f89..62e4e4d0273e 100644
--- a/arch/sh/oprofile/backtrace.c
+++ b/arch/sh/oprofile/backtrace.c
@@ -20,6 +20,39 @@
#include <asm/ptrace.h>
#include <asm/uaccess.h>
#include <asm/sections.h>
+#include <asm/stacktrace.h>
+
+static void backtrace_warning_symbol(void *data, char *msg,
+ unsigned long symbol)
+{
+ /* Ignore warnings */
+}
+
+static void backtrace_warning(void *data, char *msg)
+{
+ /* Ignore warnings */
+}
+
+static int backtrace_stack(void *data, char *name)
+{
+ /* Yes, we want all stacks */
+ return 0;
+}
+
+static void backtrace_address(void *data, unsigned long addr, int reliable)
+{
+ unsigned int *depth = data;
+
+ if ((*depth)--)
+ oprofile_add_trace(addr);
+}
+
+static struct stacktrace_ops backtrace_ops = {
+ .warning = backtrace_warning,
+ .warning_symbol = backtrace_warning_symbol,
+ .stack = backtrace_stack,
+ .address = backtrace_address,
+};
/* Limit to stop backtracing too far. */
static int backtrace_limit = 20;
@@ -74,23 +107,6 @@ static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs)
return ((unsigned long)stackaddr > stack) && ((unsigned long)stackaddr < stack_base);
}
-static unsigned long *
-kernel_backtrace(unsigned long *stackaddr, struct pt_regs *regs)
-{
- unsigned long addr;
-
- /*
- * If not a valid kernel address, keep going till we find one
- * or the SP stops being a valid address.
- */
- do {
- addr = *stackaddr++;
- oprofile_add_trace(addr);
- } while (valid_kernel_stack(stackaddr, regs));
-
- return stackaddr;
-}
-
void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
{
unsigned long *stackaddr;
@@ -103,9 +119,9 @@ void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
stackaddr = (unsigned long *)regs->regs[15];
if (!user_mode(regs)) {
- while (depth-- && valid_kernel_stack(stackaddr, regs))
- stackaddr = kernel_backtrace(stackaddr, regs);
-
+ if (depth)
+ dump_trace(NULL, regs, stackaddr,
+ &backtrace_ops, &depth);
return;
}