Linux-2.6.12-rc2
[linux-2.6.git] / arch / i386 / oprofile / backtrace.c
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
16 struct frame_head {
17         struct frame_head * ebp;
18         unsigned long ret;
19 } __attribute__((packed));
20
21 static struct frame_head *
22 dump_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 */
35 static 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
66 static 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 */
76 static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
77 {
78         return 0;
79 }
80 #endif
81
82
83 void
84 x86_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 }