diff options
author | Jan Blunck <jblunck@suse.de> | 2007-10-19 14:35:03 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2007-10-19 14:35:03 -0400 |
commit | 574a60421c8ea5383a54ebee1f37fa871d00e1b9 (patch) | |
tree | 56ecb2678936ee0c3d4b53575804abbb35d13e63 /arch/x86/oprofile | |
parent | 948062683004d13ca21c8c05ac052d387978a449 (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/x86/oprofile')
-rw-r--r-- | arch/x86/oprofile/backtrace.c | 104 |
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 | ||
17 | struct frame_head { | 18 | static 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 | ||
22 | static struct frame_head * | 24 | static void backtrace_warning(void *data, char *msg) |
23 | dump_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 | 29 | static 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; | 35 | static 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 | ||
43 | static 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 | |||
50 | struct frame_head { | ||
51 | struct frame_head *ebp; | ||
52 | unsigned long ret; | ||
53 | } __attribute__((packed)); | ||
54 | |||
35 | static struct frame_head * | 55 | static struct frame_head * |
36 | dump_user_backtrace(struct frame_head * head) | 56 | dump_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 | ||
87 | static 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 */ | ||
101 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | ||
102 | { | ||
103 | return 0; | ||
104 | } | ||
105 | #endif | ||
106 | |||
107 | |||
108 | void | 76 | void |
109 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | 77 | x86_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 | ||