aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordann <dann@xtensa-linux.org>2008-05-21 20:43:50 -0400
committerChris Zankel <chris@zankel.net>2013-02-23 22:22:54 -0500
commite6ffe17ec45dd763ee8278246a112562f64a4ef2 (patch)
tree3efe66a318764e0aeab4385eb407ec597f1f9f9b
parent2d6f82fee45a52359012948306587eba704cf35b (diff)
xtensa: add support for oprofile
Support call graph profiling. Keep upper two bits of PC unchanged through backtrace rather than take them from sp (a1). The stack pointer is usually in the same GB (same upper 2 bits) as PC, but technically doesn't always have to be (and might not in the future, when taking full advantage of MMU v3). Signed-off-by: Dan Nicolaescu <dann@xtensa-linux.org> Signed-off-by: Pete Delaney <piet@tensilica.com> Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Chris Zankel <chris@zankel.net>
-rw-r--r--arch/xtensa/Kconfig1
-rw-r--r--arch/xtensa/Makefile1
-rw-r--r--arch/xtensa/kernel/entry.S2
-rw-r--r--arch/xtensa/oprofile/Makefile9
-rw-r--r--arch/xtensa/oprofile/backtrace.c171
-rw-r--r--arch/xtensa/oprofile/init.c26
6 files changed, 209 insertions, 1 deletions
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index f83780f8f290..f61f7362c8f6 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -17,6 +17,7 @@ config XTENSA
17 select ARCH_WANT_OPTIONAL_GPIOLIB 17 select ARCH_WANT_OPTIONAL_GPIOLIB
18 select CLONE_BACKWARDS 18 select CLONE_BACKWARDS
19 select IRQ_DOMAIN 19 select IRQ_DOMAIN
20 select HAVE_OPROFILE
20 help 21 help
21 Xtensa processors are 32-bit RISC machines designed by Tensilica 22 Xtensa processors are 32-bit RISC machines designed by Tensilica
22 primarily for embedded systems. These processors are both 23 primarily for embedded systems. These processors are both
diff --git a/arch/xtensa/Makefile b/arch/xtensa/Makefile
index afec8f000c4a..136224b74d4f 100644
--- a/arch/xtensa/Makefile
+++ b/arch/xtensa/Makefile
@@ -87,6 +87,7 @@ core-y += arch/xtensa/kernel/ arch/xtensa/mm/
87core-y += $(buildvar) $(buildplf) 87core-y += $(buildvar) $(buildplf)
88 88
89libs-y += arch/xtensa/lib/ $(LIBGCC) 89libs-y += arch/xtensa/lib/ $(LIBGCC)
90drivers-$(CONFIG_OPROFILE) += arch/xtensa/oprofile/
90 91
91ifneq ($(CONFIG_BUILTIN_DTB),"") 92ifneq ($(CONFIG_BUILTIN_DTB),"")
92core-$(CONFIG_OF) += arch/xtensa/boot/dts/ 93core-$(CONFIG_OF) += arch/xtensa/boot/dts/
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S
index 0ace2acbbad0..70d5a9e33573 100644
--- a/arch/xtensa/kernel/entry.S
+++ b/arch/xtensa/kernel/entry.S
@@ -399,7 +399,7 @@ common_exception:
399 callx4 a4 399 callx4 a4
400 400
401 /* Jump here for exception exit */ 401 /* Jump here for exception exit */
402 402 .global common_exception_return
403common_exception_return: 403common_exception_return:
404 404
405 /* Jump if we are returning from kernel exceptions. */ 405 /* Jump if we are returning from kernel exceptions. */
diff --git a/arch/xtensa/oprofile/Makefile b/arch/xtensa/oprofile/Makefile
new file mode 100644
index 000000000000..69ffbe80f184
--- /dev/null
+++ b/arch/xtensa/oprofile/Makefile
@@ -0,0 +1,9 @@
1obj-$(CONFIG_OPROFILE) += oprofile.o
2
3DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
4 oprof.o cpu_buffer.o buffer_sync.o \
5 event_buffer.o oprofile_files.o \
6 oprofilefs.o oprofile_stats.o \
7 timer_int.o )
8
9oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
diff --git a/arch/xtensa/oprofile/backtrace.c b/arch/xtensa/oprofile/backtrace.c
new file mode 100644
index 000000000000..66f32ee2c982
--- /dev/null
+++ b/arch/xtensa/oprofile/backtrace.c
@@ -0,0 +1,171 @@
1/**
2 * @file backtrace.c
3 *
4 * @remark Copyright 2008 Tensilica Inc.
5 * @remark Read the file COPYING
6 *
7 */
8
9#include <linux/oprofile.h>
10#include <linux/sched.h>
11#include <linux/mm.h>
12#include <asm/ptrace.h>
13#include <asm/uaccess.h>
14#include <asm/traps.h>
15
16/* Address of common_exception_return, used to check the
17 * transition from kernel to user space.
18 */
19extern int common_exception_return;
20
21/* A struct that maps to the part of the frame containing the a0 and
22 * a1 registers.
23 */
24struct frame_start {
25 unsigned long a0;
26 unsigned long a1;
27};
28
29static void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth)
30{
31 unsigned long windowstart = regs->windowstart;
32 unsigned long windowbase = regs->windowbase;
33 unsigned long a0 = regs->areg[0];
34 unsigned long a1 = regs->areg[1];
35 unsigned long pc = MAKE_PC_FROM_RA(a0, regs->pc);
36 int index;
37
38 /* First add the current PC to the trace. */
39 if (pc != 0 && pc <= TASK_SIZE)
40 oprofile_add_trace(pc);
41 else
42 return;
43
44 /* Two steps:
45 *
46 * 1. Look through the register window for the
47 * previous PCs in the call trace.
48 *
49 * 2. Look on the stack.
50 */
51
52 /* Step 1. */
53 /* Rotate WINDOWSTART to move the bit corresponding to
54 * the current window to the bit #0.
55 */
56 windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
57
58 /* Look for bits that are set, they correspond to
59 * valid windows.
60 */
61 for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
62 if (windowstart & (1 << index)) {
63 /* Read a0 and a1 from the
64 * corresponding position in AREGs.
65 */
66 a0 = regs->areg[index * 4];
67 a1 = regs->areg[index * 4 + 1];
68 /* Get the PC from a0 and a1. */
69 pc = MAKE_PC_FROM_RA(a0, pc);
70
71 /* Add the PC to the trace. */
72 if (pc != 0 && pc <= TASK_SIZE)
73 oprofile_add_trace(pc);
74 else
75 return;
76 }
77
78 /* Step 2. */
79 /* We are done with the register window, we need to
80 * look through the stack.
81 */
82 if (depth > 0) {
83 /* Start from the a1 register. */
84 /* a1 = regs->areg[1]; */
85 while (a0 != 0 && depth--) {
86
87 struct frame_start frame_start;
88 /* Get the location for a1, a0 for the
89 * previous frame from the current a1.
90 */
91 unsigned long *psp = (unsigned long *)a1;
92 psp -= 4;
93
94 /* Check if the region is OK to access. */
95 if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
96 return;
97 /* Copy a1, a0 from user space stack frame. */
98 if (__copy_from_user_inatomic(&frame_start, psp,
99 sizeof(frame_start)))
100 return;
101
102 a0 = frame_start.a0;
103 a1 = frame_start.a1;
104 pc = MAKE_PC_FROM_RA(a0, pc);
105
106 if (pc != 0 && pc <= TASK_SIZE)
107 oprofile_add_trace(pc);
108 else
109 return;
110 }
111 }
112}
113
114static void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth)
115{
116 unsigned long pc = regs->pc;
117 unsigned long *psp;
118 unsigned long sp_start, sp_end;
119 unsigned long a0 = regs->areg[0];
120 unsigned long a1 = regs->areg[1];
121
122 sp_start = a1 & ~(THREAD_SIZE-1);
123 sp_end = sp_start + THREAD_SIZE;
124
125 /* Spill the register window to the stack first. */
126 spill_registers();
127
128 /* Read the stack frames one by one and create the PC
129 * from the a0 and a1 registers saved there.
130 */
131 while (a1 > sp_start && a1 < sp_end && depth--) {
132 pc = MAKE_PC_FROM_RA(a0, pc);
133
134 /* Add the PC to the trace. */
135 if (kernel_text_address(pc))
136 oprofile_add_trace(pc);
137
138 if (pc == (unsigned long) &common_exception_return) {
139 regs = (struct pt_regs *)a1;
140 if (user_mode(regs)) {
141 pc = regs->pc;
142 if (pc != 0 && pc <= TASK_SIZE)
143 oprofile_add_trace(pc);
144 else
145 return;
146 return xtensa_backtrace_user(regs, depth);
147 }
148 a0 = regs->areg[0];
149 a1 = regs->areg[1];
150 continue;
151 }
152
153 psp = (unsigned long *)a1;
154
155 a0 = *(psp - 4);
156 a1 = *(psp - 3);
157
158 if (a1 <= (unsigned long)psp)
159 return;
160
161 }
162 return;
163}
164
165void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
166{
167 if (user_mode(regs))
168 xtensa_backtrace_user(regs, depth);
169 else
170 xtensa_backtrace_kernel(regs, depth);
171}
diff --git a/arch/xtensa/oprofile/init.c b/arch/xtensa/oprofile/init.c
new file mode 100644
index 000000000000..a67eea379766
--- /dev/null
+++ b/arch/xtensa/oprofile/init.c
@@ -0,0 +1,26 @@
1/**
2 * @file init.c
3 *
4 * @remark Copyright 2008 Tensilica Inc.
5 * @remark Read the file COPYING
6 *
7 */
8
9#include <linux/kernel.h>
10#include <linux/oprofile.h>
11#include <linux/errno.h>
12#include <linux/init.h>
13
14
15extern void xtensa_backtrace(struct pt_regs *const regs, unsigned int depth);
16
17int __init oprofile_arch_init(struct oprofile_operations *ops)
18{
19 ops->backtrace = xtensa_backtrace;
20 return -ENODEV;
21}
22
23
24void oprofile_arch_exit(void)
25{
26}