diff options
Diffstat (limited to 'arch/x86_64/kernel/stacktrace.c')
-rw-r--r-- | arch/x86_64/kernel/stacktrace.c | 214 |
1 files changed, 26 insertions, 188 deletions
diff --git a/arch/x86_64/kernel/stacktrace.c b/arch/x86_64/kernel/stacktrace.c index 1c022af8fe1e..6026b31d037e 100644 --- a/arch/x86_64/kernel/stacktrace.c +++ b/arch/x86_64/kernel/stacktrace.c | |||
@@ -7,211 +7,49 @@ | |||
7 | */ | 7 | */ |
8 | #include <linux/sched.h> | 8 | #include <linux/sched.h> |
9 | #include <linux/stacktrace.h> | 9 | #include <linux/stacktrace.h> |
10 | #include <linux/module.h> | ||
11 | #include <asm/stacktrace.h> | ||
10 | 12 | ||
11 | #include <asm/smp.h> | 13 | static void save_stack_warning(void *data, char *msg) |
12 | |||
13 | static inline int | ||
14 | in_range(unsigned long start, unsigned long addr, unsigned long end) | ||
15 | { | 14 | { |
16 | return addr >= start && addr <= end; | ||
17 | } | 15 | } |
18 | 16 | ||
19 | static unsigned long | 17 | static void |
20 | get_stack_end(struct task_struct *task, unsigned long stack) | 18 | save_stack_warning_symbol(void *data, char *msg, unsigned long symbol) |
21 | { | 19 | { |
22 | unsigned long stack_start, stack_end, flags; | ||
23 | int i, cpu; | ||
24 | |||
25 | /* | ||
26 | * The most common case is that we are in the task stack: | ||
27 | */ | ||
28 | stack_start = (unsigned long)task->thread_info; | ||
29 | stack_end = stack_start + THREAD_SIZE; | ||
30 | |||
31 | if (in_range(stack_start, stack, stack_end)) | ||
32 | return stack_end; | ||
33 | |||
34 | /* | ||
35 | * We are in an interrupt if irqstackptr is set: | ||
36 | */ | ||
37 | raw_local_irq_save(flags); | ||
38 | cpu = safe_smp_processor_id(); | ||
39 | stack_end = (unsigned long)cpu_pda(cpu)->irqstackptr; | ||
40 | |||
41 | if (stack_end) { | ||
42 | stack_start = stack_end & ~(IRQSTACKSIZE-1); | ||
43 | if (in_range(stack_start, stack, stack_end)) | ||
44 | goto out_restore; | ||
45 | /* | ||
46 | * We get here if we are in an IRQ context but we | ||
47 | * are also in an exception stack. | ||
48 | */ | ||
49 | } | ||
50 | |||
51 | /* | ||
52 | * Iterate over all exception stacks, and figure out whether | ||
53 | * 'stack' is in one of them: | ||
54 | */ | ||
55 | for (i = 0; i < N_EXCEPTION_STACKS; i++) { | ||
56 | /* | ||
57 | * set 'end' to the end of the exception stack. | ||
58 | */ | ||
59 | stack_end = per_cpu(init_tss, cpu).ist[i]; | ||
60 | stack_start = stack_end - EXCEPTION_STKSZ; | ||
61 | |||
62 | /* | ||
63 | * Is 'stack' above this exception frame's end? | ||
64 | * If yes then skip to the next frame. | ||
65 | */ | ||
66 | if (stack >= stack_end) | ||
67 | continue; | ||
68 | /* | ||
69 | * Is 'stack' above this exception frame's start address? | ||
70 | * If yes then we found the right frame. | ||
71 | */ | ||
72 | if (stack >= stack_start) | ||
73 | goto out_restore; | ||
74 | |||
75 | /* | ||
76 | * If this is a debug stack, and if it has a larger size than | ||
77 | * the usual exception stacks, then 'stack' might still | ||
78 | * be within the lower portion of the debug stack: | ||
79 | */ | ||
80 | #if DEBUG_STKSZ > EXCEPTION_STKSZ | ||
81 | if (i == DEBUG_STACK - 1 && stack >= stack_end - DEBUG_STKSZ) { | ||
82 | /* | ||
83 | * Black magic. A large debug stack is composed of | ||
84 | * multiple exception stack entries, which we | ||
85 | * iterate through now. Dont look: | ||
86 | */ | ||
87 | do { | ||
88 | stack_end -= EXCEPTION_STKSZ; | ||
89 | stack_start -= EXCEPTION_STKSZ; | ||
90 | } while (stack < stack_start); | ||
91 | |||
92 | goto out_restore; | ||
93 | } | ||
94 | #endif | ||
95 | } | ||
96 | /* | ||
97 | * Ok, 'stack' is not pointing to any of the system stacks. | ||
98 | */ | ||
99 | stack_end = 0; | ||
100 | |||
101 | out_restore: | ||
102 | raw_local_irq_restore(flags); | ||
103 | |||
104 | return stack_end; | ||
105 | } | 20 | } |
106 | 21 | ||
107 | 22 | static int save_stack_stack(void *data, char *name) | |
108 | /* | ||
109 | * Save stack-backtrace addresses into a stack_trace buffer: | ||
110 | */ | ||
111 | static inline unsigned long | ||
112 | save_context_stack(struct stack_trace *trace, | ||
113 | unsigned long stack, unsigned long stack_end) | ||
114 | { | 23 | { |
115 | int skip = trace->skip; | 24 | struct stack_trace *trace = (struct stack_trace *)data; |
116 | unsigned long addr; | 25 | return trace->all_contexts ? 0 : -1; |
117 | 26 | } | |
118 | #ifdef CONFIG_FRAME_POINTER | ||
119 | unsigned long prev_stack = 0; | ||
120 | 27 | ||
121 | while (in_range(prev_stack, stack, stack_end)) { | 28 | static void save_stack_address(void *data, unsigned long addr) |
122 | pr_debug("stack: %p\n", (void *)stack); | 29 | { |
123 | addr = (unsigned long)(((unsigned long *)stack)[1]); | 30 | struct stack_trace *trace = (struct stack_trace *)data; |
124 | pr_debug("addr: %p\n", (void *)addr); | 31 | if (trace->skip > 0) { |
125 | if (!skip) | 32 | trace->skip--; |
126 | trace->entries[trace->nr_entries++] = addr-1; | 33 | return; |
127 | else | ||
128 | skip--; | ||
129 | if (trace->nr_entries >= trace->max_entries) | ||
130 | break; | ||
131 | if (!addr) | ||
132 | return 0; | ||
133 | /* | ||
134 | * Stack frames must go forwards (otherwise a loop could | ||
135 | * happen if the stackframe is corrupted), so we move | ||
136 | * prev_stack forwards: | ||
137 | */ | ||
138 | prev_stack = stack; | ||
139 | stack = (unsigned long)(((unsigned long *)stack)[0]); | ||
140 | } | ||
141 | pr_debug("invalid: %p\n", (void *)stack); | ||
142 | #else | ||
143 | while (stack < stack_end) { | ||
144 | addr = ((unsigned long *)stack)[0]; | ||
145 | stack += sizeof(long); | ||
146 | if (__kernel_text_address(addr)) { | ||
147 | if (!skip) | ||
148 | trace->entries[trace->nr_entries++] = addr-1; | ||
149 | else | ||
150 | skip--; | ||
151 | if (trace->nr_entries >= trace->max_entries) | ||
152 | break; | ||
153 | } | ||
154 | } | 34 | } |
155 | #endif | 35 | if (trace->nr_entries < trace->max_entries - 1) |
156 | return stack; | 36 | trace->entries[trace->nr_entries++] = addr; |
157 | } | 37 | } |
158 | 38 | ||
159 | #define MAX_STACKS 10 | 39 | static struct stacktrace_ops save_stack_ops = { |
40 | .warning = save_stack_warning, | ||
41 | .warning_symbol = save_stack_warning_symbol, | ||
42 | .stack = save_stack_stack, | ||
43 | .address = save_stack_address, | ||
44 | }; | ||
160 | 45 | ||
161 | /* | 46 | /* |
162 | * Save stack-backtrace addresses into a stack_trace buffer. | 47 | * Save stack-backtrace addresses into a stack_trace buffer. |
163 | */ | 48 | */ |
164 | void save_stack_trace(struct stack_trace *trace, struct task_struct *task) | 49 | void save_stack_trace(struct stack_trace *trace, struct task_struct *task) |
165 | { | 50 | { |
166 | unsigned long stack = (unsigned long)&stack; | 51 | dump_trace(task, NULL, NULL, &save_stack_ops, trace); |
167 | int i, nr_stacks = 0, stacks_done[MAX_STACKS]; | 52 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
168 | |||
169 | WARN_ON(trace->nr_entries || !trace->max_entries); | ||
170 | |||
171 | if (!task) | ||
172 | task = current; | ||
173 | |||
174 | pr_debug("task: %p, ti: %p\n", task, task->thread_info); | ||
175 | |||
176 | if (!task || task == current) { | ||
177 | /* Grab rbp right from our regs: */ | ||
178 | asm ("mov %%rbp, %0" : "=r" (stack)); | ||
179 | pr_debug("rbp: %p\n", (void *)stack); | ||
180 | } else { | ||
181 | /* rbp is the last reg pushed by switch_to(): */ | ||
182 | stack = task->thread.rsp; | ||
183 | pr_debug("other task rsp: %p\n", (void *)stack); | ||
184 | stack = (unsigned long)(((unsigned long *)stack)[0]); | ||
185 | pr_debug("other task rbp: %p\n", (void *)stack); | ||
186 | } | ||
187 | |||
188 | while (1) { | ||
189 | unsigned long stack_end = get_stack_end(task, stack); | ||
190 | |||
191 | pr_debug("stack: %p\n", (void *)stack); | ||
192 | pr_debug("stack end: %p\n", (void *)stack_end); | ||
193 | |||
194 | /* | ||
195 | * Invalid stack addres? | ||
196 | */ | ||
197 | if (!stack_end) | ||
198 | return; | ||
199 | /* | ||
200 | * Were we in this stack already? (recursion) | ||
201 | */ | ||
202 | for (i = 0; i < nr_stacks; i++) | ||
203 | if (stacks_done[i] == stack_end) | ||
204 | return; | ||
205 | stacks_done[nr_stacks] = stack_end; | ||
206 | |||
207 | stack = save_context_stack(trace, stack, stack_end); | ||
208 | if (!stack || trace->nr_entries >= trace->max_entries) | ||
209 | return; | ||
210 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
211 | if (trace->nr_entries >= trace->max_entries) | ||
212 | return; | ||
213 | if (++nr_stacks >= MAX_STACKS) | ||
214 | return; | ||
215 | } | ||
216 | } | 53 | } |
54 | EXPORT_SYMBOL(save_stack_trace); | ||
217 | 55 | ||