Commit | Line | Data |
---|---|---|
c5d4bb17 JD |
1 | /* |
2 | * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | |
9d1ee8ce RW |
3 | * Copyright (C) 2013 Richard Weinberger <richrd@nod.at> |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
1da177e4 LT |
8 | */ |
9 | ||
c5d4bb17 JD |
10 | #include <linux/kallsyms.h> |
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/sched.h> | |
382d95fd | 14 | #include <asm/sysrq.h> |
f72c22e4 | 15 | #include <os.h> |
1da177e4 | 16 | |
9d1ee8ce RW |
17 | struct stack_frame { |
18 | struct stack_frame *next_frame; | |
19 | unsigned long return_address; | |
20 | }; | |
21 | ||
22 | static void print_stack_trace(unsigned long *sp, unsigned long bp) | |
1da177e4 | 23 | { |
9d1ee8ce | 24 | int reliable; |
c5d4bb17 | 25 | unsigned long addr; |
9d1ee8ce | 26 | struct stack_frame *frame = (struct stack_frame *)bp; |
1da177e4 | 27 | |
9d1ee8ce RW |
28 | printk(KERN_INFO "Call Trace:\n"); |
29 | while (((long) sp & (THREAD_SIZE-1)) != 0) { | |
30 | addr = *sp; | |
1da177e4 | 31 | if (__kernel_text_address(addr)) { |
9d1ee8ce RW |
32 | reliable = 0; |
33 | if ((unsigned long) sp == bp + sizeof(long)) { | |
34 | frame = frame ? frame->next_frame : NULL; | |
35 | bp = (unsigned long)frame; | |
36 | reliable = 1; | |
37 | } | |
38 | ||
39 | printk(KERN_INFO " [<%08lx>]", addr); | |
40 | printk(KERN_CONT " %s", reliable ? "" : "? "); | |
41 | print_symbol(KERN_CONT "%s", addr); | |
c5d4bb17 JD |
42 | printk(KERN_CONT "\n"); |
43 | } | |
9d1ee8ce | 44 | sp++; |
c5d4bb17 JD |
45 | } |
46 | printk(KERN_INFO "\n"); | |
1da177e4 LT |
47 | } |
48 | ||
f72c22e4 RW |
49 | static unsigned long get_frame_pointer(struct task_struct *task, |
50 | struct pt_regs *segv_regs) | |
9d1ee8ce RW |
51 | { |
52 | if (!task || task == current) | |
f72c22e4 | 53 | return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); |
9d1ee8ce RW |
54 | else |
55 | return KSTK_EBP(task); | |
56 | } | |
57 | ||
f72c22e4 RW |
58 | static unsigned long *get_stack_pointer(struct task_struct *task, |
59 | struct pt_regs *segv_regs) | |
60 | { | |
61 | if (!task || task == current) | |
62 | return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); | |
63 | else | |
64 | return (unsigned long *)KSTK_ESP(task); | |
65 | } | |
66 | ||
9d1ee8ce | 67 | void show_stack(struct task_struct *task, unsigned long *stack) |
1da177e4 | 68 | { |
9d1ee8ce | 69 | unsigned long *sp = stack, bp = 0; |
f72c22e4 | 70 | struct pt_regs *segv_regs = current->thread.segv_regs; |
1da177e4 LT |
71 | int i; |
72 | ||
f72c22e4 RW |
73 | if (!segv_regs && os_is_signal_stack()) { |
74 | printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler," | |
75 | " aborting stack trace!\n"); | |
76 | return; | |
77 | } | |
78 | ||
9d1ee8ce | 79 | #ifdef CONFIG_FRAME_POINTER |
f72c22e4 | 80 | bp = get_frame_pointer(task, segv_regs); |
9d1ee8ce RW |
81 | #endif |
82 | ||
f72c22e4 RW |
83 | if (!stack) |
84 | sp = get_stack_pointer(task, segv_regs); | |
1da177e4 | 85 | |
9d1ee8ce RW |
86 | printk(KERN_INFO "Stack:\n"); |
87 | stack = sp; | |
e96d1c36 | 88 | for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { |
1da177e4 LT |
89 | if (kstack_end(stack)) |
90 | break; | |
9d1ee8ce RW |
91 | if (i && ((i % STACKSLOTS_PER_LINE) == 0)) |
92 | printk(KERN_CONT "\n"); | |
93 | printk(KERN_CONT " %08lx", *stack++); | |
1da177e4 | 94 | } |
9d1ee8ce | 95 | printk(KERN_CONT "\n"); |
1da177e4 | 96 | |
9d1ee8ce | 97 | print_stack_trace(sp, bp); |
1da177e4 | 98 | } |