Skip to content

Commit

Permalink
[ARM] Add stacktrace support and make oprofile use it
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Russell King authored and Russell King committed Apr 28, 2007
1 parent ed519de commit f16fb1e
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 54 deletions.
8 changes: 8 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ config GENERIC_HARDIRQS
bool
default y

config STACKTRACE_SUPPORT
bool
default y

config LOCKDEP_SUPPORT
bool
default y

config TRACE_IRQFLAGS_SUPPORT
bool
default y
Expand Down
4 changes: 2 additions & 2 deletions arch/arm/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
# Object file lists.

obj-y := compat.o entry-armv.o entry-common.o irq.o \
process.o ptrace.o semaphore.o setup.o signal.o sys_arm.o \
time.o traps.o
process.o ptrace.o semaphore.o setup.o signal.o \
sys_arm.o stacktrace.o time.o traps.o

obj-$(CONFIG_ISA_DMA_API) += dma.o
obj-$(CONFIG_ARCH_ACORN) += ecard.o
Expand Down
73 changes: 73 additions & 0 deletions arch/arm/kernel/stacktrace.c
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions arch/arm/kernel/stacktrace.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
struct stackframe {
unsigned long fp;
unsigned long sp;
unsigned long lr;
unsigned long pc;
};

int walk_stackframe(unsigned long fp, unsigned long low, unsigned long high,
int (*fn)(struct stackframe *, void *), void *data);
69 changes: 17 additions & 52 deletions arch/arm/oprofile/backtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@
#include <asm/ptrace.h>
#include <asm/uaccess.h>

#include "../kernel/stacktrace.h"

static int report_trace(struct stackframe *frame, void *d)
{
unsigned int *depth = d;

if (*depth) {
oprofile_add_trace(frame->lr);
(*depth)--;
}

return *depth == 0;
}

/*
* The registers we're interested in are at the end of the variable
Expand All @@ -32,21 +45,6 @@ struct frame_tail {
unsigned long lr;
} __attribute__((packed));


#ifdef CONFIG_FRAME_POINTER
static struct frame_tail* kernel_backtrace(struct frame_tail *tail)
{
oprofile_add_trace(tail->lr);

/* frame pointers should strictly progress back up the stack
* (towards higher addresses) */
if (tail >= tail->fp)
return NULL;

return tail->fp-1;
}
#endif

static struct frame_tail* user_backtrace(struct frame_tail *tail)
{
struct frame_tail buftail[2];
Expand All @@ -67,47 +65,14 @@ static struct frame_tail* user_backtrace(struct frame_tail *tail)
return buftail[0].fp-1;
}

/*
* | | /\ Higher addresses
* | |
* --------------- stack base (address of current_thread_info)
* | thread info |
* . .
* | stack |
* --------------- saved regs->ARM_fp value if valid (frame_tail address)
* . .
* --------------- struct pt_regs stored on stack (struct pt_regs *)
* | |
* . .
* | |
* --------------- %esp
* | |
* | | \/ Lower addresses
*
* Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
*/
static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs)
{
unsigned long tailaddr = (unsigned long)tail;
unsigned long stack = (unsigned long)regs;
unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;

return (tailaddr > stack) && (tailaddr < stack_base);
}

void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
{
struct frame_tail *tail;

tail = ((struct frame_tail *) regs->ARM_fp) - 1;
struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1;

if (!user_mode(regs)) {

#ifdef CONFIG_FRAME_POINTER
while (depth-- && tail && valid_kernel_stack(tail, regs)) {
tail = kernel_backtrace(tail);
}
#endif
unsigned long base = ((unsigned long)regs) & ~(THREAD_SIZE - 1);
walk_stackframe(regs->ARM_fp, base, base + THREAD_SIZE,
report_trace, &depth);
return;
}

Expand Down

0 comments on commit f16fb1e

Please sign in to comment.