| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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> | 
| Hugh Dickins | c34d1b4 | 2005-10-29 18:16:32 -0700 | [diff] [blame] | 15 | #include <asm/uaccess.h> | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 16 | #include <asm/stacktrace.h> | 
| Jiri Olsa | f6dedec | 2010-09-29 10:46:47 -0400 | [diff] [blame] | 17 | #include <linux/compat.h> | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 18 |  | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 19 | static int backtrace_stack(void *data, char *name) | 
|  | 20 | { | 
|  | 21 | /* Yes, we want all stacks */ | 
|  | 22 | return 0; | 
|  | 23 | } | 
|  | 24 |  | 
| Arjan van de Ven | bc850d6 | 2008-01-30 13:33:07 +0100 | [diff] [blame] | 25 | static void backtrace_address(void *data, unsigned long addr, int reliable) | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 26 | { | 
|  | 27 | unsigned int *depth = data; | 
|  | 28 |  | 
|  | 29 | if ((*depth)--) | 
|  | 30 | oprofile_add_trace(addr); | 
|  | 31 | } | 
|  | 32 |  | 
|  | 33 | static struct stacktrace_ops backtrace_ops = { | 
| Frederic Weisbecker | 61c1917 | 2009-12-17 05:40:33 +0100 | [diff] [blame] | 34 | .stack		= backtrace_stack, | 
|  | 35 | .address	= backtrace_address, | 
|  | 36 | .walk_stack	= print_context_stack, | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 37 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 |  | 
| Jiri Olsa | f6dedec | 2010-09-29 10:46:47 -0400 | [diff] [blame] | 39 | #ifdef CONFIG_COMPAT | 
|  | 40 | static struct stack_frame_ia32 * | 
|  | 41 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | 
|  | 42 | { | 
|  | 43 | struct stack_frame_ia32 bufhead[2]; | 
|  | 44 | struct stack_frame_ia32 *fp; | 
|  | 45 |  | 
|  | 46 | /* Also check accessibility of one struct frame_head beyond */ | 
|  | 47 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 
|  | 48 | return NULL; | 
|  | 49 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | 
|  | 50 | return NULL; | 
|  | 51 |  | 
|  | 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 |  | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 88 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 89 | { | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 90 | struct stack_frame bufhead[2]; | 
| Hugh Dickins | c34d1b4 | 2005-10-29 18:16:32 -0700 | [diff] [blame] | 91 |  | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 92 | /* Also check accessibility of one struct stack_frame beyond */ | 
| Hugh Dickins | c34d1b4 | 2005-10-29 18:16:32 -0700 | [diff] [blame] | 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 |  | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 98 | oprofile_add_trace(bufhead[0].return_address); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 |  | 
|  | 100 | /* frame pointers should strictly progress back up the stack | 
|  | 101 | * (towards higher addresses) */ | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 102 | if (head >= bufhead[0].next_frame) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | return NULL; | 
|  | 104 |  | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 105 | return bufhead[0].next_frame; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | } | 
|  | 107 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | void | 
|  | 109 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | 
|  | 110 | { | 
| Jiri Olsa | 40c6b3c | 2010-09-29 10:46:46 -0400 | [diff] [blame] | 111 | struct stack_frame *head = (struct stack_frame *)frame_pointer(regs); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 |  | 
| Vincent Hanquez | fa1e1bd | 2005-06-23 00:08:44 -0700 | [diff] [blame] | 113 | if (!user_mode_vm(regs)) { | 
| Masami Hiramatsu | 7b6c6c7 | 2009-05-11 17:03:00 -0400 | [diff] [blame] | 114 | unsigned long stack = kernel_stack_pointer(regs); | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 115 | if (depth) | 
| Namhyung Kim | e8e999c | 2011-03-18 11:40:06 +0900 | [diff] [blame] | 116 | dump_trace(NULL, regs, (unsigned long *)stack, 0, | 
| Jan Blunck | 574a604 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 117 | &backtrace_ops, &depth); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | return; | 
|  | 119 | } | 
|  | 120 |  | 
| Jiri Olsa | f6dedec | 2010-09-29 10:46:47 -0400 | [diff] [blame] | 121 | if (x86_backtrace_32(regs, depth)) | 
|  | 122 | return; | 
|  | 123 |  | 
| Hugh Dickins | c34d1b4 | 2005-10-29 18:16:32 -0700 | [diff] [blame] | 124 | while (depth-- && head) | 
| Gerald Britton | 3037944 | 2006-02-14 10:19:04 -0500 | [diff] [blame] | 125 | head = dump_user_backtrace(head); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 126 | } |