diff options
Diffstat (limited to 'arch/x86_64/kernel/traps.c')
-rw-r--r-- | arch/x86_64/kernel/traps.c | 99 |
1 files changed, 76 insertions, 23 deletions
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 9ec2b1d5893d..4ac18b02eada 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <asm/pda.h> | 45 | #include <asm/pda.h> |
46 | #include <asm/proto.h> | 46 | #include <asm/proto.h> |
47 | #include <asm/nmi.h> | 47 | #include <asm/nmi.h> |
48 | #include <asm/stacktrace.h> | ||
48 | 49 | ||
49 | asmlinkage void divide_error(void); | 50 | asmlinkage void divide_error(void); |
50 | asmlinkage void debug(void); | 51 | asmlinkage void debug(void); |
@@ -142,7 +143,7 @@ void printk_address(unsigned long address) | |||
142 | #endif | 143 | #endif |
143 | 144 | ||
144 | static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, | 145 | static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, |
145 | unsigned *usedp, const char **idp) | 146 | unsigned *usedp, char **idp) |
146 | { | 147 | { |
147 | static char ids[][8] = { | 148 | static char ids[][8] = { |
148 | [DEBUG_STACK - 1] = "#DB", | 149 | [DEBUG_STACK - 1] = "#DB", |
@@ -234,13 +235,19 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, | |||
234 | return NULL; | 235 | return NULL; |
235 | } | 236 | } |
236 | 237 | ||
237 | static int show_trace_unwind(struct unwind_frame_info *info, void *context) | 238 | struct ops_and_data { |
239 | struct stacktrace_ops *ops; | ||
240 | void *data; | ||
241 | }; | ||
242 | |||
243 | static int dump_trace_unwind(struct unwind_frame_info *info, void *context) | ||
238 | { | 244 | { |
245 | struct ops_and_data *oad = (struct ops_and_data *)context; | ||
239 | int n = 0; | 246 | int n = 0; |
240 | 247 | ||
241 | while (unwind(info) == 0 && UNW_PC(info)) { | 248 | while (unwind(info) == 0 && UNW_PC(info)) { |
242 | n++; | 249 | n++; |
243 | printk_address(UNW_PC(info)); | 250 | oad->ops->address(oad->data, UNW_PC(info)); |
244 | if (arch_unw_user_mode(info)) | 251 | if (arch_unw_user_mode(info)) |
245 | break; | 252 | break; |
246 | } | 253 | } |
@@ -254,45 +261,51 @@ static int show_trace_unwind(struct unwind_frame_info *info, void *context) | |||
254 | * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack | 261 | * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack |
255 | */ | 262 | */ |
256 | 263 | ||
257 | void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * stack) | 264 | void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * stack, |
265 | struct stacktrace_ops *ops, void *data) | ||
258 | { | 266 | { |
259 | const unsigned cpu = safe_smp_processor_id(); | 267 | const unsigned cpu = safe_smp_processor_id(); |
260 | unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; | 268 | unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; |
261 | unsigned used = 0; | 269 | unsigned used = 0; |
262 | 270 | ||
263 | printk("\nCall Trace:\n"); | ||
264 | |||
265 | if (!tsk) | 271 | if (!tsk) |
266 | tsk = current; | 272 | tsk = current; |
267 | 273 | ||
268 | if (call_trace >= 0) { | 274 | if (call_trace >= 0) { |
269 | int unw_ret = 0; | 275 | int unw_ret = 0; |
270 | struct unwind_frame_info info; | 276 | struct unwind_frame_info info; |
277 | struct ops_and_data oad = { .ops = ops, .data = data }; | ||
271 | 278 | ||
272 | if (regs) { | 279 | if (regs) { |
273 | if (unwind_init_frame_info(&info, tsk, regs) == 0) | 280 | if (unwind_init_frame_info(&info, tsk, regs) == 0) |
274 | unw_ret = show_trace_unwind(&info, NULL); | 281 | unw_ret = dump_trace_unwind(&info, &oad); |
275 | } else if (tsk == current) | 282 | } else if (tsk == current) |
276 | unw_ret = unwind_init_running(&info, show_trace_unwind, NULL); | 283 | unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); |
277 | else { | 284 | else { |
278 | if (unwind_init_blocked(&info, tsk) == 0) | 285 | if (unwind_init_blocked(&info, tsk) == 0) |
279 | unw_ret = show_trace_unwind(&info, NULL); | 286 | unw_ret = dump_trace_unwind(&info, &oad); |
280 | } | 287 | } |
281 | if (unw_ret > 0) { | 288 | if (unw_ret > 0) { |
282 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { | 289 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { |
283 | print_symbol("DWARF2 unwinder stuck at %s\n", | 290 | ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", |
284 | UNW_PC(&info)); | 291 | UNW_PC(&info)); |
285 | if ((long)UNW_SP(&info) < 0) { | 292 | if ((long)UNW_SP(&info) < 0) { |
286 | printk("Leftover inexact backtrace:\n"); | 293 | ops->warning(data, "Leftover inexact backtrace:\n"); |
287 | stack = (unsigned long *)UNW_SP(&info); | 294 | stack = (unsigned long *)UNW_SP(&info); |
288 | } else | 295 | } else |
289 | printk("Full inexact backtrace again:\n"); | 296 | ops->warning(data, "Full inexact backtrace again:\n"); |
290 | } else if (call_trace >= 1) | 297 | } else if (call_trace >= 1) |
291 | return; | 298 | return; |
292 | else | 299 | else |
293 | printk("Full inexact backtrace again:\n"); | 300 | ops->warning(data, "Full inexact backtrace again:\n"); |
294 | } else | 301 | } else |
295 | printk("Inexact backtrace:\n"); | 302 | ops->warning(data, "Inexact backtrace:\n"); |
303 | } | ||
304 | if (!stack) { | ||
305 | unsigned long dummy; | ||
306 | stack = &dummy; | ||
307 | if (tsk && tsk != current) | ||
308 | stack = (unsigned long *)tsk->thread.rsp; | ||
296 | } | 309 | } |
297 | 310 | ||
298 | /* | 311 | /* |
@@ -312,7 +325,7 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
312 | * down the cause of the crash will be able to figure \ | 325 | * down the cause of the crash will be able to figure \ |
313 | * out the call path that was taken. \ | 326 | * out the call path that was taken. \ |
314 | */ \ | 327 | */ \ |
315 | printk_address(addr); \ | 328 | ops->address(data, addr); \ |
316 | } \ | 329 | } \ |
317 | } while (0) | 330 | } while (0) |
318 | 331 | ||
@@ -321,16 +334,17 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
321 | * current stack address. If the stacks consist of nested | 334 | * current stack address. If the stacks consist of nested |
322 | * exceptions | 335 | * exceptions |
323 | */ | 336 | */ |
324 | for ( ; ; ) { | 337 | for (;;) { |
325 | const char *id; | 338 | char *id; |
326 | unsigned long *estack_end; | 339 | unsigned long *estack_end; |
327 | estack_end = in_exception_stack(cpu, (unsigned long)stack, | 340 | estack_end = in_exception_stack(cpu, (unsigned long)stack, |
328 | &used, &id); | 341 | &used, &id); |
329 | 342 | ||
330 | if (estack_end) { | 343 | if (estack_end) { |
331 | printk(" <%s>", id); | 344 | if (ops->stack(data, id) < 0) |
345 | break; | ||
332 | HANDLE_STACK (stack < estack_end); | 346 | HANDLE_STACK (stack < estack_end); |
333 | printk(" <EOE>"); | 347 | ops->stack(data, "<EOE>"); |
334 | /* | 348 | /* |
335 | * We link to the next stack via the | 349 | * We link to the next stack via the |
336 | * second-to-last pointer (index -2 to end) in the | 350 | * second-to-last pointer (index -2 to end) in the |
@@ -345,7 +359,8 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
345 | (IRQSTACKSIZE - 64) / sizeof(*irqstack); | 359 | (IRQSTACKSIZE - 64) / sizeof(*irqstack); |
346 | 360 | ||
347 | if (stack >= irqstack && stack < irqstack_end) { | 361 | if (stack >= irqstack && stack < irqstack_end) { |
348 | printk(" <IRQ>"); | 362 | if (ops->stack(data, "IRQ") < 0) |
363 | break; | ||
349 | HANDLE_STACK (stack < irqstack_end); | 364 | HANDLE_STACK (stack < irqstack_end); |
350 | /* | 365 | /* |
351 | * We link to the next stack (which would be | 366 | * We link to the next stack (which would be |
@@ -354,7 +369,7 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
354 | */ | 369 | */ |
355 | stack = (unsigned long *) (irqstack_end[-1]); | 370 | stack = (unsigned long *) (irqstack_end[-1]); |
356 | irqstack_end = NULL; | 371 | irqstack_end = NULL; |
357 | printk(" <EOI>"); | 372 | ops->stack(data, "EOI"); |
358 | continue; | 373 | continue; |
359 | } | 374 | } |
360 | } | 375 | } |
@@ -362,15 +377,53 @@ void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
362 | } | 377 | } |
363 | 378 | ||
364 | /* | 379 | /* |
365 | * This prints the process stack: | 380 | * This handles the process stack: |
366 | */ | 381 | */ |
367 | HANDLE_STACK (((long) stack & (THREAD_SIZE-1)) != 0); | 382 | HANDLE_STACK (((long) stack & (THREAD_SIZE-1)) != 0); |
368 | #undef HANDLE_STACK | 383 | #undef HANDLE_STACK |
384 | } | ||
385 | EXPORT_SYMBOL(dump_trace); | ||
369 | 386 | ||
387 | static void | ||
388 | print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) | ||
389 | { | ||
390 | print_symbol(msg, symbol); | ||
391 | printk("\n"); | ||
392 | } | ||
393 | |||
394 | static void print_trace_warning(void *data, char *msg) | ||
395 | { | ||
396 | printk("%s\n", msg); | ||
397 | } | ||
398 | |||
399 | static int print_trace_stack(void *data, char *name) | ||
400 | { | ||
401 | printk(" <%s> ", name); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static void print_trace_address(void *data, unsigned long addr) | ||
406 | { | ||
407 | printk_address(addr); | ||
408 | } | ||
409 | |||
410 | static struct stacktrace_ops print_trace_ops = { | ||
411 | .warning = print_trace_warning, | ||
412 | .warning_symbol = print_trace_warning_symbol, | ||
413 | .stack = print_trace_stack, | ||
414 | .address = print_trace_address, | ||
415 | }; | ||
416 | |||
417 | void | ||
418 | show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long *stack) | ||
419 | { | ||
420 | printk("\nCall Trace:\n"); | ||
421 | dump_trace(tsk, regs, stack, &print_trace_ops, NULL); | ||
370 | printk("\n"); | 422 | printk("\n"); |
371 | } | 423 | } |
372 | 424 | ||
373 | static void _show_stack(struct task_struct *tsk, struct pt_regs *regs, unsigned long * rsp) | 425 | static void |
426 | _show_stack(struct task_struct *tsk, struct pt_regs *regs, unsigned long *rsp) | ||
374 | { | 427 | { |
375 | unsigned long *stack; | 428 | unsigned long *stack; |
376 | int i; | 429 | int i; |