diff options
Diffstat (limited to 'arch/x86/oprofile/backtrace.c')
-rw-r--r-- | arch/x86/oprofile/backtrace.c | 83 |
1 files changed, 59 insertions, 24 deletions
diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index 3855096c59b8..a5b64ab4cd6e 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c | |||
@@ -14,17 +14,7 @@ | |||
14 | #include <asm/ptrace.h> | 14 | #include <asm/ptrace.h> |
15 | #include <asm/uaccess.h> | 15 | #include <asm/uaccess.h> |
16 | #include <asm/stacktrace.h> | 16 | #include <asm/stacktrace.h> |
17 | 17 | #include <linux/compat.h> | |
18 | static void backtrace_warning_symbol(void *data, char *msg, | ||
19 | unsigned long symbol) | ||
20 | { | ||
21 | /* Ignore warnings */ | ||
22 | } | ||
23 | |||
24 | static void backtrace_warning(void *data, char *msg) | ||
25 | { | ||
26 | /* Ignore warnings */ | ||
27 | } | ||
28 | 18 | ||
29 | static int backtrace_stack(void *data, char *name) | 19 | static int backtrace_stack(void *data, char *name) |
30 | { | 20 | { |
@@ -41,21 +31,17 @@ static void backtrace_address(void *data, unsigned long addr, int reliable) | |||
41 | } | 31 | } |
42 | 32 | ||
43 | static struct stacktrace_ops backtrace_ops = { | 33 | static struct stacktrace_ops backtrace_ops = { |
44 | .warning = backtrace_warning, | ||
45 | .warning_symbol = backtrace_warning_symbol, | ||
46 | .stack = backtrace_stack, | 34 | .stack = backtrace_stack, |
47 | .address = backtrace_address, | 35 | .address = backtrace_address, |
48 | .walk_stack = print_context_stack, | 36 | .walk_stack = print_context_stack, |
49 | }; | 37 | }; |
50 | 38 | ||
51 | struct frame_head { | 39 | #ifdef CONFIG_COMPAT |
52 | struct frame_head *bp; | 40 | static struct stack_frame_ia32 * |
53 | unsigned long ret; | 41 | dump_user_backtrace_32(struct stack_frame_ia32 *head) |
54 | } __attribute__((packed)); | ||
55 | |||
56 | static struct frame_head *dump_user_backtrace(struct frame_head *head) | ||
57 | { | 42 | { |
58 | struct frame_head bufhead[2]; | 43 | struct stack_frame_ia32 bufhead[2]; |
44 | struct stack_frame_ia32 *fp; | ||
59 | 45 | ||
60 | /* Also check accessibility of one struct frame_head beyond */ | 46 | /* Also check accessibility of one struct frame_head beyond */ |
61 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 47 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) |
@@ -63,20 +49,66 @@ static struct frame_head *dump_user_backtrace(struct frame_head *head) | |||
63 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | 49 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) |
64 | return NULL; | 50 | return NULL; |
65 | 51 | ||
66 | oprofile_add_trace(bufhead[0].ret); | 52 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); |
53 | |||
54 | oprofile_add_trace(bufhead[0].return_address); | ||
55 | |||
56 | /* frame pointers should strictly progress back up the stack | ||
57 | * (towards higher addresses) */ | ||
58 | if (head >= fp) | ||
59 | return NULL; | ||
60 | |||
61 | return fp; | ||
62 | } | ||
63 | |||
64 | static inline int | ||
65 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | ||
66 | { | ||
67 | struct stack_frame_ia32 *head; | ||
68 | |||
69 | /* User process is 32-bit */ | ||
70 | if (!current || !test_thread_flag(TIF_IA32)) | ||
71 | return 0; | ||
72 | |||
73 | head = (struct stack_frame_ia32 *) regs->bp; | ||
74 | while (depth-- && head) | ||
75 | head = dump_user_backtrace_32(head); | ||
76 | |||
77 | return 1; | ||
78 | } | ||
79 | |||
80 | #else | ||
81 | static inline int | ||
82 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | ||
83 | { | ||
84 | return 0; | ||
85 | } | ||
86 | #endif /* CONFIG_COMPAT */ | ||
87 | |||
88 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | ||
89 | { | ||
90 | struct stack_frame bufhead[2]; | ||
91 | |||
92 | /* Also check accessibility of one struct stack_frame beyond */ | ||
93 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | ||
94 | return NULL; | ||
95 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
96 | return NULL; | ||
97 | |||
98 | oprofile_add_trace(bufhead[0].return_address); | ||
67 | 99 | ||
68 | /* frame pointers should strictly progress back up the stack | 100 | /* frame pointers should strictly progress back up the stack |
69 | * (towards higher addresses) */ | 101 | * (towards higher addresses) */ |
70 | if (head >= bufhead[0].bp) | 102 | if (head >= bufhead[0].next_frame) |
71 | return NULL; | 103 | return NULL; |
72 | 104 | ||
73 | return bufhead[0].bp; | 105 | return bufhead[0].next_frame; |
74 | } | 106 | } |
75 | 107 | ||
76 | void | 108 | void |
77 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | 109 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) |
78 | { | 110 | { |
79 | struct frame_head *head = (struct frame_head *)frame_pointer(regs); | 111 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); |
80 | 112 | ||
81 | if (!user_mode_vm(regs)) { | 113 | if (!user_mode_vm(regs)) { |
82 | unsigned long stack = kernel_stack_pointer(regs); | 114 | unsigned long stack = kernel_stack_pointer(regs); |
@@ -86,6 +118,9 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |||
86 | return; | 118 | return; |
87 | } | 119 | } |
88 | 120 | ||
121 | if (x86_backtrace_32(regs, depth)) | ||
122 | return; | ||
123 | |||
89 | while (depth-- && head) | 124 | while (depth-- && head) |
90 | head = dump_user_backtrace(head); | 125 | head = dump_user_backtrace(head); |
91 | } | 126 | } |