aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/oprofile/backtrace.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/i386/oprofile/backtrace.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/i386/oprofile/backtrace.c')
-rw-r--r--arch/i386/oprofile/backtrace.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/arch/i386/oprofile/backtrace.c b/arch/i386/oprofile/backtrace.c
new file mode 100644
index 000000000000..52d72e074f7f
--- /dev/null
+++ b/arch/i386/oprofile/backtrace.c
@@ -0,0 +1,111 @@
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
16struct frame_head {
17 struct frame_head * ebp;
18 unsigned long ret;
19} __attribute__((packed));
20
21static struct frame_head *
22dump_backtrace(struct frame_head * head)
23{
24 oprofile_add_trace(head->ret);
25
26 /* frame pointers should strictly progress back up the stack
27 * (towards higher addresses) */
28 if (head >= head->ebp)
29 return NULL;
30
31 return head->ebp;
32}
33
34/* check that the page(s) containing the frame head are present */
35static int pages_present(struct frame_head * head)
36{
37 struct mm_struct * mm = current->mm;
38
39 /* FIXME: only necessary once per page */
40 if (!check_user_page_readable(mm, (unsigned long)head))
41 return 0;
42
43 return check_user_page_readable(mm, (unsigned long)(head + 1));
44}
45
46/*
47 * | | /\ Higher addresses
48 * | |
49 * --------------- stack base (address of current_thread_info)
50 * | thread info |
51 * . .
52 * | stack |
53 * --------------- saved regs->ebp value if valid (frame_head address)
54 * . .
55 * --------------- struct pt_regs stored on stack (struct pt_regs *)
56 * | |
57 * . .
58 * | |
59 * --------------- %esp
60 * | |
61 * | | \/ Lower addresses
62 *
63 * Thus, &pt_regs <-> stack base restricts the valid(ish) ebp values
64 */
65#ifdef CONFIG_FRAME_POINTER
66static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
67{
68 unsigned long headaddr = (unsigned long)head;
69 unsigned long stack = (unsigned long)regs;
70 unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
71
72 return headaddr > stack && headaddr < stack_base;
73}
74#else
75/* without fp, it's just junk */
76static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
77{
78 return 0;
79}
80#endif
81
82
83void
84x86_backtrace(struct pt_regs * const regs, unsigned int depth)
85{
86 struct frame_head *head;
87
88#ifdef CONFIG_X86_64
89 head = (struct frame_head *)regs->rbp;
90#else
91 head = (struct frame_head *)regs->ebp;
92#endif
93
94 if (!user_mode(regs)) {
95 while (depth-- && valid_kernel_stack(head, regs))
96 head = dump_backtrace(head);
97 return;
98 }
99
100#ifdef CONFIG_SMP
101 if (!spin_trylock(&current->mm->page_table_lock))
102 return;
103#endif
104
105 while (depth-- && head && pages_present(head))
106 head = dump_backtrace(head);
107
108#ifdef CONFIG_SMP
109 spin_unlock(&current->mm->page_table_lock);
110#endif
111}