diff options
Diffstat (limited to 'arch/mips/kernel/ftrace.c')
-rw-r--r-- | arch/mips/kernel/ftrace.c | 106 |
1 files changed, 106 insertions, 0 deletions
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 */ | ||