diff options
Diffstat (limited to 'arch/xtensa/kernel/stacktrace.c')
-rw-r--r-- | arch/xtensa/kernel/stacktrace.c | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/arch/xtensa/kernel/stacktrace.c b/arch/xtensa/kernel/stacktrace.c new file mode 100644 index 000000000000..e3aff9575183 --- /dev/null +++ b/arch/xtensa/kernel/stacktrace.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * arch/xtensa/kernel/stacktrace.c | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2001 - 2013 Tensilica Inc. | ||
9 | */ | ||
10 | #include <linux/export.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/stacktrace.h> | ||
13 | |||
14 | #include <asm/stacktrace.h> | ||
15 | #include <asm/traps.h> | ||
16 | |||
17 | void walk_stackframe(unsigned long *sp, | ||
18 | int (*fn)(struct stackframe *frame, void *data), | ||
19 | void *data) | ||
20 | { | ||
21 | unsigned long a0, a1; | ||
22 | unsigned long sp_end; | ||
23 | |||
24 | a1 = (unsigned long)sp; | ||
25 | sp_end = ALIGN(a1, THREAD_SIZE); | ||
26 | |||
27 | spill_registers(); | ||
28 | |||
29 | while (a1 < sp_end) { | ||
30 | struct stackframe frame; | ||
31 | |||
32 | sp = (unsigned long *)a1; | ||
33 | |||
34 | a0 = *(sp - 4); | ||
35 | a1 = *(sp - 3); | ||
36 | |||
37 | if (a1 <= (unsigned long)sp) | ||
38 | break; | ||
39 | |||
40 | frame.pc = MAKE_PC_FROM_RA(a0, a1); | ||
41 | frame.sp = a1; | ||
42 | |||
43 | if (fn(&frame, data)) | ||
44 | return; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | #ifdef CONFIG_STACKTRACE | ||
49 | |||
50 | struct stack_trace_data { | ||
51 | struct stack_trace *trace; | ||
52 | unsigned skip; | ||
53 | }; | ||
54 | |||
55 | static int stack_trace_cb(struct stackframe *frame, void *data) | ||
56 | { | ||
57 | struct stack_trace_data *trace_data = data; | ||
58 | struct stack_trace *trace = trace_data->trace; | ||
59 | |||
60 | if (trace_data->skip) { | ||
61 | --trace_data->skip; | ||
62 | return 0; | ||
63 | } | ||
64 | if (!kernel_text_address(frame->pc)) | ||
65 | return 0; | ||
66 | |||
67 | trace->entries[trace->nr_entries++] = frame->pc; | ||
68 | return trace->nr_entries >= trace->max_entries; | ||
69 | } | ||
70 | |||
71 | void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace) | ||
72 | { | ||
73 | struct stack_trace_data trace_data = { | ||
74 | .trace = trace, | ||
75 | .skip = trace->skip, | ||
76 | }; | ||
77 | walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data); | ||
78 | } | ||
79 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | ||
80 | |||
81 | void save_stack_trace(struct stack_trace *trace) | ||
82 | { | ||
83 | save_stack_trace_tsk(current, trace); | ||
84 | } | ||
85 | EXPORT_SYMBOL_GPL(save_stack_trace); | ||
86 | |||
87 | #endif | ||