Linux-2.6.12-rc2
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / arch / i386 / oprofile / backtrace.c
blob52d72e074f7f9589917863c83301bb62331e0294
1 /**
2 * @file backtrace.c
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
7 * @author John Levon
8 * @author David Smith
9 */
11 #include <linux/oprofile.h>
12 #include <linux/sched.h>
13 #include <linux/mm.h>
14 #include <asm/ptrace.h>
16 struct frame_head {
17 struct frame_head * ebp;
18 unsigned long ret;
19 } __attribute__((packed));
21 static struct frame_head *
22 dump_backtrace(struct frame_head * head)
24 oprofile_add_trace(head->ret);
26 /* frame pointers should strictly progress back up the stack
27 * (towards higher addresses) */
28 if (head >= head->ebp)
29 return NULL;
31 return head->ebp;
34 /* check that the page(s) containing the frame head are present */
35 static int pages_present(struct frame_head * head)
37 struct mm_struct * mm = current->mm;
39 /* FIXME: only necessary once per page */
40 if (!check_user_page_readable(mm, (unsigned long)head))
41 return 0;
43 return check_user_page_readable(mm, (unsigned long)(head + 1));
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
63 * Thus, &pt_regs <-> stack base restricts the valid(ish) ebp values
65 #ifdef CONFIG_FRAME_POINTER
66 static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
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;
72 return headaddr > stack && headaddr < stack_base;
74 #else
75 /* without fp, it's just junk */
76 static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
78 return 0;
80 #endif
83 void
84 x86_backtrace(struct pt_regs * const regs, unsigned int depth)
86 struct frame_head *head;
88 #ifdef CONFIG_X86_64
89 head = (struct frame_head *)regs->rbp;
90 #else
91 head = (struct frame_head *)regs->ebp;
92 #endif
94 if (!user_mode(regs)) {
95 while (depth-- && valid_kernel_stack(head, regs))
96 head = dump_backtrace(head);
97 return;
100 #ifdef CONFIG_SMP
101 if (!spin_trylock(&current->mm->page_table_lock))
102 return;
103 #endif
105 while (depth-- && head && pages_present(head))
106 head = dump_backtrace(head);
108 #ifdef CONFIG_SMP
109 spin_unlock(&current->mm->page_table_lock);
110 #endif