diff options
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 334 |
1 files changed, 42 insertions, 292 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 086af4f5c3e8..c2af1560e856 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -16,33 +16,6 @@ | |||
16 | #include "trace.h" | 16 | #include "trace.h" |
17 | #include "trace_output.h" | 17 | #include "trace_output.h" |
18 | 18 | ||
19 | static bool kill_ftrace_graph; | ||
20 | |||
21 | /** | ||
22 | * ftrace_graph_is_dead - returns true if ftrace_graph_stop() was called | ||
23 | * | ||
24 | * ftrace_graph_stop() is called when a severe error is detected in | ||
25 | * the function graph tracing. This function is called by the critical | ||
26 | * paths of function graph to keep those paths from doing any more harm. | ||
27 | */ | ||
28 | bool ftrace_graph_is_dead(void) | ||
29 | { | ||
30 | return kill_ftrace_graph; | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * ftrace_graph_stop - set to permanently disable function graph tracincg | ||
35 | * | ||
36 | * In case of an error int function graph tracing, this is called | ||
37 | * to try to keep function graph tracing from causing any more harm. | ||
38 | * Usually this is pretty severe and this is called to try to at least | ||
39 | * get a warning out to the user. | ||
40 | */ | ||
41 | void ftrace_graph_stop(void) | ||
42 | { | ||
43 | kill_ftrace_graph = true; | ||
44 | } | ||
45 | |||
46 | /* When set, irq functions will be ignored */ | 19 | /* When set, irq functions will be ignored */ |
47 | static int ftrace_graph_skip_irqs; | 20 | static int ftrace_graph_skip_irqs; |
48 | 21 | ||
@@ -87,8 +60,12 @@ static struct tracer_opt trace_opts[] = { | |||
87 | { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) }, | 60 | { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) }, |
88 | /* Include sleep time (scheduled out) between entry and return */ | 61 | /* Include sleep time (scheduled out) between entry and return */ |
89 | { TRACER_OPT(sleep-time, TRACE_GRAPH_SLEEP_TIME) }, | 62 | { TRACER_OPT(sleep-time, TRACE_GRAPH_SLEEP_TIME) }, |
63 | |||
64 | #ifdef CONFIG_FUNCTION_PROFILER | ||
90 | /* Include time within nested functions */ | 65 | /* Include time within nested functions */ |
91 | { TRACER_OPT(graph-time, TRACE_GRAPH_GRAPH_TIME) }, | 66 | { TRACER_OPT(graph-time, TRACE_GRAPH_GRAPH_TIME) }, |
67 | #endif | ||
68 | |||
92 | { } /* Empty entry */ | 69 | { } /* Empty entry */ |
93 | }; | 70 | }; |
94 | 71 | ||
@@ -117,258 +94,6 @@ static void | |||
117 | print_graph_duration(struct trace_array *tr, unsigned long long duration, | 94 | print_graph_duration(struct trace_array *tr, unsigned long long duration, |
118 | struct trace_seq *s, u32 flags); | 95 | struct trace_seq *s, u32 flags); |
119 | 96 | ||
120 | /* Add a function return address to the trace stack on thread info.*/ | ||
121 | static int | ||
122 | ftrace_push_return_trace(unsigned long ret, unsigned long func, | ||
123 | unsigned long frame_pointer, unsigned long *retp) | ||
124 | { | ||
125 | unsigned long long calltime; | ||
126 | int index; | ||
127 | |||
128 | if (unlikely(ftrace_graph_is_dead())) | ||
129 | return -EBUSY; | ||
130 | |||
131 | if (!current->ret_stack) | ||
132 | return -EBUSY; | ||
133 | |||
134 | /* | ||
135 | * We must make sure the ret_stack is tested before we read | ||
136 | * anything else. | ||
137 | */ | ||
138 | smp_rmb(); | ||
139 | |||
140 | /* The return trace stack is full */ | ||
141 | if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { | ||
142 | atomic_inc(¤t->trace_overrun); | ||
143 | return -EBUSY; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * The curr_ret_stack is an index to ftrace return stack of | ||
148 | * current task. Its value should be in [0, FTRACE_RETFUNC_ | ||
149 | * DEPTH) when the function graph tracer is used. To support | ||
150 | * filtering out specific functions, it makes the index | ||
151 | * negative by subtracting huge value (FTRACE_NOTRACE_DEPTH) | ||
152 | * so when it sees a negative index the ftrace will ignore | ||
153 | * the record. And the index gets recovered when returning | ||
154 | * from the filtered function by adding the FTRACE_NOTRACE_ | ||
155 | * DEPTH and then it'll continue to record functions normally. | ||
156 | * | ||
157 | * The curr_ret_stack is initialized to -1 and get increased | ||
158 | * in this function. So it can be less than -1 only if it was | ||
159 | * filtered out via ftrace_graph_notrace_addr() which can be | ||
160 | * set from set_graph_notrace file in tracefs by user. | ||
161 | */ | ||
162 | if (current->curr_ret_stack < -1) | ||
163 | return -EBUSY; | ||
164 | |||
165 | calltime = trace_clock_local(); | ||
166 | |||
167 | index = ++current->curr_ret_stack; | ||
168 | if (ftrace_graph_notrace_addr(func)) | ||
169 | current->curr_ret_stack -= FTRACE_NOTRACE_DEPTH; | ||
170 | barrier(); | ||
171 | current->ret_stack[index].ret = ret; | ||
172 | current->ret_stack[index].func = func; | ||
173 | current->ret_stack[index].calltime = calltime; | ||
174 | #ifdef HAVE_FUNCTION_GRAPH_FP_TEST | ||
175 | current->ret_stack[index].fp = frame_pointer; | ||
176 | #endif | ||
177 | #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR | ||
178 | current->ret_stack[index].retp = retp; | ||
179 | #endif | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | int function_graph_enter(unsigned long ret, unsigned long func, | ||
184 | unsigned long frame_pointer, unsigned long *retp) | ||
185 | { | ||
186 | struct ftrace_graph_ent trace; | ||
187 | |||
188 | trace.func = func; | ||
189 | trace.depth = ++current->curr_ret_depth; | ||
190 | |||
191 | if (ftrace_push_return_trace(ret, func, | ||
192 | frame_pointer, retp)) | ||
193 | goto out; | ||
194 | |||
195 | /* Only trace if the calling function expects to */ | ||
196 | if (!ftrace_graph_entry(&trace)) | ||
197 | goto out_ret; | ||
198 | |||
199 | return 0; | ||
200 | out_ret: | ||
201 | current->curr_ret_stack--; | ||
202 | out: | ||
203 | current->curr_ret_depth--; | ||
204 | return -EBUSY; | ||
205 | } | ||
206 | |||
207 | /* Retrieve a function return address to the trace stack on thread info.*/ | ||
208 | static void | ||
209 | ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, | ||
210 | unsigned long frame_pointer) | ||
211 | { | ||
212 | int index; | ||
213 | |||
214 | index = current->curr_ret_stack; | ||
215 | |||
216 | /* | ||
217 | * A negative index here means that it's just returned from a | ||
218 | * notrace'd function. Recover index to get an original | ||
219 | * return address. See ftrace_push_return_trace(). | ||
220 | * | ||
221 | * TODO: Need to check whether the stack gets corrupted. | ||
222 | */ | ||
223 | if (index < 0) | ||
224 | index += FTRACE_NOTRACE_DEPTH; | ||
225 | |||
226 | if (unlikely(index < 0 || index >= FTRACE_RETFUNC_DEPTH)) { | ||
227 | ftrace_graph_stop(); | ||
228 | WARN_ON(1); | ||
229 | /* Might as well panic, otherwise we have no where to go */ | ||
230 | *ret = (unsigned long)panic; | ||
231 | return; | ||
232 | } | ||
233 | |||
234 | #ifdef HAVE_FUNCTION_GRAPH_FP_TEST | ||
235 | /* | ||
236 | * The arch may choose to record the frame pointer used | ||
237 | * and check it here to make sure that it is what we expect it | ||
238 | * to be. If gcc does not set the place holder of the return | ||
239 | * address in the frame pointer, and does a copy instead, then | ||
240 | * the function graph trace will fail. This test detects this | ||
241 | * case. | ||
242 | * | ||
243 | * Currently, x86_32 with optimize for size (-Os) makes the latest | ||
244 | * gcc do the above. | ||
245 | * | ||
246 | * Note, -mfentry does not use frame pointers, and this test | ||
247 | * is not needed if CC_USING_FENTRY is set. | ||
248 | */ | ||
249 | if (unlikely(current->ret_stack[index].fp != frame_pointer)) { | ||
250 | ftrace_graph_stop(); | ||
251 | WARN(1, "Bad frame pointer: expected %lx, received %lx\n" | ||
252 | " from func %ps return to %lx\n", | ||
253 | current->ret_stack[index].fp, | ||
254 | frame_pointer, | ||
255 | (void *)current->ret_stack[index].func, | ||
256 | current->ret_stack[index].ret); | ||
257 | *ret = (unsigned long)panic; | ||
258 | return; | ||
259 | } | ||
260 | #endif | ||
261 | |||
262 | *ret = current->ret_stack[index].ret; | ||
263 | trace->func = current->ret_stack[index].func; | ||
264 | trace->calltime = current->ret_stack[index].calltime; | ||
265 | trace->overrun = atomic_read(¤t->trace_overrun); | ||
266 | trace->depth = current->curr_ret_depth--; | ||
267 | /* | ||
268 | * We still want to trace interrupts coming in if | ||
269 | * max_depth is set to 1. Make sure the decrement is | ||
270 | * seen before ftrace_graph_return. | ||
271 | */ | ||
272 | barrier(); | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * Send the trace to the ring-buffer. | ||
277 | * @return the original return address. | ||
278 | */ | ||
279 | unsigned long ftrace_return_to_handler(unsigned long frame_pointer) | ||
280 | { | ||
281 | struct ftrace_graph_ret trace; | ||
282 | unsigned long ret; | ||
283 | |||
284 | ftrace_pop_return_trace(&trace, &ret, frame_pointer); | ||
285 | trace.rettime = trace_clock_local(); | ||
286 | ftrace_graph_return(&trace); | ||
287 | /* | ||
288 | * The ftrace_graph_return() may still access the current | ||
289 | * ret_stack structure, we need to make sure the update of | ||
290 | * curr_ret_stack is after that. | ||
291 | */ | ||
292 | barrier(); | ||
293 | current->curr_ret_stack--; | ||
294 | /* | ||
295 | * The curr_ret_stack can be less than -1 only if it was | ||
296 | * filtered out and it's about to return from the function. | ||
297 | * Recover the index and continue to trace normal functions. | ||
298 | */ | ||
299 | if (current->curr_ret_stack < -1) { | ||
300 | current->curr_ret_stack += FTRACE_NOTRACE_DEPTH; | ||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | if (unlikely(!ret)) { | ||
305 | ftrace_graph_stop(); | ||
306 | WARN_ON(1); | ||
307 | /* Might as well panic. What else to do? */ | ||
308 | ret = (unsigned long)panic; | ||
309 | } | ||
310 | |||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * ftrace_graph_ret_addr - convert a potentially modified stack return address | ||
316 | * to its original value | ||
317 | * | ||
318 | * This function can be called by stack unwinding code to convert a found stack | ||
319 | * return address ('ret') to its original value, in case the function graph | ||
320 | * tracer has modified it to be 'return_to_handler'. If the address hasn't | ||
321 | * been modified, the unchanged value of 'ret' is returned. | ||
322 | * | ||
323 | * 'idx' is a state variable which should be initialized by the caller to zero | ||
324 | * before the first call. | ||
325 | * | ||
326 | * 'retp' is a pointer to the return address on the stack. It's ignored if | ||
327 | * the arch doesn't have HAVE_FUNCTION_GRAPH_RET_ADDR_PTR defined. | ||
328 | */ | ||
329 | #ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR | ||
330 | unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx, | ||
331 | unsigned long ret, unsigned long *retp) | ||
332 | { | ||
333 | int index = task->curr_ret_stack; | ||
334 | int i; | ||
335 | |||
336 | if (ret != (unsigned long)return_to_handler) | ||
337 | return ret; | ||
338 | |||
339 | if (index < -1) | ||
340 | index += FTRACE_NOTRACE_DEPTH; | ||
341 | |||
342 | if (index < 0) | ||
343 | return ret; | ||
344 | |||
345 | for (i = 0; i <= index; i++) | ||
346 | if (task->ret_stack[i].retp == retp) | ||
347 | return task->ret_stack[i].ret; | ||
348 | |||
349 | return ret; | ||
350 | } | ||
351 | #else /* !HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */ | ||
352 | unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx, | ||
353 | unsigned long ret, unsigned long *retp) | ||
354 | { | ||
355 | int task_idx; | ||
356 | |||
357 | if (ret != (unsigned long)return_to_handler) | ||
358 | return ret; | ||
359 | |||
360 | task_idx = task->curr_ret_stack; | ||
361 | |||
362 | if (!task->ret_stack || task_idx < *idx) | ||
363 | return ret; | ||
364 | |||
365 | task_idx -= *idx; | ||
366 | (*idx)++; | ||
367 | |||
368 | return task->ret_stack[task_idx].ret; | ||
369 | } | ||
370 | #endif /* HAVE_FUNCTION_GRAPH_RET_ADDR_PTR */ | ||
371 | |||
372 | int __trace_graph_entry(struct trace_array *tr, | 97 | int __trace_graph_entry(struct trace_array *tr, |
373 | struct ftrace_graph_ent *trace, | 98 | struct ftrace_graph_ent *trace, |
374 | unsigned long flags, | 99 | unsigned long flags, |
@@ -409,6 +134,18 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
409 | int cpu; | 134 | int cpu; |
410 | int pc; | 135 | int pc; |
411 | 136 | ||
137 | if (trace_recursion_test(TRACE_GRAPH_NOTRACE_BIT)) | ||
138 | return 0; | ||
139 | |||
140 | if (ftrace_graph_notrace_addr(trace->func)) { | ||
141 | trace_recursion_set(TRACE_GRAPH_NOTRACE_BIT); | ||
142 | /* | ||
143 | * Need to return 1 to have the return called | ||
144 | * that will clear the NOTRACE bit. | ||
145 | */ | ||
146 | return 1; | ||
147 | } | ||
148 | |||
412 | if (!ftrace_trace_task(tr)) | 149 | if (!ftrace_trace_task(tr)) |
413 | return 0; | 150 | return 0; |
414 | 151 | ||
@@ -511,6 +248,11 @@ void trace_graph_return(struct ftrace_graph_ret *trace) | |||
511 | 248 | ||
512 | ftrace_graph_addr_finish(trace); | 249 | ftrace_graph_addr_finish(trace); |
513 | 250 | ||
251 | if (trace_recursion_test(TRACE_GRAPH_NOTRACE_BIT)) { | ||
252 | trace_recursion_clear(TRACE_GRAPH_NOTRACE_BIT); | ||
253 | return; | ||
254 | } | ||
255 | |||
514 | local_irq_save(flags); | 256 | local_irq_save(flags); |
515 | cpu = raw_smp_processor_id(); | 257 | cpu = raw_smp_processor_id(); |
516 | data = per_cpu_ptr(tr->trace_buffer.data, cpu); | 258 | data = per_cpu_ptr(tr->trace_buffer.data, cpu); |
@@ -536,6 +278,11 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace) | |||
536 | { | 278 | { |
537 | ftrace_graph_addr_finish(trace); | 279 | ftrace_graph_addr_finish(trace); |
538 | 280 | ||
281 | if (trace_recursion_test(TRACE_GRAPH_NOTRACE_BIT)) { | ||
282 | trace_recursion_clear(TRACE_GRAPH_NOTRACE_BIT); | ||
283 | return; | ||
284 | } | ||
285 | |||
539 | if (tracing_thresh && | 286 | if (tracing_thresh && |
540 | (trace->rettime - trace->calltime < tracing_thresh)) | 287 | (trace->rettime - trace->calltime < tracing_thresh)) |
541 | return; | 288 | return; |
@@ -543,17 +290,25 @@ static void trace_graph_thresh_return(struct ftrace_graph_ret *trace) | |||
543 | trace_graph_return(trace); | 290 | trace_graph_return(trace); |
544 | } | 291 | } |
545 | 292 | ||
293 | static struct fgraph_ops funcgraph_thresh_ops = { | ||
294 | .entryfunc = &trace_graph_entry, | ||
295 | .retfunc = &trace_graph_thresh_return, | ||
296 | }; | ||
297 | |||
298 | static struct fgraph_ops funcgraph_ops = { | ||
299 | .entryfunc = &trace_graph_entry, | ||
300 | .retfunc = &trace_graph_return, | ||
301 | }; | ||
302 | |||
546 | static int graph_trace_init(struct trace_array *tr) | 303 | static int graph_trace_init(struct trace_array *tr) |
547 | { | 304 | { |
548 | int ret; | 305 | int ret; |
549 | 306 | ||
550 | set_graph_array(tr); | 307 | set_graph_array(tr); |
551 | if (tracing_thresh) | 308 | if (tracing_thresh) |
552 | ret = register_ftrace_graph(&trace_graph_thresh_return, | 309 | ret = register_ftrace_graph(&funcgraph_thresh_ops); |
553 | &trace_graph_entry); | ||
554 | else | 310 | else |
555 | ret = register_ftrace_graph(&trace_graph_return, | 311 | ret = register_ftrace_graph(&funcgraph_ops); |
556 | &trace_graph_entry); | ||
557 | if (ret) | 312 | if (ret) |
558 | return ret; | 313 | return ret; |
559 | tracing_start_cmdline_record(); | 314 | tracing_start_cmdline_record(); |
@@ -564,7 +319,10 @@ static int graph_trace_init(struct trace_array *tr) | |||
564 | static void graph_trace_reset(struct trace_array *tr) | 319 | static void graph_trace_reset(struct trace_array *tr) |
565 | { | 320 | { |
566 | tracing_stop_cmdline_record(); | 321 | tracing_stop_cmdline_record(); |
567 | unregister_ftrace_graph(); | 322 | if (tracing_thresh) |
323 | unregister_ftrace_graph(&funcgraph_thresh_ops); | ||
324 | else | ||
325 | unregister_ftrace_graph(&funcgraph_ops); | ||
568 | } | 326 | } |
569 | 327 | ||
570 | static int graph_trace_update_thresh(struct trace_array *tr) | 328 | static int graph_trace_update_thresh(struct trace_array *tr) |
@@ -874,10 +632,6 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
874 | 632 | ||
875 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); | 633 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); |
876 | 634 | ||
877 | /* If a graph tracer ignored set_graph_notrace */ | ||
878 | if (call->depth < -1) | ||
879 | call->depth += FTRACE_NOTRACE_DEPTH; | ||
880 | |||
881 | /* | 635 | /* |
882 | * Comments display at + 1 to depth. Since | 636 | * Comments display at + 1 to depth. Since |
883 | * this is a leaf function, keep the comments | 637 | * this is a leaf function, keep the comments |
@@ -920,10 +674,6 @@ print_graph_entry_nested(struct trace_iterator *iter, | |||
920 | struct fgraph_cpu_data *cpu_data; | 674 | struct fgraph_cpu_data *cpu_data; |
921 | int cpu = iter->cpu; | 675 | int cpu = iter->cpu; |
922 | 676 | ||
923 | /* If a graph tracer ignored set_graph_notrace */ | ||
924 | if (call->depth < -1) | ||
925 | call->depth += FTRACE_NOTRACE_DEPTH; | ||
926 | |||
927 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); | 677 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); |
928 | cpu_data->depth = call->depth; | 678 | cpu_data->depth = call->depth; |
929 | 679 | ||