diff options
author | Matt Fleming <matt@console-pimps.org> | 2009-08-07 15:11:19 (GMT) |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-08-13 02:50:08 (GMT) |
commit | 4e14dfc722b8e9e07a355f97aa60a3d9f0739071 (patch) | |
tree | 0d5b9f6ecb99a050e2ddb839aead6a1d235c26cb | |
parent | 922b0dc59bb43f7ff3bb8b9558ffeb3ad6af528e (diff) | |
download | linux-4e14dfc722b8e9e07a355f97aa60a3d9f0739071.tar.xz |
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>
-rw-r--r-- | arch/sh/include/asm/stacktrace.h | 25 | ||||
-rw-r--r-- | arch/sh/kernel/Makefile_32 | 2 | ||||
-rw-r--r-- | arch/sh/kernel/dumpstack.c | 128 | ||||
-rw-r--r-- | arch/sh/kernel/stacktrace.c | 87 | ||||
-rw-r--r-- | arch/sh/kernel/traps_32.c | 24 | ||||
-rw-r--r-- | arch/sh/oprofile/backtrace.c | 56 |
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 0000000..7970182 --- /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 94ed99b..6b32de7 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 0000000..6ab996f --- /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 1a2a5eb..6c24a40 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 2b77277..5634264 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 9499a29..62e4e4d 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; } |