aboutsummaryrefslogtreecommitdiffstats
path: root/arch/xtensa/oprofile
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 /arch/xtensa/oprofile
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>
Diffstat (limited to 'arch/xtensa/oprofile')
-rw-r--r--arch/xtensa/oprofile/Makefile9
-rw-r--r--arch/xtensa/oprofile/backtrace.c171
-rw-r--r--arch/xtensa/oprofile/init.c26
3 files changed, 206 insertions, 0 deletions
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}