aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJan Blunck <jblunck@suse.de>2007-10-19 14:35:03 -0400
committerThomas Gleixner <tglx@linutronix.de>2007-10-19 14:35:03 -0400
commit574a60421c8ea5383a54ebee1f37fa871d00e1b9 (patch)
tree56ecb2678936ee0c3d4b53575804abbb35d13e63 /arch
parent948062683004d13ca21c8c05ac052d387978a449 (diff)
i386: make callgraph use dump_trace() on i386/x86_64
This patch improves oprofile callgraphs for i386/x86_64. The old backtracing code was unable to produce kernel backtraces if the kernel wasn't compiled with framepointers. The code now uses dump_trace(). [ tglx: arch/x86 adaptation ] Signed-off-by: Jan Blunck <jblunck@suse.de> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/oprofile/backtrace.c104
1 files changed, 38 insertions, 66 deletions
diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c
index c049ce414f01..dc59a808009f 100644
--- a/arch/x86/oprofile/backtrace.c
+++ b/arch/x86/oprofile/backtrace.c
@@ -13,25 +13,45 @@
13#include <linux/mm.h> 13#include <linux/mm.h>
14#include <asm/ptrace.h> 14#include <asm/ptrace.h>
15#include <asm/uaccess.h> 15#include <asm/uaccess.h>
16#include <asm/stacktrace.h>
16 17
17struct frame_head { 18static void backtrace_warning_symbol(void *data, char *msg,
18 struct frame_head * ebp; 19 unsigned long symbol)
19 unsigned long ret; 20{
20} __attribute__((packed)); 21 /* Ignore warnings */
22}
21 23
22static struct frame_head * 24static void backtrace_warning(void *data, char *msg)
23dump_kernel_backtrace(struct frame_head * head)
24{ 25{
25 oprofile_add_trace(head->ret); 26 /* Ignore warnings */
27}
26 28
27 /* frame pointers should strictly progress back up the stack 29static int backtrace_stack(void *data, char *name)
28 * (towards higher addresses) */ 30{
29 if (head >= head->ebp) 31 /* Yes, we want all stacks */
30 return NULL; 32 return 0;
33}
31 34
32 return head->ebp; 35static void backtrace_address(void *data, unsigned long addr)
36{
37 unsigned int *depth = data;
38
39 if ((*depth)--)
40 oprofile_add_trace(addr);
33} 41}
34 42
43static struct stacktrace_ops backtrace_ops = {
44 .warning = backtrace_warning,
45 .warning_symbol = backtrace_warning_symbol,
46 .stack = backtrace_stack,
47 .address = backtrace_address,
48};
49
50struct frame_head {
51 struct frame_head *ebp;
52 unsigned long ret;
53} __attribute__((packed));
54
35static struct frame_head * 55static struct frame_head *
36dump_user_backtrace(struct frame_head * head) 56dump_user_backtrace(struct frame_head * head)
37{ 57{
@@ -53,72 +73,24 @@ dump_user_backtrace(struct frame_head * head)
53 return bufhead[0].ebp; 73 return bufhead[0].ebp;
54} 74}
55 75
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
87static 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 */
101static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
102{
103 return 0;
104}
105#endif
106
107
108void 76void
109x86_backtrace(struct pt_regs * const regs, unsigned int depth) 77x86_backtrace(struct pt_regs * const regs, unsigned int depth)
110{ 78{
111 struct frame_head *head; 79 struct frame_head *head;
80 unsigned long stack;
112 81
113#ifdef CONFIG_X86_64 82#ifdef CONFIG_X86_64
114 head = (struct frame_head *)regs->rbp; 83 head = (struct frame_head *)regs->rbp;
84 stack = regs->rsp;
115#else 85#else
116 head = (struct frame_head *)regs->ebp; 86 head = (struct frame_head *)regs->ebp;
87 stack = regs->esp;
117#endif 88#endif
118 89
119 if (!user_mode_vm(regs)) { 90 if (!user_mode_vm(regs)) {
120 while (depth-- && valid_kernel_stack(head, regs)) 91 if (depth)
121 head = dump_kernel_backtrace(head); 92 dump_trace(NULL, regs, (unsigned long *)stack,
93 &backtrace_ops, &depth);
122 return; 94 return;
123 } 95 }
124 96