diff options
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 108 |
1 files changed, 80 insertions, 28 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index b1342c5d37cf..9aed1a5cf553 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/debugfs.h> | 9 | #include <linux/debugfs.h> |
10 | #include <linux/uaccess.h> | 10 | #include <linux/uaccess.h> |
11 | #include <linux/ftrace.h> | 11 | #include <linux/ftrace.h> |
12 | #include <linux/slab.h> | ||
12 | #include <linux/fs.h> | 13 | #include <linux/fs.h> |
13 | 14 | ||
14 | #include "trace.h" | 15 | #include "trace.h" |
@@ -18,6 +19,7 @@ struct fgraph_cpu_data { | |||
18 | pid_t last_pid; | 19 | pid_t last_pid; |
19 | int depth; | 20 | int depth; |
20 | int ignore; | 21 | int ignore; |
22 | unsigned long enter_funcs[FTRACE_RETFUNC_DEPTH]; | ||
21 | }; | 23 | }; |
22 | 24 | ||
23 | struct fgraph_data { | 25 | struct fgraph_data { |
@@ -187,7 +189,7 @@ static int __trace_graph_entry(struct trace_array *tr, | |||
187 | struct ring_buffer *buffer = tr->buffer; | 189 | struct ring_buffer *buffer = tr->buffer; |
188 | struct ftrace_graph_ent_entry *entry; | 190 | struct ftrace_graph_ent_entry *entry; |
189 | 191 | ||
190 | if (unlikely(__this_cpu_read(per_cpu_var(ftrace_cpu_disabled)))) | 192 | if (unlikely(__this_cpu_read(ftrace_cpu_disabled))) |
191 | return 0; | 193 | return 0; |
192 | 194 | ||
193 | event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT, | 195 | event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT, |
@@ -212,13 +214,11 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
212 | int cpu; | 214 | int cpu; |
213 | int pc; | 215 | int pc; |
214 | 216 | ||
215 | if (unlikely(!tr)) | ||
216 | return 0; | ||
217 | |||
218 | if (!ftrace_trace_task(current)) | 217 | if (!ftrace_trace_task(current)) |
219 | return 0; | 218 | return 0; |
220 | 219 | ||
221 | if (!ftrace_graph_addr(trace->func)) | 220 | /* trace it when it is-nested-in or is a function enabled. */ |
221 | if (!(trace->depth || ftrace_graph_addr(trace->func))) | ||
222 | return 0; | 222 | return 0; |
223 | 223 | ||
224 | local_irq_save(flags); | 224 | local_irq_save(flags); |
@@ -231,9 +231,6 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
231 | } else { | 231 | } else { |
232 | ret = 0; | 232 | ret = 0; |
233 | } | 233 | } |
234 | /* Only do the atomic if it is not already set */ | ||
235 | if (!test_tsk_trace_graph(current)) | ||
236 | set_tsk_trace_graph(current); | ||
237 | 234 | ||
238 | atomic_dec(&data->disabled); | 235 | atomic_dec(&data->disabled); |
239 | local_irq_restore(flags); | 236 | local_irq_restore(flags); |
@@ -241,6 +238,14 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
241 | return ret; | 238 | return ret; |
242 | } | 239 | } |
243 | 240 | ||
241 | int trace_graph_thresh_entry(struct ftrace_graph_ent *trace) | ||
242 | { | ||
243 | if (tracing_thresh) | ||
244 | return 1; | ||
245 | else | ||
246 | return trace_graph_entry(trace); | ||
247 | } | ||
248 | |||
244 | static void __trace_graph_return(struct trace_array *tr, | 249 | static void __trace_graph_return(struct trace_array *tr, |
245 | struct ftrace_graph_ret *trace, | 250 | struct ftrace_graph_ret *trace, |
246 | unsigned long flags, | 251 | unsigned long flags, |
@@ -251,7 +256,7 @@ static void __trace_graph_return(struct trace_array *tr, | |||
251 | struct ring_buffer *buffer = tr->buffer; | 256 | struct ring_buffer *buffer = tr->buffer; |
252 | struct ftrace_graph_ret_entry *entry; | 257 | struct ftrace_graph_ret_entry *entry; |
253 | 258 | ||
254 | if (unlikely(__this_cpu_read(per_cpu_var(ftrace_cpu_disabled)))) | 259 | if (unlikely(__this_cpu_read(ftrace_cpu_disabled))) |
255 | return; | 260 | return; |
256 | 261 | ||
257 | event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET, | 262 | event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET, |
@@ -281,19 +286,39 @@ void trace_graph_return(struct ftrace_graph_ret *trace) | |||
281 | pc = preempt_count(); | 286 | pc = preempt_count(); |
282 | __trace_graph_return(tr, trace, flags, pc); | 287 | __trace_graph_return(tr, trace, flags, pc); |
283 | } | 288 | } |
284 | if (!trace->depth) | ||
285 | clear_tsk_trace_graph(current); | ||
286 | atomic_dec(&data->disabled); | 289 | atomic_dec(&data->disabled); |
287 | local_irq_restore(flags); | 290 | local_irq_restore(flags); |
288 | } | 291 | } |
289 | 292 | ||
293 | void set_graph_array(struct trace_array *tr) | ||
294 | { | ||
295 | graph_array = tr; | ||
296 | |||
297 | /* Make graph_array visible before we start tracing */ | ||
298 | |||
299 | smp_mb(); | ||
300 | } | ||
301 | |||
302 | void trace_graph_thresh_return(struct ftrace_graph_ret *trace) | ||
303 | { | ||
304 | if (tracing_thresh && | ||
305 | (trace->rettime - trace->calltime < tracing_thresh)) | ||
306 | return; | ||
307 | else | ||
308 | trace_graph_return(trace); | ||
309 | } | ||
310 | |||
290 | static int graph_trace_init(struct trace_array *tr) | 311 | static int graph_trace_init(struct trace_array *tr) |
291 | { | 312 | { |
292 | int ret; | 313 | int ret; |
293 | 314 | ||
294 | graph_array = tr; | 315 | set_graph_array(tr); |
295 | ret = register_ftrace_graph(&trace_graph_return, | 316 | if (tracing_thresh) |
296 | &trace_graph_entry); | 317 | ret = register_ftrace_graph(&trace_graph_thresh_return, |
318 | &trace_graph_thresh_entry); | ||
319 | else | ||
320 | ret = register_ftrace_graph(&trace_graph_return, | ||
321 | &trace_graph_entry); | ||
297 | if (ret) | 322 | if (ret) |
298 | return ret; | 323 | return ret; |
299 | tracing_start_cmdline_record(); | 324 | tracing_start_cmdline_record(); |
@@ -301,11 +326,6 @@ static int graph_trace_init(struct trace_array *tr) | |||
301 | return 0; | 326 | return 0; |
302 | } | 327 | } |
303 | 328 | ||
304 | void set_graph_array(struct trace_array *tr) | ||
305 | { | ||
306 | graph_array = tr; | ||
307 | } | ||
308 | |||
309 | static void graph_trace_reset(struct trace_array *tr) | 329 | static void graph_trace_reset(struct trace_array *tr) |
310 | { | 330 | { |
311 | tracing_stop_cmdline_record(); | 331 | tracing_stop_cmdline_record(); |
@@ -673,15 +693,21 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
673 | duration = graph_ret->rettime - graph_ret->calltime; | 693 | duration = graph_ret->rettime - graph_ret->calltime; |
674 | 694 | ||
675 | if (data) { | 695 | if (data) { |
696 | struct fgraph_cpu_data *cpu_data; | ||
676 | int cpu = iter->cpu; | 697 | int cpu = iter->cpu; |
677 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); | 698 | |
699 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); | ||
678 | 700 | ||
679 | /* | 701 | /* |
680 | * Comments display at + 1 to depth. Since | 702 | * Comments display at + 1 to depth. Since |
681 | * this is a leaf function, keep the comments | 703 | * this is a leaf function, keep the comments |
682 | * equal to this depth. | 704 | * equal to this depth. |
683 | */ | 705 | */ |
684 | *depth = call->depth - 1; | 706 | cpu_data->depth = call->depth - 1; |
707 | |||
708 | /* No need to keep this function around for this depth */ | ||
709 | if (call->depth < FTRACE_RETFUNC_DEPTH) | ||
710 | cpu_data->enter_funcs[call->depth] = 0; | ||
685 | } | 711 | } |
686 | 712 | ||
687 | /* Overhead */ | 713 | /* Overhead */ |
@@ -721,10 +747,15 @@ print_graph_entry_nested(struct trace_iterator *iter, | |||
721 | int i; | 747 | int i; |
722 | 748 | ||
723 | if (data) { | 749 | if (data) { |
750 | struct fgraph_cpu_data *cpu_data; | ||
724 | int cpu = iter->cpu; | 751 | int cpu = iter->cpu; |
725 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); | ||
726 | 752 | ||
727 | *depth = call->depth; | 753 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); |
754 | cpu_data->depth = call->depth; | ||
755 | |||
756 | /* Save this function pointer to see if the exit matches */ | ||
757 | if (call->depth < FTRACE_RETFUNC_DEPTH) | ||
758 | cpu_data->enter_funcs[call->depth] = call->func; | ||
728 | } | 759 | } |
729 | 760 | ||
730 | /* No overhead */ | 761 | /* No overhead */ |
@@ -854,19 +885,28 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
854 | struct fgraph_data *data = iter->private; | 885 | struct fgraph_data *data = iter->private; |
855 | pid_t pid = ent->pid; | 886 | pid_t pid = ent->pid; |
856 | int cpu = iter->cpu; | 887 | int cpu = iter->cpu; |
888 | int func_match = 1; | ||
857 | int ret; | 889 | int ret; |
858 | int i; | 890 | int i; |
859 | 891 | ||
860 | if (data) { | 892 | if (data) { |
893 | struct fgraph_cpu_data *cpu_data; | ||
861 | int cpu = iter->cpu; | 894 | int cpu = iter->cpu; |
862 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); | 895 | |
896 | cpu_data = per_cpu_ptr(data->cpu_data, cpu); | ||
863 | 897 | ||
864 | /* | 898 | /* |
865 | * Comments display at + 1 to depth. This is the | 899 | * Comments display at + 1 to depth. This is the |
866 | * return from a function, we now want the comments | 900 | * return from a function, we now want the comments |
867 | * to display at the same level of the bracket. | 901 | * to display at the same level of the bracket. |
868 | */ | 902 | */ |
869 | *depth = trace->depth - 1; | 903 | cpu_data->depth = trace->depth - 1; |
904 | |||
905 | if (trace->depth < FTRACE_RETFUNC_DEPTH) { | ||
906 | if (cpu_data->enter_funcs[trace->depth] != trace->func) | ||
907 | func_match = 0; | ||
908 | cpu_data->enter_funcs[trace->depth] = 0; | ||
909 | } | ||
870 | } | 910 | } |
871 | 911 | ||
872 | if (print_graph_prologue(iter, s, 0, 0)) | 912 | if (print_graph_prologue(iter, s, 0, 0)) |
@@ -891,9 +931,21 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
891 | return TRACE_TYPE_PARTIAL_LINE; | 931 | return TRACE_TYPE_PARTIAL_LINE; |
892 | } | 932 | } |
893 | 933 | ||
894 | ret = trace_seq_printf(s, "}\n"); | 934 | /* |
895 | if (!ret) | 935 | * If the return function does not have a matching entry, |
896 | return TRACE_TYPE_PARTIAL_LINE; | 936 | * then the entry was lost. Instead of just printing |
937 | * the '}' and letting the user guess what function this | ||
938 | * belongs to, write out the function name. | ||
939 | */ | ||
940 | if (func_match) { | ||
941 | ret = trace_seq_printf(s, "}\n"); | ||
942 | if (!ret) | ||
943 | return TRACE_TYPE_PARTIAL_LINE; | ||
944 | } else { | ||
945 | ret = trace_seq_printf(s, "} /* %ps */\n", (void *)trace->func); | ||
946 | if (!ret) | ||
947 | return TRACE_TYPE_PARTIAL_LINE; | ||
948 | } | ||
897 | 949 | ||
898 | /* Overrun */ | 950 | /* Overrun */ |
899 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { | 951 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { |