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