diff options
| author | Ingo Molnar <mingo@elte.hu> | 2010-10-16 14:17:25 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2010-10-16 14:17:25 -0400 |
| commit | f92f6e6ee35d2779aa62e70f78ad8e1cd417eb52 (patch) | |
| tree | 1e8e2ee34678a43d416c4bab58f9ca91673d4444 /arch/x86/oprofile/backtrace.c | |
| parent | 66af86e2c630908b21cec018cb612576cf5f516e (diff) | |
| parent | cd254f295248c98b62ea824f361e10d80a081fe7 (diff) | |
Merge branch 'core' of git://git.kernel.org/pub/scm/linux/kernel/git/rric/oprofile into perf/core
Diffstat (limited to 'arch/x86/oprofile/backtrace.c')
| -rw-r--r-- | arch/x86/oprofile/backtrace.c | 70 |
1 files changed, 59 insertions, 11 deletions
diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index 3855096c59b8..2d49d4e19a36 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c | |||
| @@ -14,6 +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 | #include <linux/compat.h> | ||
| 17 | 18 | ||
| 18 | static void backtrace_warning_symbol(void *data, char *msg, | 19 | static void backtrace_warning_symbol(void *data, char *msg, |
| 19 | unsigned long symbol) | 20 | unsigned long symbol) |
| @@ -48,14 +49,12 @@ static struct stacktrace_ops backtrace_ops = { | |||
| 48 | .walk_stack = print_context_stack, | 49 | .walk_stack = print_context_stack, |
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| 51 | struct frame_head { | 52 | #ifdef CONFIG_COMPAT |
| 52 | struct frame_head *bp; | 53 | static struct stack_frame_ia32 * |
| 53 | unsigned long ret; | 54 | 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 | { | 55 | { |
| 58 | struct frame_head bufhead[2]; | 56 | struct stack_frame_ia32 bufhead[2]; |
| 57 | struct stack_frame_ia32 *fp; | ||
| 59 | 58 | ||
| 60 | /* Also check accessibility of one struct frame_head beyond */ | 59 | /* Also check accessibility of one struct frame_head beyond */ |
| 61 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 60 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) |
| @@ -63,20 +62,66 @@ static struct frame_head *dump_user_backtrace(struct frame_head *head) | |||
| 63 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | 62 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) |
| 64 | return NULL; | 63 | return NULL; |
| 65 | 64 | ||
| 66 | oprofile_add_trace(bufhead[0].ret); | 65 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); |
| 66 | |||
| 67 | oprofile_add_trace(bufhead[0].return_address); | ||
| 68 | |||
| 69 | /* frame pointers should strictly progress back up the stack | ||
| 70 | * (towards higher addresses) */ | ||
| 71 | if (head >= fp) | ||
| 72 | return NULL; | ||
| 73 | |||
| 74 | return fp; | ||
| 75 | } | ||
| 76 | |||
| 77 | static inline int | ||
| 78 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | ||
| 79 | { | ||
| 80 | struct stack_frame_ia32 *head; | ||
| 81 | |||
| 82 | /* User process is 32-bit */ | ||
| 83 | if (!current || !test_thread_flag(TIF_IA32)) | ||
| 84 | return 0; | ||
| 85 | |||
| 86 | head = (struct stack_frame_ia32 *) regs->bp; | ||
| 87 | while (depth-- && head) | ||
| 88 | head = dump_user_backtrace_32(head); | ||
| 89 | |||
| 90 | return 1; | ||
| 91 | } | ||
| 92 | |||
| 93 | #else | ||
| 94 | static inline int | ||
| 95 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | ||
| 96 | { | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | #endif /* CONFIG_COMPAT */ | ||
| 100 | |||
| 101 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | ||
| 102 | { | ||
| 103 | struct stack_frame bufhead[2]; | ||
| 104 | |||
| 105 | /* Also check accessibility of one struct stack_frame beyond */ | ||
| 106 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | ||
| 107 | return NULL; | ||
| 108 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
| 109 | return NULL; | ||
| 110 | |||
| 111 | oprofile_add_trace(bufhead[0].return_address); | ||
| 67 | 112 | ||
| 68 | /* frame pointers should strictly progress back up the stack | 113 | /* frame pointers should strictly progress back up the stack |
| 69 | * (towards higher addresses) */ | 114 | * (towards higher addresses) */ |
| 70 | if (head >= bufhead[0].bp) | 115 | if (head >= bufhead[0].next_frame) |
| 71 | return NULL; | 116 | return NULL; |
| 72 | 117 | ||
| 73 | return bufhead[0].bp; | 118 | return bufhead[0].next_frame; |
| 74 | } | 119 | } |
| 75 | 120 | ||
| 76 | void | 121 | void |
| 77 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | 122 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) |
| 78 | { | 123 | { |
| 79 | struct frame_head *head = (struct frame_head *)frame_pointer(regs); | 124 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); |
| 80 | 125 | ||
| 81 | if (!user_mode_vm(regs)) { | 126 | if (!user_mode_vm(regs)) { |
| 82 | unsigned long stack = kernel_stack_pointer(regs); | 127 | unsigned long stack = kernel_stack_pointer(regs); |
| @@ -86,6 +131,9 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |||
| 86 | return; | 131 | return; |
| 87 | } | 132 | } |
| 88 | 133 | ||
| 134 | if (x86_backtrace_32(regs, depth)) | ||
| 135 | return; | ||
| 136 | |||
| 89 | while (depth-- && head) | 137 | while (depth-- && head) |
| 90 | head = dump_user_backtrace(head); | 138 | head = dump_user_backtrace(head); |
| 91 | } | 139 | } |
