diff options
Diffstat (limited to 'arch/cris/kernel/stacktrace.c')
| -rw-r--r-- | arch/cris/kernel/stacktrace.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/arch/cris/kernel/stacktrace.c b/arch/cris/kernel/stacktrace.c new file mode 100644 index 000000000000..99838c74456d --- /dev/null +++ b/arch/cris/kernel/stacktrace.c | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #include <linux/sched.h> | ||
| 2 | #include <linux/stacktrace.h> | ||
| 3 | #include <linux/stacktrace.h> | ||
| 4 | #include <asm/stacktrace.h> | ||
| 5 | |||
| 6 | void walk_stackframe(unsigned long sp, | ||
| 7 | int (*fn)(unsigned long addr, void *data), | ||
| 8 | void *data) | ||
| 9 | { | ||
| 10 | unsigned long high = ALIGN(sp, THREAD_SIZE); | ||
| 11 | |||
| 12 | for (; sp <= high - 4; sp += 4) { | ||
| 13 | unsigned long addr = *(unsigned long *) sp; | ||
| 14 | |||
| 15 | if (!kernel_text_address(addr)) | ||
| 16 | continue; | ||
| 17 | |||
| 18 | if (fn(addr, data)) | ||
| 19 | break; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | struct stack_trace_data { | ||
| 24 | struct stack_trace *trace; | ||
| 25 | unsigned int no_sched_functions; | ||
| 26 | unsigned int skip; | ||
| 27 | }; | ||
| 28 | |||
| 29 | #ifdef CONFIG_STACKTRACE | ||
| 30 | |||
| 31 | static int save_trace(unsigned long addr, void *d) | ||
| 32 | { | ||
| 33 | struct stack_trace_data *data = d; | ||
| 34 | struct stack_trace *trace = data->trace; | ||
| 35 | |||
| 36 | if (data->no_sched_functions && in_sched_functions(addr)) | ||
| 37 | return 0; | ||
| 38 | |||
| 39 | if (data->skip) { | ||
| 40 | data->skip--; | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | trace->entries[trace->nr_entries++] = addr; | ||
| 45 | |||
| 46 | return trace->nr_entries >= trace->max_entries; | ||
| 47 | } | ||
| 48 | |||
| 49 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||
| 50 | { | ||
| 51 | struct stack_trace_data data; | ||
| 52 | unsigned long sp; | ||
| 53 | |||
| 54 | data.trace = trace; | ||
| 55 | data.skip = trace->skip; | ||
| 56 | |||
| 57 | if (tsk != current) { | ||
| 58 | data.no_sched_functions = 1; | ||
| 59 | sp = tsk->thread.ksp; | ||
| 60 | } else { | ||
| 61 | data.no_sched_functions = 0; | ||
| 62 | sp = rdsp(); | ||
| 63 | } | ||
| 64 | |||
| 65 | walk_stackframe(sp, save_trace, &data); | ||
| 66 | if (trace->nr_entries < trace->max_entries) | ||
| 67 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
| 68 | } | ||
| 69 | |||
| 70 | void save_stack_trace(struct stack_trace *trace) | ||
| 71 | { | ||
| 72 | save_stack_trace_tsk(current, trace); | ||
| 73 | } | ||
| 74 | EXPORT_SYMBOL_GPL(save_stack_trace); | ||
| 75 | |||
| 76 | #endif /* CONFIG_STACKTRACE */ | ||
