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> | |
a0e3e702 | 14 | #include <linux/compat.h> |
1ac2e6ca | 15 | #include <linux/uaccess.h> |
a0e3e702 | 16 | |
1da177e4 | 17 | #include <asm/ptrace.h> |
574a6042 | 18 | #include <asm/stacktrace.h> |
ec2ad9cc | 19 | #include <asm/unwind.h> |
574a6042 | 20 | |
f6dedecc JO |
21 | #ifdef CONFIG_COMPAT |
22 | static struct stack_frame_ia32 * | |
23 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | |
24 | { | |
a0e3e702 | 25 | /* Also check accessibility of one struct frame_head beyond: */ |
f6dedecc JO |
26 | struct stack_frame_ia32 bufhead[2]; |
27 | struct stack_frame_ia32 *fp; | |
a0e3e702 | 28 | unsigned long bytes; |
f6dedecc | 29 | |
a0e3e702 | 30 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
0a196848 | 31 | if (bytes != 0) |
f6dedecc JO |
32 | return NULL; |
33 | ||
34 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); | |
35 | ||
36 | oprofile_add_trace(bufhead[0].return_address); | |
37 | ||
38 | /* frame pointers should strictly progress back up the stack | |
39 | * (towards higher addresses) */ | |
40 | if (head >= fp) | |
41 | return NULL; | |
42 | ||
43 | return fp; | |
44 | } | |
45 | ||
46 | static inline int | |
47 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | |
48 | { | |
49 | struct stack_frame_ia32 *head; | |
50 | ||
6bd33008 | 51 | /* User process is IA32 */ |
f6dedecc JO |
52 | if (!current || !test_thread_flag(TIF_IA32)) |
53 | return 0; | |
54 | ||
55 | head = (struct stack_frame_ia32 *) regs->bp; | |
56 | while (depth-- && head) | |
57 | head = dump_user_backtrace_32(head); | |
58 | ||
59 | return 1; | |
60 | } | |
61 | ||
62 | #else | |
63 | static inline int | |
64 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | |
65 | { | |
66 | return 0; | |
67 | } | |
68 | #endif /* CONFIG_COMPAT */ | |
69 | ||
40c6b3cb | 70 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) |
1da177e4 | 71 | { |
a0e3e702 | 72 | /* Also check accessibility of one struct frame_head beyond: */ |
40c6b3cb | 73 | struct stack_frame bufhead[2]; |
a0e3e702 | 74 | unsigned long bytes; |
1da177e4 | 75 | |
a0e3e702 | 76 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
0a196848 | 77 | if (bytes != 0) |
1da177e4 LT |
78 | return NULL; |
79 | ||
40c6b3cb | 80 | oprofile_add_trace(bufhead[0].return_address); |
1da177e4 | 81 | |
c34d1b4d HD |
82 | /* frame pointers should strictly progress back up the stack |
83 | * (towards higher addresses) */ | |
40c6b3cb | 84 | if (head >= bufhead[0].next_frame) |
c34d1b4d | 85 | return NULL; |
1da177e4 | 86 | |
40c6b3cb | 87 | return bufhead[0].next_frame; |
1da177e4 LT |
88 | } |
89 | ||
1da177e4 LT |
90 | void |
91 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |
92 | { | |
40c6b3cb | 93 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); |
1da177e4 | 94 | |
f39b6f0e | 95 | if (!user_mode(regs)) { |
ec2ad9cc JP |
96 | struct unwind_state state; |
97 | unsigned long addr; | |
98 | ||
3e344a0d JP |
99 | if (!depth) |
100 | return; | |
101 | ||
102 | oprofile_add_trace(regs->ip); | |
ec2ad9cc | 103 | |
3e344a0d JP |
104 | if (!--depth) |
105 | return; | |
106 | ||
ec2ad9cc JP |
107 | for (unwind_start(&state, current, regs, NULL); |
108 | !unwind_done(&state); unwind_next_frame(&state)) { | |
109 | addr = unwind_get_return_address(&state); | |
110 | if (!addr) | |
111 | break; | |
112 | ||
113 | oprofile_add_trace(addr); | |
114 | ||
115 | if (!--depth) | |
116 | break; | |
117 | } | |
118 | ||
1da177e4 LT |
119 | return; |
120 | } | |
121 | ||
f6dedecc JO |
122 | if (x86_backtrace_32(regs, depth)) |
123 | return; | |
124 | ||
c34d1b4d | 125 | while (depth-- && head) |
30379440 | 126 | head = dump_user_backtrace(head); |
1da177e4 | 127 | } |