Skip to content

Commit

Permalink
sh: Use the generalized stacktrace ops
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Matt Fleming authored and Paul Mundt committed Aug 13, 2009
1 parent 922b0dc commit 4e14dfc
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 71 deletions.
25 changes: 25 additions & 0 deletions arch/sh/include/asm/stacktrace.h
Original file line number Diff line number Diff line change
@@ -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 */
2 changes: 1 addition & 1 deletion arch/sh/kernel/Makefile_32
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
128 changes: 128 additions & 0 deletions arch/sh/kernel/dumpstack.c
Original file line number Diff line number Diff line change
@@ -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);
}
87 changes: 61 additions & 26 deletions arch/sh/kernel/stacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
24 changes: 0 additions & 24 deletions arch/sh/kernel/traps_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
56 changes: 36 additions & 20 deletions arch/sh/oprofile/backtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down

0 comments on commit 4e14dfc

Please sign in to comment.