aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2015-06-25 00:27:16 -0400
committerMax Filippov <jcmvbkbc@gmail.com>2015-08-17 00:32:49 -0400
commit5fdf377d802ddd439fe16dd2e9e38039af535af2 (patch)
tree70e3aed25a0864ec247c96fe2671a6e0728e118e
parentb6569439f1cec0ce15f647e2ba814431b5930b82 (diff)
xtensa: move oprofile stack tracing to stacktrace.c
Old oprofile interface will share user stack tracing with new perf interface. Move oprofile user/kernel stack tracing to stacktrace.c to make it possible. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
-rw-r--r--arch/xtensa/include/asm/stacktrace.h8
-rw-r--r--arch/xtensa/kernel/stacktrace.c167
-rw-r--r--arch/xtensa/oprofile/backtrace.c158
3 files changed, 182 insertions, 151 deletions
diff --git a/arch/xtensa/include/asm/stacktrace.h b/arch/xtensa/include/asm/stacktrace.h
index 6a05fcb0a20d..fe06e8ed162b 100644
--- a/arch/xtensa/include/asm/stacktrace.h
+++ b/arch/xtensa/include/asm/stacktrace.h
@@ -33,4 +33,12 @@ void walk_stackframe(unsigned long *sp,
33 int (*fn)(struct stackframe *frame, void *data), 33 int (*fn)(struct stackframe *frame, void *data),
34 void *data); 34 void *data);
35 35
36void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
37 int (*kfn)(struct stackframe *frame, void *data),
38 int (*ufn)(struct stackframe *frame, void *data),
39 void *data);
40void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
41 int (*ufn)(struct stackframe *frame, void *data),
42 void *data);
43
36#endif /* _XTENSA_STACKTRACE_H */ 44#endif /* _XTENSA_STACKTRACE_H */
diff --git a/arch/xtensa/kernel/stacktrace.c b/arch/xtensa/kernel/stacktrace.c
index 7d2c317bd98b..7538d802b65a 100644
--- a/arch/xtensa/kernel/stacktrace.c
+++ b/arch/xtensa/kernel/stacktrace.c
@@ -1,11 +1,12 @@
1/* 1/*
2 * arch/xtensa/kernel/stacktrace.c 2 * Kernel and userspace stack tracing.
3 * 3 *
4 * This file is subject to the terms and conditions of the GNU General Public 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 5 * License. See the file "COPYING" in the main directory of this archive
6 * for more details. 6 * for more details.
7 * 7 *
8 * Copyright (C) 2001 - 2013 Tensilica Inc. 8 * Copyright (C) 2001 - 2013 Tensilica Inc.
9 * Copyright (C) 2015 Cadence Design Systems Inc.
9 */ 10 */
10#include <linux/export.h> 11#include <linux/export.h>
11#include <linux/sched.h> 12#include <linux/sched.h>
@@ -13,6 +14,170 @@
13 14
14#include <asm/stacktrace.h> 15#include <asm/stacktrace.h>
15#include <asm/traps.h> 16#include <asm/traps.h>
17#include <asm/uaccess.h>
18
19#if IS_ENABLED(CONFIG_OPROFILE) || IS_ENABLED(CONFIG_PERF_EVENTS)
20
21/* Address of common_exception_return, used to check the
22 * transition from kernel to user space.
23 */
24extern int common_exception_return;
25
26/* A struct that maps to the part of the frame containing the a0 and
27 * a1 registers.
28 */
29struct frame_start {
30 unsigned long a0;
31 unsigned long a1;
32};
33
34void xtensa_backtrace_user(struct pt_regs *regs, unsigned int depth,
35 int (*ufn)(struct stackframe *frame, void *data),
36 void *data)
37{
38 unsigned long windowstart = regs->windowstart;
39 unsigned long windowbase = regs->windowbase;
40 unsigned long a0 = regs->areg[0];
41 unsigned long a1 = regs->areg[1];
42 unsigned long pc = regs->pc;
43 struct stackframe frame;
44 int index;
45
46 if (!depth--)
47 return;
48
49 frame.pc = pc;
50 frame.sp = a1;
51
52 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
53 return;
54
55 /* Two steps:
56 *
57 * 1. Look through the register window for the
58 * previous PCs in the call trace.
59 *
60 * 2. Look on the stack.
61 */
62
63 /* Step 1. */
64 /* Rotate WINDOWSTART to move the bit corresponding to
65 * the current window to the bit #0.
66 */
67 windowstart = (windowstart << WSBITS | windowstart) >> windowbase;
68
69 /* Look for bits that are set, they correspond to
70 * valid windows.
71 */
72 for (index = WSBITS - 1; (index > 0) && depth; depth--, index--)
73 if (windowstart & (1 << index)) {
74 /* Get the PC from a0 and a1. */
75 pc = MAKE_PC_FROM_RA(a0, pc);
76 /* Read a0 and a1 from the
77 * corresponding position in AREGs.
78 */
79 a0 = regs->areg[index * 4];
80 a1 = regs->areg[index * 4 + 1];
81
82 frame.pc = pc;
83 frame.sp = a1;
84
85 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
86 return;
87 }
88
89 /* Step 2. */
90 /* We are done with the register window, we need to
91 * look through the stack.
92 */
93 if (!depth)
94 return;
95
96 /* Start from the a1 register. */
97 /* a1 = regs->areg[1]; */
98 while (a0 != 0 && depth--) {
99 struct frame_start frame_start;
100 /* Get the location for a1, a0 for the
101 * previous frame from the current a1.
102 */
103 unsigned long *psp = (unsigned long *)a1;
104
105 psp -= 4;
106
107 /* Check if the region is OK to access. */
108 if (!access_ok(VERIFY_READ, psp, sizeof(frame_start)))
109 return;
110 /* Copy a1, a0 from user space stack frame. */
111 if (__copy_from_user_inatomic(&frame_start, psp,
112 sizeof(frame_start)))
113 return;
114
115 pc = MAKE_PC_FROM_RA(a0, pc);
116 a0 = frame_start.a0;
117 a1 = frame_start.a1;
118
119 frame.pc = pc;
120 frame.sp = a1;
121
122 if (pc == 0 || pc >= TASK_SIZE || ufn(&frame, data))
123 return;
124 }
125}
126EXPORT_SYMBOL(xtensa_backtrace_user);
127
128void xtensa_backtrace_kernel(struct pt_regs *regs, unsigned int depth,
129 int (*kfn)(struct stackframe *frame, void *data),
130 int (*ufn)(struct stackframe *frame, void *data),
131 void *data)
132{
133 unsigned long pc = regs->depc > VALID_DOUBLE_EXCEPTION_ADDRESS ?
134 regs->depc : regs->pc;
135 unsigned long sp_start, sp_end;
136 unsigned long a0 = regs->areg[0];
137 unsigned long a1 = regs->areg[1];
138
139 sp_start = a1 & ~(THREAD_SIZE - 1);
140 sp_end = sp_start + THREAD_SIZE;
141
142 /* Spill the register window to the stack first. */
143 spill_registers();
144
145 /* Read the stack frames one by one and create the PC
146 * from the a0 and a1 registers saved there.
147 */
148 while (a1 > sp_start && a1 < sp_end && depth--) {
149 struct stackframe frame;
150 unsigned long *psp = (unsigned long *)a1;
151
152 frame.pc = pc;
153 frame.sp = a1;
154
155 if (kernel_text_address(pc) && kfn(&frame, data))
156 return;
157
158 if (pc == (unsigned long)&common_exception_return) {
159 regs = (struct pt_regs *)a1;
160 if (user_mode(regs)) {
161 if (ufn == NULL)
162 return;
163 xtensa_backtrace_user(regs, depth, ufn, data);
164 return;
165 }
166 a0 = regs->areg[0];
167 a1 = regs->areg[1];
168 continue;
169 }
170
171 sp_start = a1;
172
173 pc = MAKE_PC_FROM_RA(a0, pc);
174 a0 = *(psp - 4);
175 a1 = *(psp - 3);
176 }
177}
178EXPORT_SYMBOL(xtensa_backtrace_kernel);
179
180#endif
16 181
17void walk_stackframe(unsigned long *sp, 182void walk_stackframe(unsigned long *sp,
18 int (*fn)(struct stackframe *frame, void *data), 183 int (*fn)(struct stackframe *frame, void *data),
diff --git a/arch/xtensa/oprofile/backtrace.c b/arch/xtensa/oprofile/backtrace.c
index 5f03a593d84f..8f952034e161 100644
--- a/arch/xtensa/oprofile/backtrace.c
+++ b/arch/xtensa/oprofile/backtrace.c
@@ -2,168 +2,26 @@
2 * @file backtrace.c 2 * @file backtrace.c
3 * 3 *
4 * @remark Copyright 2008 Tensilica Inc. 4 * @remark Copyright 2008 Tensilica Inc.
5 * Copyright (C) 2015 Cadence Design Systems Inc.
5 * @remark Read the file COPYING 6 * @remark Read the file COPYING
6 * 7 *
7 */ 8 */
8 9
9#include <linux/oprofile.h> 10#include <linux/oprofile.h>
10#include <linux/sched.h>
11#include <linux/mm.h>
12#include <asm/ptrace.h> 11#include <asm/ptrace.h>
13#include <asm/uaccess.h> 12#include <asm/stacktrace.h>
14#include <asm/traps.h>
15 13
16/* Address of common_exception_return, used to check the 14static int xtensa_backtrace_cb(struct stackframe *frame, void *data)
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{ 15{
116 unsigned long pc = regs->pc; 16 oprofile_add_trace(frame->pc);
117 unsigned long *psp; 17 return 0;
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 oprofile_add_trace(pc);
136 if (pc == (unsigned long) &common_exception_return) {
137 regs = (struct pt_regs *)a1;
138 if (user_mode(regs)) {
139 pc = regs->pc;
140 if (pc != 0 && pc <= TASK_SIZE)
141 oprofile_add_trace(pc);
142 else
143 return;
144 return xtensa_backtrace_user(regs, depth);
145 }
146 a0 = regs->areg[0];
147 a1 = regs->areg[1];
148 continue;
149 }
150
151 psp = (unsigned long *)a1;
152
153 a0 = *(psp - 4);
154 a1 = *(psp - 3);
155
156 if (a1 <= (unsigned long)psp)
157 return;
158
159 }
160 return;
161} 18}
162 19
163void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth) 20void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth)
164{ 21{
165 if (user_mode(regs)) 22 if (user_mode(regs))
166 xtensa_backtrace_user(regs, depth); 23 xtensa_backtrace_user(regs, depth, xtensa_backtrace_cb, NULL);
167 else 24 else
168 xtensa_backtrace_kernel(regs, depth); 25 xtensa_backtrace_kernel(regs, depth, xtensa_backtrace_cb,
26 xtensa_backtrace_cb, NULL);
169} 27}