Commit 4e14dfc7 authored by Matt Fleming's avatar Matt Fleming Committed by Paul Mundt

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: default avatarMatt Fleming <matt@console-pimps.org>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent 922b0dc5
/*
* 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 */
...@@ -9,7 +9,7 @@ ifdef CONFIG_FUNCTION_TRACER ...@@ -9,7 +9,7 @@ ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_ftrace.o = -pg
endif 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 \ 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 \ sys_sh.o sys_sh32.o syscalls_32.o time.o topology.o \
traps.o traps_32.o traps.o traps_32.o
......
/*
* 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);
}
...@@ -14,46 +14,81 @@ ...@@ -14,46 +14,81 @@
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/ptrace.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. * 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) void save_stack_trace(struct stack_trace *trace)
{ {
unsigned long *sp = (unsigned long *)current_stack_pointer; unsigned long *sp = (unsigned long *)current_stack_pointer;
while (!kstack_end(sp)) { dump_trace(current, NULL, sp, &save_stack_ops, trace);
unsigned long addr = *sp++; }
EXPORT_SYMBOL_GPL(save_stack_trace);
if (__kernel_text_address(addr)) { static void
if (trace->skip > 0) 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--; trace->skip--;
else return;
trace->entries[trace->nr_entries++] = addr;
if (trace->nr_entries >= trace->max_entries)
break;
}
} }
if (trace->nr_entries < trace->max_entries)
trace->entries[trace->nr_entries++] = addr;
} }
EXPORT_SYMBOL_GPL(save_stack_trace);
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) void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{ {
unsigned long *sp = (unsigned long *)tsk->thread.sp; unsigned long *sp = (unsigned long *)tsk->thread.sp;
while (!kstack_end(sp)) { dump_trace(current, NULL, sp, &save_stack_ops_nosched, trace);
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;
}
}
} }
EXPORT_SYMBOL_GPL(save_stack_trace_tsk); EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
...@@ -858,30 +858,6 @@ void __init trap_init(void) ...@@ -858,30 +858,6 @@ void __init trap_init(void)
per_cpu_trap_init(); 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) void show_stack(struct task_struct *tsk, unsigned long *sp)
{ {
unsigned long stack; unsigned long stack;
......
...@@ -20,6 +20,39 @@ ...@@ -20,6 +20,39 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/sections.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. */ /* Limit to stop backtracing too far. */
static int backtrace_limit = 20; static int backtrace_limit = 20;
...@@ -74,23 +107,6 @@ static int valid_kernel_stack(unsigned long *stackaddr, struct pt_regs *regs) ...@@ -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); 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) void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
{ {
unsigned long *stackaddr; unsigned long *stackaddr;
...@@ -103,9 +119,9 @@ void sh_backtrace(struct pt_regs * const regs, unsigned int depth) ...@@ -103,9 +119,9 @@ void sh_backtrace(struct pt_regs * const regs, unsigned int depth)
stackaddr = (unsigned long *)regs->regs[15]; stackaddr = (unsigned long *)regs->regs[15];
if (!user_mode(regs)) { if (!user_mode(regs)) {
while (depth-- && valid_kernel_stack(stackaddr, regs)) if (depth)
stackaddr = kernel_backtrace(stackaddr, regs); dump_trace(NULL, regs, stackaddr,
&backtrace_ops, &depth);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment