diff options
author | Max Filippov <jcmvbkbc@gmail.com> | 2015-06-25 00:27:16 -0400 |
---|---|---|
committer | Max Filippov <jcmvbkbc@gmail.com> | 2015-08-17 00:32:49 -0400 |
commit | 5fdf377d802ddd439fe16dd2e9e38039af535af2 (patch) | |
tree | 70e3aed25a0864ec247c96fe2671a6e0728e118e | |
parent | b6569439f1cec0ce15f647e2ba814431b5930b82 (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.h | 8 | ||||
-rw-r--r-- | arch/xtensa/kernel/stacktrace.c | 167 | ||||
-rw-r--r-- | arch/xtensa/oprofile/backtrace.c | 158 |
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 | ||
36 | void 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); | ||
40 | void 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 | */ | ||
24 | extern int common_exception_return; | ||
25 | |||
26 | /* A struct that maps to the part of the frame containing the a0 and | ||
27 | * a1 registers. | ||
28 | */ | ||
29 | struct frame_start { | ||
30 | unsigned long a0; | ||
31 | unsigned long a1; | ||
32 | }; | ||
33 | |||
34 | void 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 | } | ||
126 | EXPORT_SYMBOL(xtensa_backtrace_user); | ||
127 | |||
128 | void 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 | } | ||
178 | EXPORT_SYMBOL(xtensa_backtrace_kernel); | ||
179 | |||
180 | #endif | ||
16 | 181 | ||
17 | void walk_stackframe(unsigned long *sp, | 182 | void 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 | 14 | static int xtensa_backtrace_cb(struct stackframe *frame, void *data) |
17 | * transition from kernel to user space. | ||
18 | */ | ||
19 | extern int common_exception_return; | ||
20 | |||
21 | /* A struct that maps to the part of the frame containing the a0 and | ||
22 | * a1 registers. | ||
23 | */ | ||
24 | struct frame_start { | ||
25 | unsigned long a0; | ||
26 | unsigned long a1; | ||
27 | }; | ||
28 | |||
29 | static 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 | |||
114 | static 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 | ||
163 | void xtensa_backtrace(struct pt_regs * const regs, unsigned int depth) | 20 | void 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 | } |