Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /** |
2 | * @file backtrace.c | |
3 | * | |
4 | * @remark Copyright 2002 OProfile authors | |
5 | * @remark Read the file COPYING | |
6 | * | |
7 | * @author John Levon | |
8 | * @author David Smith | |
9 | */ | |
10 | ||
11 | #include <linux/oprofile.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/mm.h> | |
14 | #include <asm/ptrace.h> | |
c34d1b4d | 15 | #include <asm/uaccess.h> |
1da177e4 LT |
16 | |
17 | struct frame_head { | |
18 | struct frame_head * ebp; | |
19 | unsigned long ret; | |
20 | } __attribute__((packed)); | |
21 | ||
22 | static struct frame_head * | |
30379440 GB |
23 | dump_kernel_backtrace(struct frame_head * head) |
24 | { | |
25 | oprofile_add_trace(head->ret); | |
26 | ||
27 | /* frame pointers should strictly progress back up the stack | |
28 | * (towards higher addresses) */ | |
29 | if (head >= head->ebp) | |
30 | return NULL; | |
31 | ||
32 | return head->ebp; | |
33 | } | |
34 | ||
35 | static struct frame_head * | |
36 | dump_user_backtrace(struct frame_head * head) | |
1da177e4 | 37 | { |
c34d1b4d | 38 | struct frame_head bufhead[2]; |
1da177e4 | 39 | |
c34d1b4d HD |
40 | /* Also check accessibility of one struct frame_head beyond */ |
41 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | |
42 | return NULL; | |
43 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | |
1da177e4 LT |
44 | return NULL; |
45 | ||
c34d1b4d | 46 | oprofile_add_trace(bufhead[0].ret); |
1da177e4 | 47 | |
c34d1b4d HD |
48 | /* frame pointers should strictly progress back up the stack |
49 | * (towards higher addresses) */ | |
50 | if (head >= bufhead[0].ebp) | |
51 | return NULL; | |
1da177e4 | 52 | |
c34d1b4d | 53 | return bufhead[0].ebp; |
1da177e4 LT |
54 | } |
55 | ||
56 | /* | |
57 | * | | /\ Higher addresses | |
58 | * | | | |
59 | * --------------- stack base (address of current_thread_info) | |
60 | * | thread info | | |
61 | * . . | |
62 | * | stack | | |
63 | * --------------- saved regs->ebp value if valid (frame_head address) | |
64 | * . . | |
23332c2e TL |
65 | * --------------- saved regs->rsp value if x86_64 |
66 | * | | | |
67 | * --------------- struct pt_regs * stored on stack if 32-bit | |
1da177e4 LT |
68 | * | | |
69 | * . . | |
70 | * | | | |
71 | * --------------- %esp | |
72 | * | | | |
73 | * | | \/ Lower addresses | |
74 | * | |
23332c2e TL |
75 | * Thus, regs (or regs->rsp for x86_64) <-> stack base restricts the |
76 | * valid(ish) ebp values. Note: (1) for x86_64, NMI and several other | |
77 | * exceptions use special stacks, maintained by the interrupt stack table | |
78 | * (IST). These stacks are set up in trap_init() in | |
79 | * arch/x86_64/kernel/traps.c. Thus, for x86_64, regs now does not point | |
80 | * to the kernel stack; instead, it points to some location on the NMI | |
81 | * stack. On the other hand, regs->rsp is the stack pointer saved when the | |
82 | * NMI occurred. (2) For 32-bit, regs->esp is not valid because the | |
83 | * processor does not save %esp on the kernel stack when interrupts occur | |
84 | * in the kernel mode. | |
1da177e4 LT |
85 | */ |
86 | #ifdef CONFIG_FRAME_POINTER | |
87 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | |
88 | { | |
89 | unsigned long headaddr = (unsigned long)head; | |
23332c2e TL |
90 | #ifdef CONFIG_X86_64 |
91 | unsigned long stack = (unsigned long)regs->rsp; | |
92 | #else | |
1da177e4 | 93 | unsigned long stack = (unsigned long)regs; |
23332c2e | 94 | #endif |
1da177e4 LT |
95 | unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; |
96 | ||
97 | return headaddr > stack && headaddr < stack_base; | |
98 | } | |
99 | #else | |
100 | /* without fp, it's just junk */ | |
101 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | |
102 | { | |
103 | return 0; | |
104 | } | |
105 | #endif | |
106 | ||
107 | ||
108 | void | |
109 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |
110 | { | |
111 | struct frame_head *head; | |
112 | ||
113 | #ifdef CONFIG_X86_64 | |
114 | head = (struct frame_head *)regs->rbp; | |
115 | #else | |
116 | head = (struct frame_head *)regs->ebp; | |
117 | #endif | |
118 | ||
fa1e1bdf | 119 | if (!user_mode_vm(regs)) { |
1da177e4 | 120 | while (depth-- && valid_kernel_stack(head, regs)) |
30379440 | 121 | head = dump_kernel_backtrace(head); |
1da177e4 LT |
122 | return; |
123 | } | |
124 | ||
c34d1b4d | 125 | while (depth-- && head) |
30379440 | 126 | head = dump_user_backtrace(head); |
1da177e4 | 127 | } |