diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2013-04-15 01:20:48 -0400 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2013-05-09 04:07:10 -0400 |
commit | 3e4196a5cc9cff50900f3dc8acc3d41e5963fd50 (patch) | |
tree | 149403b0517e402fbf29ce9aac65a49d1119234e | |
parent | 4e96274e2b7a7463de6849c0411d876182ee8889 (diff) |
xtensa: add stacktrace support
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
-rw-r--r-- | arch/xtensa/Kconfig | 3 | ||||
-rw-r--r-- | arch/xtensa/include/asm/stacktrace.h | 36 | ||||
-rw-r--r-- | arch/xtensa/kernel/Makefile | 6 | ||||
-rw-r--r-- | arch/xtensa/kernel/stacktrace.c | 87 | ||||
-rw-r--r-- | arch/xtensa/kernel/traps.c | 49 |
5 files changed, 140 insertions, 41 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index a9bdb1c5f13d..13280f2780c8 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig | |||
@@ -52,6 +52,9 @@ config HZ | |||
52 | source "init/Kconfig" | 52 | source "init/Kconfig" |
53 | source "kernel/Kconfig.freezer" | 53 | source "kernel/Kconfig.freezer" |
54 | 54 | ||
55 | config STACKTRACE_SUPPORT | ||
56 | def_bool y | ||
57 | |||
55 | config MMU | 58 | config MMU |
56 | def_bool n | 59 | def_bool n |
57 | 60 | ||
diff --git a/arch/xtensa/include/asm/stacktrace.h b/arch/xtensa/include/asm/stacktrace.h new file mode 100644 index 000000000000..6a05fcb0a20d --- /dev/null +++ b/arch/xtensa/include/asm/stacktrace.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * arch/xtensa/include/asm/stacktrace.h | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2001 - 2013 Tensilica Inc. | ||
9 | */ | ||
10 | #ifndef _XTENSA_STACKTRACE_H | ||
11 | #define _XTENSA_STACKTRACE_H | ||
12 | |||
13 | #include <linux/sched.h> | ||
14 | |||
15 | struct stackframe { | ||
16 | unsigned long pc; | ||
17 | unsigned long sp; | ||
18 | }; | ||
19 | |||
20 | static __always_inline unsigned long *stack_pointer(struct task_struct *task) | ||
21 | { | ||
22 | unsigned long *sp; | ||
23 | |||
24 | if (!task || task == current) | ||
25 | __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp)); | ||
26 | else | ||
27 | sp = (unsigned long *)task->thread.sp; | ||
28 | |||
29 | return sp; | ||
30 | } | ||
31 | |||
32 | void walk_stackframe(unsigned long *sp, | ||
33 | int (*fn)(struct stackframe *frame, void *data), | ||
34 | void *data); | ||
35 | |||
36 | #endif /* _XTENSA_STACKTRACE_H */ | ||
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index c433a56fbff9..1e7fc87a94bb 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile | |||
@@ -4,9 +4,9 @@ | |||
4 | 4 | ||
5 | extra-y := head.o vmlinux.lds | 5 | extra-y := head.o vmlinux.lds |
6 | 6 | ||
7 | obj-y := align.o entry.o irq.o coprocessor.o process.o ptrace.o \ | 7 | obj-y := align.o coprocessor.o entry.o irq.o pci-dma.o platform.o process.o \ |
8 | setup.o signal.o syscall.o time.o traps.o vectors.o platform.o \ | 8 | ptrace.o setup.o signal.o stacktrace.o syscall.o time.o traps.o \ |
9 | pci-dma.o | 9 | vectors.o |
10 | 10 | ||
11 | obj-$(CONFIG_KGDB) += xtensa-stub.o | 11 | obj-$(CONFIG_KGDB) += xtensa-stub.o |
12 | obj-$(CONFIG_PCI) += pci.o | 12 | obj-$(CONFIG_PCI) += pci.o |
diff --git a/arch/xtensa/kernel/stacktrace.c b/arch/xtensa/kernel/stacktrace.c new file mode 100644 index 000000000000..e3aff9575183 --- /dev/null +++ b/arch/xtensa/kernel/stacktrace.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * arch/xtensa/kernel/stacktrace.c | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Copyright (C) 2001 - 2013 Tensilica Inc. | ||
9 | */ | ||
10 | #include <linux/export.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/stacktrace.h> | ||
13 | |||
14 | #include <asm/stacktrace.h> | ||
15 | #include <asm/traps.h> | ||
16 | |||
17 | void walk_stackframe(unsigned long *sp, | ||
18 | int (*fn)(struct stackframe *frame, void *data), | ||
19 | void *data) | ||
20 | { | ||
21 | unsigned long a0, a1; | ||
22 | unsigned long sp_end; | ||
23 | |||
24 | a1 = (unsigned long)sp; | ||
25 | sp_end = ALIGN(a1, THREAD_SIZE); | ||
26 | |||
27 | spill_registers(); | ||
28 | |||
29 | while (a1 < sp_end) { | ||
30 | struct stackframe frame; | ||
31 | |||
32 | sp = (unsigned long *)a1; | ||
33 | |||
34 | a0 = *(sp - 4); | ||
35 | a1 = *(sp - 3); | ||
36 | |||
37 | if (a1 <= (unsigned long)sp) | ||
38 | break; | ||
39 | |||
40 | frame.pc = MAKE_PC_FROM_RA(a0, a1); | ||
41 | frame.sp = a1; | ||
42 | |||
43 | if (fn(&frame, data)) | ||
44 | return; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | #ifdef CONFIG_STACKTRACE | ||
49 | |||
50 | struct stack_trace_data { | ||
51 | struct stack_trace *trace; | ||
52 | unsigned skip; | ||
53 | }; | ||
54 | |||
55 | static int stack_trace_cb(struct stackframe *frame, void *data) | ||
56 | { | ||
57 | struct stack_trace_data *trace_data = data; | ||
58 | struct stack_trace *trace = trace_data->trace; | ||
59 | |||
60 | if (trace_data->skip) { | ||
61 | --trace_data->skip; | ||
62 | return 0; | ||
63 | } | ||
64 | if (!kernel_text_address(frame->pc)) | ||
65 | return 0; | ||
66 | |||
67 | trace->entries[trace->nr_entries++] = frame->pc; | ||
68 | return trace->nr_entries >= trace->max_entries; | ||
69 | } | ||
70 | |||
71 | void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace) | ||
72 | { | ||
73 | struct stack_trace_data trace_data = { | ||
74 | .trace = trace, | ||
75 | .skip = trace->skip, | ||
76 | }; | ||
77 | walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data); | ||
78 | } | ||
79 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | ||
80 | |||
81 | void save_stack_trace(struct stack_trace *trace) | ||
82 | { | ||
83 | save_stack_trace_tsk(current, trace); | ||
84 | } | ||
85 | EXPORT_SYMBOL_GPL(save_stack_trace); | ||
86 | |||
87 | #endif | ||
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 923db5c15278..cf065e165ceb 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c | |||
@@ -11,7 +11,7 @@ | |||
11 | * | 11 | * |
12 | * Essentially rewritten for the Xtensa architecture port. | 12 | * Essentially rewritten for the Xtensa architecture port. |
13 | * | 13 | * |
14 | * Copyright (C) 2001 - 2005 Tensilica Inc. | 14 | * Copyright (C) 2001 - 2013 Tensilica Inc. |
15 | * | 15 | * |
16 | * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> | 16 | * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> |
17 | * Chris Zankel <chris@zankel.net> | 17 | * Chris Zankel <chris@zankel.net> |
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/delay.h> | 32 | #include <linux/delay.h> |
33 | #include <linux/hardirq.h> | 33 | #include <linux/hardirq.h> |
34 | 34 | ||
35 | #include <asm/stacktrace.h> | ||
35 | #include <asm/ptrace.h> | 36 | #include <asm/ptrace.h> |
36 | #include <asm/timex.h> | 37 | #include <asm/timex.h> |
37 | #include <asm/uaccess.h> | 38 | #include <asm/uaccess.h> |
@@ -402,53 +403,25 @@ void show_regs(struct pt_regs * regs) | |||
402 | regs->syscall); | 403 | regs->syscall); |
403 | } | 404 | } |
404 | 405 | ||
405 | static __always_inline unsigned long *stack_pointer(struct task_struct *task) | 406 | static int show_trace_cb(struct stackframe *frame, void *data) |
406 | { | 407 | { |
407 | unsigned long *sp; | 408 | if (kernel_text_address(frame->pc)) { |
408 | 409 | printk(" [<%08lx>] ", frame->pc); | |
409 | if (!task || task == current) | 410 | print_symbol("%s\n", frame->pc); |
410 | __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp)); | 411 | } |
411 | else | 412 | return 0; |
412 | sp = (unsigned long *)task->thread.sp; | ||
413 | |||
414 | return sp; | ||
415 | } | 413 | } |
416 | 414 | ||
417 | void show_trace(struct task_struct *task, unsigned long *sp) | 415 | void show_trace(struct task_struct *task, unsigned long *sp) |
418 | { | 416 | { |
419 | unsigned long a0, a1, pc; | 417 | if (!sp) |
420 | unsigned long sp_start, sp_end; | 418 | sp = stack_pointer(task); |
421 | |||
422 | if (sp) | ||
423 | a1 = (unsigned long)sp; | ||
424 | else | ||
425 | a1 = (unsigned long)stack_pointer(task); | ||
426 | |||
427 | sp_start = a1 & ~(THREAD_SIZE-1); | ||
428 | sp_end = sp_start + THREAD_SIZE; | ||
429 | 419 | ||
430 | printk("Call Trace:"); | 420 | printk("Call Trace:"); |
431 | #ifdef CONFIG_KALLSYMS | 421 | #ifdef CONFIG_KALLSYMS |
432 | printk("\n"); | 422 | printk("\n"); |
433 | #endif | 423 | #endif |
434 | spill_registers(); | 424 | walk_stackframe(sp, show_trace_cb, NULL); |
435 | |||
436 | while (a1 > sp_start && a1 < sp_end) { | ||
437 | sp = (unsigned long*)a1; | ||
438 | |||
439 | a0 = *(sp - 4); | ||
440 | a1 = *(sp - 3); | ||
441 | |||
442 | if (a1 <= (unsigned long) sp) | ||
443 | break; | ||
444 | |||
445 | pc = MAKE_PC_FROM_RA(a0, a1); | ||
446 | |||
447 | if (kernel_text_address(pc)) { | ||
448 | printk(" [<%08lx>] ", pc); | ||
449 | print_symbol("%s\n", pc); | ||
450 | } | ||
451 | } | ||
452 | printk("\n"); | 425 | printk("\n"); |
453 | } | 426 | } |
454 | 427 | ||