diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:16:55 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-11 05:16:55 -0400 |
commit | ff4395654dc6a3a5e35611940047114d4f3d0a7a (patch) | |
tree | 1f17f5160046496c29afeb2872153dcbb939b8a0 /arch/x86/oprofile/backtrace.c | |
parent | 9402e12b8fef1efe9cf949fc020dcda22d9d8667 (diff) |
i386: move oprofile
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/oprofile/backtrace.c')
-rw-r--r-- | arch/x86/oprofile/backtrace.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c new file mode 100644 index 000000000000..c049ce414f01 --- /dev/null +++ b/arch/x86/oprofile/backtrace.c | |||
@@ -0,0 +1,127 @@ | |||
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> | ||
15 | #include <asm/uaccess.h> | ||
16 | |||
17 | struct frame_head { | ||
18 | struct frame_head * ebp; | ||
19 | unsigned long ret; | ||
20 | } __attribute__((packed)); | ||
21 | |||
22 | static struct frame_head * | ||
23 | dump_kernel_backtrace(struct frame_head * head) | ||
24 | { | ||
25 | oprofile_add_trace(head->ret); | ||
26 | |||
27 | /* frame pointers should strictly progress back up the stack | ||
28 | * (towards higher addresses) */ | ||
29 | if (head >= head->ebp) | ||
30 | return NULL; | ||
31 | |||
32 | return head->ebp; | ||
33 | } | ||
34 | |||
35 | static struct frame_head * | ||
36 | dump_user_backtrace(struct frame_head * head) | ||
37 | { | ||
38 | struct frame_head bufhead[2]; | ||
39 | |||
40 | /* Also check accessibility of one struct frame_head beyond */ | ||
41 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | ||
42 | return NULL; | ||
43 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
44 | return NULL; | ||
45 | |||
46 | oprofile_add_trace(bufhead[0].ret); | ||
47 | |||
48 | /* frame pointers should strictly progress back up the stack | ||
49 | * (towards higher addresses) */ | ||
50 | if (head >= bufhead[0].ebp) | ||
51 | return NULL; | ||
52 | |||
53 | return bufhead[0].ebp; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * | | /\ Higher addresses | ||
58 | * | | | ||
59 | * --------------- stack base (address of current_thread_info) | ||
60 | * | thread info | | ||
61 | * . . | ||
62 | * | stack | | ||
63 | * --------------- saved regs->ebp value if valid (frame_head address) | ||
64 | * . . | ||
65 | * --------------- saved regs->rsp value if x86_64 | ||
66 | * | | | ||
67 | * --------------- struct pt_regs * stored on stack if 32-bit | ||
68 | * | | | ||
69 | * . . | ||
70 | * | | | ||
71 | * --------------- %esp | ||
72 | * | | | ||
73 | * | | \/ Lower addresses | ||
74 | * | ||
75 | * Thus, regs (or regs->rsp for x86_64) <-> stack base restricts the | ||
76 | * valid(ish) ebp values. Note: (1) for x86_64, NMI and several other | ||
77 | * exceptions use special stacks, maintained by the interrupt stack table | ||
78 | * (IST). These stacks are set up in trap_init() in | ||
79 | * arch/x86_64/kernel/traps.c. Thus, for x86_64, regs now does not point | ||
80 | * to the kernel stack; instead, it points to some location on the NMI | ||
81 | * stack. On the other hand, regs->rsp is the stack pointer saved when the | ||
82 | * NMI occurred. (2) For 32-bit, regs->esp is not valid because the | ||
83 | * processor does not save %esp on the kernel stack when interrupts occur | ||
84 | * in the kernel mode. | ||
85 | */ | ||
86 | #ifdef CONFIG_FRAME_POINTER | ||
87 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | ||
88 | { | ||
89 | unsigned long headaddr = (unsigned long)head; | ||
90 | #ifdef CONFIG_X86_64 | ||
91 | unsigned long stack = (unsigned long)regs->rsp; | ||
92 | #else | ||
93 | unsigned long stack = (unsigned long)regs; | ||
94 | #endif | ||
95 | unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; | ||
96 | |||
97 | return headaddr > stack && headaddr < stack_base; | ||
98 | } | ||
99 | #else | ||
100 | /* without fp, it's just junk */ | ||
101 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | ||
102 | { | ||
103 | return 0; | ||
104 | } | ||
105 | #endif | ||
106 | |||
107 | |||
108 | void | ||
109 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | ||
110 | { | ||
111 | struct frame_head *head; | ||
112 | |||
113 | #ifdef CONFIG_X86_64 | ||
114 | head = (struct frame_head *)regs->rbp; | ||
115 | #else | ||
116 | head = (struct frame_head *)regs->ebp; | ||
117 | #endif | ||
118 | |||
119 | if (!user_mode_vm(regs)) { | ||
120 | while (depth-- && valid_kernel_stack(head, regs)) | ||
121 | head = dump_kernel_backtrace(head); | ||
122 | return; | ||
123 | } | ||
124 | |||
125 | while (depth-- && head) | ||
126 | head = dump_user_backtrace(head); | ||
127 | } | ||