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> |
1da177e4 | 19 | |
574a6042 JB |
20 | static int backtrace_stack(void *data, char *name) |
21 | { | |
22 | /* Yes, we want all stacks */ | |
23 | return 0; | |
24 | } | |
30379440 | 25 | |
568b329a | 26 | static int backtrace_address(void *data, unsigned long addr, int reliable) |
574a6042 JB |
27 | { |
28 | unsigned int *depth = data; | |
29 | ||
30 | if ((*depth)--) | |
31 | oprofile_add_trace(addr); | |
568b329a | 32 | return 0; |
30379440 GB |
33 | } |
34 | ||
574a6042 | 35 | static struct stacktrace_ops backtrace_ops = { |
61c1917f FW |
36 | .stack = backtrace_stack, |
37 | .address = backtrace_address, | |
38 | .walk_stack = print_context_stack, | |
574a6042 JB |
39 | }; |
40 | ||
f6dedecc JO |
41 | #ifdef CONFIG_COMPAT |
42 | static struct stack_frame_ia32 * | |
43 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | |
44 | { | |
a0e3e702 | 45 | /* Also check accessibility of one struct frame_head beyond: */ |
f6dedecc JO |
46 | struct stack_frame_ia32 bufhead[2]; |
47 | struct stack_frame_ia32 *fp; | |
a0e3e702 | 48 | unsigned long bytes; |
f6dedecc | 49 | |
a0e3e702 | 50 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
0a196848 | 51 | if (bytes != 0) |
f6dedecc JO |
52 | return NULL; |
53 | ||
54 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); | |
55 | ||
56 | oprofile_add_trace(bufhead[0].return_address); | |
57 | ||
58 | /* frame pointers should strictly progress back up the stack | |
59 | * (towards higher addresses) */ | |
60 | if (head >= fp) | |
61 | return NULL; | |
62 | ||
63 | return fp; | |
64 | } | |
65 | ||
66 | static inline int | |
67 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | |
68 | { | |
69 | struct stack_frame_ia32 *head; | |
70 | ||
6bd33008 | 71 | /* User process is IA32 */ |
f6dedecc JO |
72 | if (!current || !test_thread_flag(TIF_IA32)) |
73 | return 0; | |
74 | ||
75 | head = (struct stack_frame_ia32 *) regs->bp; | |
76 | while (depth-- && head) | |
77 | head = dump_user_backtrace_32(head); | |
78 | ||
79 | return 1; | |
80 | } | |
81 | ||
82 | #else | |
83 | static inline int | |
84 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | |
85 | { | |
86 | return 0; | |
87 | } | |
88 | #endif /* CONFIG_COMPAT */ | |
89 | ||
40c6b3cb | 90 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) |
1da177e4 | 91 | { |
a0e3e702 | 92 | /* Also check accessibility of one struct frame_head beyond: */ |
40c6b3cb | 93 | struct stack_frame bufhead[2]; |
a0e3e702 | 94 | unsigned long bytes; |
1da177e4 | 95 | |
a0e3e702 | 96 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
0a196848 | 97 | if (bytes != 0) |
1da177e4 LT |
98 | return NULL; |
99 | ||
40c6b3cb | 100 | oprofile_add_trace(bufhead[0].return_address); |
1da177e4 | 101 | |
c34d1b4d HD |
102 | /* frame pointers should strictly progress back up the stack |
103 | * (towards higher addresses) */ | |
40c6b3cb | 104 | if (head >= bufhead[0].next_frame) |
c34d1b4d | 105 | return NULL; |
1da177e4 | 106 | |
40c6b3cb | 107 | return bufhead[0].next_frame; |
1da177e4 LT |
108 | } |
109 | ||
1da177e4 LT |
110 | void |
111 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |
112 | { | |
40c6b3cb | 113 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); |
1da177e4 | 114 | |
f39b6f0e | 115 | if (!user_mode(regs)) { |
7b6c6c77 | 116 | unsigned long stack = kernel_stack_pointer(regs); |
574a6042 | 117 | if (depth) |
e8e999cf | 118 | dump_trace(NULL, regs, (unsigned long *)stack, 0, |
574a6042 | 119 | &backtrace_ops, &depth); |
1da177e4 LT |
120 | return; |
121 | } | |
122 | ||
f6dedecc JO |
123 | if (x86_backtrace_32(regs, depth)) |
124 | return; | |
125 | ||
c34d1b4d | 126 | while (depth-- && head) |
30379440 | 127 | head = dump_user_backtrace(head); |
1da177e4 | 128 | } |