diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/mips/Kconfig | 1 | ||||
-rw-r--r-- | arch/mips/kernel/ftrace.c | 106 | ||||
-rw-r--r-- | arch/mips/kernel/mcount.S | 42 |
3 files changed, 149 insertions, 0 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index af6940c5b272..2906ae59af49 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig | |||
@@ -9,6 +9,7 @@ config MIPS | |||
9 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST | 9 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST |
10 | select HAVE_DYNAMIC_FTRACE | 10 | select HAVE_DYNAMIC_FTRACE |
11 | select HAVE_FTRACE_MCOUNT_RECORD | 11 | select HAVE_FTRACE_MCOUNT_RECORD |
12 | select HAVE_FUNCTION_GRAPH_TRACER | ||
12 | # Horrible source of confusion. Die, die, die ... | 13 | # Horrible source of confusion. Die, die, die ... |
13 | select EMBEDDED | 14 | select EMBEDDED |
14 | select RTC_LIB if !MACH_LOONGSON | 15 | select RTC_LIB if !MACH_LOONGSON |
diff --git a/arch/mips/kernel/ftrace.c b/arch/mips/kernel/ftrace.c index 5459a7838370..65a3f8a89927 100644 --- a/arch/mips/kernel/ftrace.c +++ b/arch/mips/kernel/ftrace.c | |||
@@ -13,6 +13,8 @@ | |||
13 | #include <linux/ftrace.h> | 13 | #include <linux/ftrace.h> |
14 | 14 | ||
15 | #include <asm/cacheflush.h> | 15 | #include <asm/cacheflush.h> |
16 | #include <asm/asm.h> | ||
17 | #include <asm/asm-offsets.h> | ||
16 | 18 | ||
17 | #ifdef CONFIG_DYNAMIC_FTRACE | 19 | #ifdef CONFIG_DYNAMIC_FTRACE |
18 | 20 | ||
@@ -110,3 +112,107 @@ int __init ftrace_dyn_arch_init(void *data) | |||
110 | return 0; | 112 | return 0; |
111 | } | 113 | } |
112 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 114 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
115 | |||
116 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
117 | |||
118 | #define S_RA_SP (0xafbf << 16) /* s{d,w} ra, offset(sp) */ | ||
119 | #define S_R_SP (0xafb0 << 16) /* s{d,w} R, offset(sp) */ | ||
120 | #define OFFSET_MASK 0xffff /* stack offset range: 0 ~ PT_SIZE */ | ||
121 | |||
122 | unsigned long ftrace_get_parent_addr(unsigned long self_addr, | ||
123 | unsigned long parent, | ||
124 | unsigned long parent_addr, | ||
125 | unsigned long fp) | ||
126 | { | ||
127 | unsigned long sp, ip, ra; | ||
128 | unsigned int code; | ||
129 | |||
130 | /* in module or kernel? */ | ||
131 | if (self_addr & 0x40000000) { | ||
132 | /* module: move to the instruction "lui v1, HI_16BIT_OF_MCOUNT" */ | ||
133 | ip = self_addr - 20; | ||
134 | } else { | ||
135 | /* kernel: move to the instruction "move ra, at" */ | ||
136 | ip = self_addr - 12; | ||
137 | } | ||
138 | |||
139 | /* search the text until finding the non-store instruction or "s{d,w} | ||
140 | * ra, offset(sp)" instruction */ | ||
141 | do { | ||
142 | ip -= 4; | ||
143 | |||
144 | /* get the code at "ip" */ | ||
145 | code = *(unsigned int *)ip; | ||
146 | |||
147 | /* If we hit the non-store instruction before finding where the | ||
148 | * ra is stored, then this is a leaf function and it does not | ||
149 | * store the ra on the stack. */ | ||
150 | if ((code & S_R_SP) != S_R_SP) | ||
151 | return parent_addr; | ||
152 | |||
153 | } while (((code & S_RA_SP) != S_RA_SP)); | ||
154 | |||
155 | sp = fp + (code & OFFSET_MASK); | ||
156 | ra = *(unsigned long *)sp; | ||
157 | |||
158 | if (ra == parent) | ||
159 | return sp; | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Hook the return address and push it in the stack of return addrs | ||
166 | * in current thread info. | ||
167 | */ | ||
168 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, | ||
169 | unsigned long fp) | ||
170 | { | ||
171 | unsigned long old; | ||
172 | struct ftrace_graph_ent trace; | ||
173 | unsigned long return_hooker = (unsigned long) | ||
174 | &return_to_handler; | ||
175 | |||
176 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) | ||
177 | return; | ||
178 | |||
179 | /* "parent" is the stack address saved the return address of the caller | ||
180 | * of _mcount, for a leaf function not save the return address in the | ||
181 | * stack address, so, we "emulate" one in _mcount's stack space, and | ||
182 | * hijack it directly, but for a non-leaf function, it will save the | ||
183 | * return address to the its stack space, so, we can not hijack the | ||
184 | * "parent" directly, but need to find the real stack address, | ||
185 | * ftrace_get_parent_addr() does it! | ||
186 | */ | ||
187 | |||
188 | old = *parent; | ||
189 | |||
190 | parent = (unsigned long *)ftrace_get_parent_addr(self_addr, old, | ||
191 | (unsigned long)parent, | ||
192 | fp); | ||
193 | |||
194 | /* If fails when getting the stack address of the non-leaf function's | ||
195 | * ra, stop function graph tracer and return */ | ||
196 | if (parent == 0) { | ||
197 | ftrace_graph_stop(); | ||
198 | WARN_ON(1); | ||
199 | return; | ||
200 | } | ||
201 | |||
202 | *parent = return_hooker; | ||
203 | |||
204 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, fp) == | ||
205 | -EBUSY) { | ||
206 | *parent = old; | ||
207 | return; | ||
208 | } | ||
209 | |||
210 | trace.func = self_addr; | ||
211 | |||
212 | /* Only trace if the calling function expects to */ | ||
213 | if (!ftrace_graph_entry(&trace)) { | ||
214 | current->curr_ret_stack--; | ||
215 | *parent = old; | ||
216 | } | ||
217 | } | ||
218 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S index ffc425979d4f..b50e38d828c4 100644 --- a/arch/mips/kernel/mcount.S +++ b/arch/mips/kernel/mcount.S | |||
@@ -93,6 +93,16 @@ NESTED(_mcount, PT_SIZE, ra) | |||
93 | PTR_L t1, ftrace_trace_function /* Prepare t1 for (1) */ | 93 | PTR_L t1, ftrace_trace_function /* Prepare t1 for (1) */ |
94 | bne t0, t1, static_trace | 94 | bne t0, t1, static_trace |
95 | nop | 95 | nop |
96 | |||
97 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
98 | PTR_L t2, ftrace_graph_return | ||
99 | bne t0, t2, ftrace_graph_caller | ||
100 | nop | ||
101 | PTR_LA t0, ftrace_graph_entry_stub | ||
102 | PTR_L t2, ftrace_graph_entry | ||
103 | bne t0, t2, ftrace_graph_caller | ||
104 | nop | ||
105 | #endif | ||
96 | b ftrace_stub | 106 | b ftrace_stub |
97 | nop | 107 | nop |
98 | 108 | ||
@@ -111,5 +121,37 @@ ftrace_stub: | |||
111 | 121 | ||
112 | #endif /* ! CONFIG_DYNAMIC_FTRACE */ | 122 | #endif /* ! CONFIG_DYNAMIC_FTRACE */ |
113 | 123 | ||
124 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
125 | |||
126 | NESTED(ftrace_graph_caller, PT_SIZE, ra) | ||
127 | MCOUNT_SAVE_REGS | ||
128 | |||
129 | PTR_LA a0, PT_R1(sp) /* arg1: &AT -> a0 */ | ||
130 | move a1, ra /* arg2: next ip, selfaddr */ | ||
131 | jal prepare_ftrace_return | ||
132 | move a2, fp /* arg3: frame pointer */ | ||
133 | |||
134 | MCOUNT_RESTORE_REGS | ||
135 | RETURN_BACK | ||
136 | END(ftrace_graph_caller) | ||
137 | |||
138 | .align 2 | ||
139 | .globl return_to_handler | ||
140 | return_to_handler: | ||
141 | PTR_SUBU sp, PT_SIZE | ||
142 | PTR_S v0, PT_R2(sp) | ||
143 | |||
144 | jal ftrace_return_to_handler | ||
145 | PTR_S v1, PT_R3(sp) | ||
146 | |||
147 | /* restore the real parent address: v0 -> ra */ | ||
148 | move ra, v0 | ||
149 | |||
150 | PTR_L v0, PT_R2(sp) | ||
151 | PTR_L v1, PT_R3(sp) | ||
152 | jr ra | ||
153 | PTR_ADDIU sp, PT_SIZE | ||
154 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | ||
155 | |||
114 | .set at | 156 | .set at |
115 | .set reorder | 157 | .set reorder |