|
@@ -3,6 +3,7 @@
|
|
|
#include <linux/stacktrace.h>
|
|
|
|
|
|
#include <asm/stacktrace.h>
|
|
|
+#include <asm/traps.h>
|
|
|
|
|
|
#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
|
|
|
/*
|
|
@@ -61,6 +62,7 @@ EXPORT_SYMBOL(walk_stackframe);
|
|
|
#ifdef CONFIG_STACKTRACE
|
|
|
struct stack_trace_data {
|
|
|
struct stack_trace *trace;
|
|
|
+ unsigned long last_pc;
|
|
|
unsigned int no_sched_functions;
|
|
|
unsigned int skip;
|
|
|
};
|
|
@@ -69,6 +71,7 @@ static int save_trace(struct stackframe *frame, void *d)
|
|
|
{
|
|
|
struct stack_trace_data *data = d;
|
|
|
struct stack_trace *trace = data->trace;
|
|
|
+ struct pt_regs *regs;
|
|
|
unsigned long addr = frame->pc;
|
|
|
|
|
|
if (data->no_sched_functions && in_sched_functions(addr))
|
|
@@ -80,6 +83,25 @@ static int save_trace(struct stackframe *frame, void *d)
|
|
|
|
|
|
trace->entries[trace->nr_entries++] = addr;
|
|
|
|
|
|
+ if (trace->nr_entries >= trace->max_entries)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * in_exception_text() is designed to test if the PC is one of
|
|
|
+ * the functions which has an exception stack above it, but
|
|
|
+ * unfortunately what is in frame->pc is the return LR value,
|
|
|
+ * not the saved PC value. So, we need to track the previous
|
|
|
+ * frame PC value when doing this.
|
|
|
+ */
|
|
|
+ addr = data->last_pc;
|
|
|
+ data->last_pc = frame->pc;
|
|
|
+ if (!in_exception_text(addr))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ regs = (struct pt_regs *)frame->sp;
|
|
|
+
|
|
|
+ trace->entries[trace->nr_entries++] = regs->ARM_pc;
|
|
|
+
|
|
|
return trace->nr_entries >= trace->max_entries;
|
|
|
}
|
|
|
|
|
@@ -91,6 +113,7 @@ static noinline void __save_stack_trace(struct task_struct *tsk,
|
|
|
struct stackframe frame;
|
|
|
|
|
|
data.trace = trace;
|
|
|
+ data.last_pc = ULONG_MAX;
|
|
|
data.skip = trace->skip;
|
|
|
data.no_sched_functions = nosched;
|
|
|
|