aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@novell.com>2006-06-26 07:57:32 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 13:48:17 -0400
commitb538ed278b80641ee6d7b75497d5e14de1bb1137 (patch)
treefe508a2f950ec08c832474a253f41b188ceece75
parent4552d5dc08b79868829b4be8951b29b07284753f (diff)
[PATCH] x86_64: reliable stack trace support (x86-64)
These are the x86_64-specific pieces to enable reliable stack traces. The only restriction with this is that it currently cannot unwind across the interrupt->normal stack boundary, as that transition is lacking proper annotation. Signed-off-by: Jan Beulich <jbeulich@novell.com> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/x86_64/kernel/entry.S33
-rw-r--r--arch/x86_64/kernel/process.c2
-rw-r--r--arch/x86_64/kernel/traps.c54
-rw-r--r--arch/x86_64/kernel/vmlinux.lds.S9
-rw-r--r--include/asm-x86_64/proto.h2
-rw-r--r--include/asm-x86_64/unwind.h106
-rw-r--r--lib/Kconfig.debug2
7 files changed, 199 insertions, 9 deletions
diff --git a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S
index 9999d703b6c4..6c68beec51b3 100644
--- a/arch/x86_64/kernel/entry.S
+++ b/arch/x86_64/kernel/entry.S
@@ -1089,3 +1089,36 @@ ENTRY(call_softirq)
1089 ret 1089 ret
1090 CFI_ENDPROC 1090 CFI_ENDPROC
1091ENDPROC(call_softirq) 1091ENDPROC(call_softirq)
1092
1093#ifdef CONFIG_STACK_UNWIND
1094ENTRY(arch_unwind_init_running)
1095 CFI_STARTPROC
1096 movq %r15, R15(%rdi)
1097 movq %r14, R14(%rdi)
1098 xchgq %rsi, %rdx
1099 movq %r13, R13(%rdi)
1100 movq %r12, R12(%rdi)
1101 xorl %eax, %eax
1102 movq %rbp, RBP(%rdi)
1103 movq %rbx, RBX(%rdi)
1104 movq (%rsp), %rcx
1105 movq %rax, R11(%rdi)
1106 movq %rax, R10(%rdi)
1107 movq %rax, R9(%rdi)
1108 movq %rax, R8(%rdi)
1109 movq %rax, RAX(%rdi)
1110 movq %rax, RCX(%rdi)
1111 movq %rax, RDX(%rdi)
1112 movq %rax, RSI(%rdi)
1113 movq %rax, RDI(%rdi)
1114 movq %rax, ORIG_RAX(%rdi)
1115 movq %rcx, RIP(%rdi)
1116 leaq 8(%rsp), %rcx
1117 movq $__KERNEL_CS, CS(%rdi)
1118 movq %rax, EFLAGS(%rdi)
1119 movq %rcx, RSP(%rdi)
1120 movq $__KERNEL_DS, SS(%rdi)
1121 jmpq *%rdx
1122 CFI_ENDPROC
1123ENDPROC(arch_unwind_init_running)
1124#endif
diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c
index dcb77b462348..d6fa41459c80 100644
--- a/arch/x86_64/kernel/process.c
+++ b/arch/x86_64/kernel/process.c
@@ -334,7 +334,7 @@ void show_regs(struct pt_regs *regs)
334{ 334{
335 printk("CPU %d:", smp_processor_id()); 335 printk("CPU %d:", smp_processor_id());
336 __show_regs(regs); 336 __show_regs(regs);
337 show_trace(&regs->rsp); 337 show_trace(NULL, regs, (void *)(regs + 1));
338} 338}
339 339
340/* 340/*
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c
index 9f8f1eff4a6c..eb1534ff1f5f 100644
--- a/arch/x86_64/kernel/traps.c
+++ b/arch/x86_64/kernel/traps.c
@@ -29,6 +29,7 @@
29#include <linux/nmi.h> 29#include <linux/nmi.h>
30#include <linux/kprobes.h> 30#include <linux/kprobes.h>
31#include <linux/kexec.h> 31#include <linux/kexec.h>
32#include <linux/unwind.h>
32 33
33#include <asm/system.h> 34#include <asm/system.h>
34#include <asm/uaccess.h> 35#include <asm/uaccess.h>
@@ -39,7 +40,7 @@
39#include <asm/i387.h> 40#include <asm/i387.h>
40#include <asm/kdebug.h> 41#include <asm/kdebug.h>
41#include <asm/processor.h> 42#include <asm/processor.h>
42 43#include <asm/unwind.h>
43#include <asm/smp.h> 44#include <asm/smp.h>
44#include <asm/pgalloc.h> 45#include <asm/pgalloc.h>
45#include <asm/pda.h> 46#include <asm/pda.h>
@@ -189,6 +190,23 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
189 return NULL; 190 return NULL;
190} 191}
191 192
193static void show_trace_unwind(struct unwind_frame_info *info, void *context)
194{
195 int i = 11;
196
197 while (unwind(info) == 0 && UNW_PC(info)) {
198 if (i > 50) {
199 printk("\n ");
200 i = 7;
201 } else
202 i += printk(" ");
203 i += printk_address(UNW_PC(info));
204 if (arch_unw_user_mode(info))
205 break;
206 }
207 printk("\n");
208}
209
192/* 210/*
193 * x86-64 can have upto three kernel stacks: 211 * x86-64 can have upto three kernel stacks:
194 * process stack 212 * process stack
@@ -196,15 +214,34 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
196 * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack 214 * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
197 */ 215 */
198 216
199void show_trace(unsigned long *stack) 217void show_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * stack)
200{ 218{
201 const unsigned cpu = safe_smp_processor_id(); 219 const unsigned cpu = safe_smp_processor_id();
202 unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; 220 unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr;
203 int i; 221 int i;
204 unsigned used = 0; 222 unsigned used = 0;
223 struct unwind_frame_info info;
205 224
206 printk("\nCall Trace:"); 225 printk("\nCall Trace:");
207 226
227 if (!tsk)
228 tsk = current;
229
230 if (regs) {
231 if (unwind_init_frame_info(&info, tsk, regs) == 0) {
232 show_trace_unwind(&info, NULL);
233 return;
234 }
235 } else if (tsk == current) {
236 if (unwind_init_running(&info, show_trace_unwind, NULL) == 0)
237 return;
238 } else {
239 if (unwind_init_blocked(&info, tsk) == 0) {
240 show_trace_unwind(&info, NULL);
241 return;
242 }
243 }
244
208#define HANDLE_STACK(cond) \ 245#define HANDLE_STACK(cond) \
209 do while (cond) { \ 246 do while (cond) { \
210 unsigned long addr = *stack++; \ 247 unsigned long addr = *stack++; \
@@ -262,7 +299,7 @@ void show_trace(unsigned long *stack)
262 printk("\n"); 299 printk("\n");
263} 300}
264 301
265void show_stack(struct task_struct *tsk, unsigned long * rsp) 302static void _show_stack(struct task_struct *tsk, struct pt_regs *regs, unsigned long * rsp)
266{ 303{
267 unsigned long *stack; 304 unsigned long *stack;
268 int i; 305 int i;
@@ -296,7 +333,12 @@ void show_stack(struct task_struct *tsk, unsigned long * rsp)
296 printk("%016lx ", *stack++); 333 printk("%016lx ", *stack++);
297 touch_nmi_watchdog(); 334 touch_nmi_watchdog();
298 } 335 }
299 show_trace((unsigned long *)rsp); 336 show_trace(tsk, regs, rsp);
337}
338
339void show_stack(struct task_struct *tsk, unsigned long * rsp)
340{
341 _show_stack(tsk, NULL, rsp);
300} 342}
301 343
302/* 344/*
@@ -305,7 +347,7 @@ void show_stack(struct task_struct *tsk, unsigned long * rsp)
305void dump_stack(void) 347void dump_stack(void)
306{ 348{
307 unsigned long dummy; 349 unsigned long dummy;
308 show_trace(&dummy); 350 show_trace(NULL, NULL, &dummy);
309} 351}
310 352
311EXPORT_SYMBOL(dump_stack); 353EXPORT_SYMBOL(dump_stack);
@@ -332,7 +374,7 @@ void show_registers(struct pt_regs *regs)
332 if (in_kernel) { 374 if (in_kernel) {
333 375
334 printk("Stack: "); 376 printk("Stack: ");
335 show_stack(NULL, (unsigned long*)rsp); 377 _show_stack(NULL, regs, (unsigned long*)rsp);
336 378
337 printk("\nCode: "); 379 printk("\nCode: ");
338 if (regs->rip < PAGE_OFFSET) 380 if (regs->rip < PAGE_OFFSET)
diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S
index 5968c2415da9..1c6a5f322919 100644
--- a/arch/x86_64/kernel/vmlinux.lds.S
+++ b/arch/x86_64/kernel/vmlinux.lds.S
@@ -45,6 +45,15 @@ SECTIONS
45 45
46 RODATA 46 RODATA
47 47
48#ifdef CONFIG_STACK_UNWIND
49 . = ALIGN(8);
50 .eh_frame : AT(ADDR(.eh_frame) - LOAD_OFFSET) {
51 __start_unwind = .;
52 *(.eh_frame)
53 __end_unwind = .;
54 }
55#endif
56
48 /* Data */ 57 /* Data */
49 .data : AT(ADDR(.data) - LOAD_OFFSET) { 58 .data : AT(ADDR(.data) - LOAD_OFFSET) {
50 *(.data) 59 *(.data)
diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h
index 1064533e0959..105476ff8039 100644
--- a/include/asm-x86_64/proto.h
+++ b/include/asm-x86_64/proto.h
@@ -75,7 +75,7 @@ extern void main_timer_handler(struct pt_regs *regs);
75 75
76extern unsigned long end_pfn_map; 76extern unsigned long end_pfn_map;
77 77
78extern void show_trace(unsigned long * rsp); 78extern void show_trace(struct task_struct *, struct pt_regs *, unsigned long * rsp);
79extern void show_registers(struct pt_regs *regs); 79extern void show_registers(struct pt_regs *regs);
80 80
81extern void exception_table_check(void); 81extern void exception_table_check(void);
diff --git a/include/asm-x86_64/unwind.h b/include/asm-x86_64/unwind.h
new file mode 100644
index 000000000000..4f61de246179
--- /dev/null
+++ b/include/asm-x86_64/unwind.h
@@ -0,0 +1,106 @@
1#ifndef _ASM_X86_64_UNWIND_H
2#define _ASM_X86_64_UNWIND_H
3
4/*
5 * Copyright (C) 2002-2006 Novell, Inc.
6 * Jan Beulich <jbeulich@novell.com>
7 * This code is released under version 2 of the GNU GPL.
8 */
9
10#ifdef CONFIG_STACK_UNWIND
11
12#include <linux/sched.h>
13#include <asm/ptrace.h>
14#include <asm/uaccess.h>
15#include <asm/vsyscall.h>
16
17struct unwind_frame_info
18{
19 struct pt_regs regs;
20 struct task_struct *task;
21};
22
23#define UNW_PC(frame) (frame)->regs.rip
24#define UNW_SP(frame) (frame)->regs.rsp
25#ifdef CONFIG_FRAME_POINTER
26#define UNW_FP(frame) (frame)->regs.rbp
27#define FRAME_RETADDR_OFFSET 8
28#define FRAME_LINK_OFFSET 0
29#define STACK_BOTTOM(tsk) (((tsk)->thread.rsp0 - 1) & ~(THREAD_SIZE - 1))
30#define STACK_TOP(tsk) ((tsk)->thread.rsp0)
31#endif
32/* Might need to account for the special exception and interrupt handling
33 stacks here, since normally
34 EXCEPTION_STACK_ORDER < THREAD_ORDER < IRQSTACK_ORDER,
35 but the construct is needed only for getting across the stack switch to
36 the interrupt stack - thus considering the IRQ stack itself is unnecessary,
37 and the overhead of comparing against all exception handling stacks seems
38 not desirable. */
39#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1))
40
41#define UNW_REGISTER_INFO \
42 PTREGS_INFO(rax), \
43 PTREGS_INFO(rdx), \
44 PTREGS_INFO(rcx), \
45 PTREGS_INFO(rbx), \
46 PTREGS_INFO(rsi), \
47 PTREGS_INFO(rdi), \
48 PTREGS_INFO(rbp), \
49 PTREGS_INFO(rsp), \
50 PTREGS_INFO(r8), \
51 PTREGS_INFO(r9), \
52 PTREGS_INFO(r10), \
53 PTREGS_INFO(r11), \
54 PTREGS_INFO(r12), \
55 PTREGS_INFO(r13), \
56 PTREGS_INFO(r14), \
57 PTREGS_INFO(r15), \
58 PTREGS_INFO(rip)
59
60static inline void arch_unw_init_frame_info(struct unwind_frame_info *info,
61 /*const*/ struct pt_regs *regs)
62{
63 info->regs = *regs;
64}
65
66static inline void arch_unw_init_blocked(struct unwind_frame_info *info)
67{
68 extern const char thread_return[];
69
70 memset(&info->regs, 0, sizeof(info->regs));
71 info->regs.rip = (unsigned long)thread_return;
72 info->regs.cs = __KERNEL_CS;
73 __get_user(info->regs.rbp, (unsigned long *)info->task->thread.rsp);
74 info->regs.rsp = info->task->thread.rsp;
75 info->regs.ss = __KERNEL_DS;
76}
77
78extern void arch_unwind_init_running(struct unwind_frame_info *,
79 void (*callback)(struct unwind_frame_info *,
80 void *arg),
81 void *arg);
82
83static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
84{
85#if 0 /* This can only work when selector register saves/restores
86 are properly annotated (and tracked in UNW_REGISTER_INFO). */
87 return user_mode(&info->regs);
88#else
89 return (long)info->regs.rip >= 0
90 || (info->regs.rip >= VSYSCALL_START && info->regs.rip < VSYSCALL_END)
91 || (long)info->regs.rsp >= 0;
92#endif
93}
94
95#else
96
97#define UNW_PC(frame) ((void)(frame), 0)
98
99static inline int arch_unw_user_mode(const void *info)
100{
101 return 0;
102}
103
104#endif
105
106#endif /* _ASM_X86_64_UNWIND_H */
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 256b3b805c5c..bbaed84a686b 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -199,7 +199,7 @@ config UNWIND_INFO
199config STACK_UNWIND 199config STACK_UNWIND
200 bool "Stack unwind support" 200 bool "Stack unwind support"
201 depends on UNWIND_INFO 201 depends on UNWIND_INFO
202 depends on n 202 depends on X86_64
203 help 203 help
204 This enables more precise stack traces, omitting all unrelated 204 This enables more precise stack traces, omitting all unrelated
205 occurrences of pointers into kernel code from the dump. 205 occurrences of pointers into kernel code from the dump.