aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/xtensa/Kconfig3
-rw-r--r--arch/xtensa/include/asm/stacktrace.h36
-rw-r--r--arch/xtensa/kernel/Makefile6
-rw-r--r--arch/xtensa/kernel/stacktrace.c87
-rw-r--r--arch/xtensa/kernel/traps.c49
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
52source "init/Kconfig" 52source "init/Kconfig"
53source "kernel/Kconfig.freezer" 53source "kernel/Kconfig.freezer"
54 54
55config STACKTRACE_SUPPORT
56 def_bool y
57
55config MMU 58config 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
15struct stackframe {
16 unsigned long pc;
17 unsigned long sp;
18};
19
20static __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
32void 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
5extra-y := head.o vmlinux.lds 5extra-y := head.o vmlinux.lds
6 6
7obj-y := align.o entry.o irq.o coprocessor.o process.o ptrace.o \ 7obj-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
11obj-$(CONFIG_KGDB) += xtensa-stub.o 11obj-$(CONFIG_KGDB) += xtensa-stub.o
12obj-$(CONFIG_PCI) += pci.o 12obj-$(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
17void 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
50struct stack_trace_data {
51 struct stack_trace *trace;
52 unsigned skip;
53};
54
55static 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
71void 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}
79EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
80
81void save_stack_trace(struct stack_trace *trace)
82{
83 save_stack_trace_tsk(current, trace);
84}
85EXPORT_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
405static __always_inline unsigned long *stack_pointer(struct task_struct *task) 406static 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
417void show_trace(struct task_struct *task, unsigned long *sp) 415void 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