4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
11 #include <linux/oprofile.h>
12 #include <linux/sched.h>
14 #include <asm/ptrace.h>
17 struct frame_head * ebp;
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)
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))
43 return check_user_page_readable(mm, (unsigned long)(head + 1));
47 * | | /\ Higher addresses
49 * --------------- stack base (address of current_thread_info)
53 * --------------- saved regs->ebp value if valid (frame_head address)
55 * --------------- struct pt_regs stored on stack (struct pt_regs *)
59 * --------------- %esp
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;
75 /* without fp, it's just junk */
76 static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
84 x86_backtrace(struct pt_regs * const regs, unsigned int depth)
86 struct frame_head *head;
89 head = (struct frame_head *)regs->rbp;
91 head = (struct frame_head *)regs->ebp;
94 if (!user_mode_vm(regs)) {
95 while (depth-- && valid_kernel_stack(head, regs))
96 head = dump_backtrace(head);
101 if (!spin_trylock(¤t->mm->page_table_lock))
105 while (depth-- && head && pages_present(head))
106 head = dump_backtrace(head);
109 spin_unlock(¤t->mm->page_table_lock);