aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_functions_graph.c
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2010-02-26 17:08:16 -0500
committerSteven Rostedt <rostedt@goodmis.org>2010-02-26 19:25:53 -0500
commitf1c7f517a5dc23bce07efa5ed55e2c074ed9d4ba (patch)
tree187be4d66aaa35db2eb1cd77b49520868e999b67 /kernel/trace/trace_functions_graph.c
parent281b3714e91162b66add1cfac404cf7b81e3e2f2 (diff)
ftrace: Add function names to dangling } in function graph tracer
The function graph tracer is currently the most invasive tracer in the ftrace family. It can easily overflow the buffer even with 10megs per CPU. This means that events can often be lost. On start up, or after events are lost, if the function return is recorded but the function enter was lost, all we get to see is the exiting '}'. Here is how a typical trace output starts: [tracing] cat trace # tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | 0) + 91.897 us | } 0) ! 567.961 us | } 0) <========== | 0) ! 579.083 us | _raw_spin_lock_irqsave(); 0) 4.694 us | _raw_spin_unlock_irqrestore(); 0) ! 594.862 us | } 0) ! 603.361 us | } 0) ! 613.574 us | } 0) ! 623.554 us | } 0) 3.653 us | fget_light(); 0) | sock_poll() { There are a series of '}' with no matching "func() {". There's no information to what functions these ending brackets belong to. This patch adds a stack on the per cpu structure used in outputting the function graph tracer to keep track of what function was outputted. Then on a function exit event, it checks the depth to see if the function exit has a matching entry event. If it does, then it only prints the '}', otherwise it adds the function name after the '}'. This allows function exit events to show what function they belong to at trace output startup, when the entry was lost due to ring buffer overflow, or even after a new task is scheduled in. Here is what the above trace will look like after this patch: [tracing] cat trace # tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | 0) + 91.897 us | } (irq_exit) 0) ! 567.961 us | } (smp_apic_timer_interrupt) 0) <========== | 0) ! 579.083 us | _raw_spin_lock_irqsave(); 0) 4.694 us | _raw_spin_unlock_irqrestore(); 0) ! 594.862 us | } (add_wait_queue) 0) ! 603.361 us | } (__pollwait) 0) ! 613.574 us | } (tcp_poll) 0) ! 623.554 us | } (sock_poll) 0) 3.653 us | fget_light(); 0) | sock_poll() { Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
-rw-r--r--kernel/trace/trace_functions_graph.c52
1 files changed, 43 insertions, 9 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 112561df2a0a..e998a824e9db 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -18,6 +18,7 @@ struct fgraph_cpu_data {
18 pid_t last_pid; 18 pid_t last_pid;
19 int depth; 19 int depth;
20 int ignore; 20 int ignore;
21 unsigned long enter_funcs[FTRACE_RETFUNC_DEPTH];
21}; 22};
22 23
23struct fgraph_data { 24struct fgraph_data {
@@ -670,15 +671,21 @@ print_graph_entry_leaf(struct trace_iterator *iter,
670 duration = graph_ret->rettime - graph_ret->calltime; 671 duration = graph_ret->rettime - graph_ret->calltime;
671 672
672 if (data) { 673 if (data) {
674 struct fgraph_cpu_data *cpu_data;
673 int cpu = iter->cpu; 675 int cpu = iter->cpu;
674 int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); 676
677 cpu_data = per_cpu_ptr(data->cpu_data, cpu);
675 678
676 /* 679 /*
677 * Comments display at + 1 to depth. Since 680 * Comments display at + 1 to depth. Since
678 * this is a leaf function, keep the comments 681 * this is a leaf function, keep the comments
679 * equal to this depth. 682 * equal to this depth.
680 */ 683 */
681 *depth = call->depth - 1; 684 cpu_data->depth = call->depth - 1;
685
686 /* No need to keep this function around for this depth */
687 if (call->depth < FTRACE_RETFUNC_DEPTH)
688 cpu_data->enter_funcs[call->depth] = 0;
682 } 689 }
683 690
684 /* Overhead */ 691 /* Overhead */
@@ -718,10 +725,15 @@ print_graph_entry_nested(struct trace_iterator *iter,
718 int i; 725 int i;
719 726
720 if (data) { 727 if (data) {
728 struct fgraph_cpu_data *cpu_data;
721 int cpu = iter->cpu; 729 int cpu = iter->cpu;
722 int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth);
723 730
724 *depth = call->depth; 731 cpu_data = per_cpu_ptr(data->cpu_data, cpu);
732 cpu_data->depth = call->depth;
733
734 /* Save this function pointer to see if the exit matches */
735 if (call->depth < FTRACE_RETFUNC_DEPTH)
736 cpu_data->enter_funcs[call->depth] = call->func;
725 } 737 }
726 738
727 /* No overhead */ 739 /* No overhead */
@@ -851,18 +863,28 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
851 struct fgraph_data *data = iter->private; 863 struct fgraph_data *data = iter->private;
852 pid_t pid = ent->pid; 864 pid_t pid = ent->pid;
853 int cpu = iter->cpu; 865 int cpu = iter->cpu;
866 int func_match = 1;
854 int ret; 867 int ret;
855 int i; 868 int i;
856 869
857 if (data) { 870 if (data) {
858 int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); 871 struct fgraph_cpu_data *cpu_data;
872 int cpu = iter->cpu;
873
874 cpu_data = per_cpu_ptr(data->cpu_data, cpu);
859 875
860 /* 876 /*
861 * Comments display at + 1 to depth. This is the 877 * Comments display at + 1 to depth. This is the
862 * return from a function, we now want the comments 878 * return from a function, we now want the comments
863 * to display at the same level of the bracket. 879 * to display at the same level of the bracket.
864 */ 880 */
865 *depth = trace->depth - 1; 881 cpu_data->depth = trace->depth - 1;
882
883 if (trace->depth < FTRACE_RETFUNC_DEPTH) {
884 if (cpu_data->enter_funcs[trace->depth] != trace->func)
885 func_match = 0;
886 cpu_data->enter_funcs[trace->depth] = 0;
887 }
866 } 888 }
867 889
868 if (print_graph_prologue(iter, s, 0, 0)) 890 if (print_graph_prologue(iter, s, 0, 0))
@@ -887,9 +909,21 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
887 return TRACE_TYPE_PARTIAL_LINE; 909 return TRACE_TYPE_PARTIAL_LINE;
888 } 910 }
889 911
890 ret = trace_seq_printf(s, "}\n"); 912 /*
891 if (!ret) 913 * If the return function does not have a matching entry,
892 return TRACE_TYPE_PARTIAL_LINE; 914 * then the entry was lost. Instead of just printing
915 * the '}' and letting the user guess what function this
916 * belongs to, write out the function name.
917 */
918 if (func_match) {
919 ret = trace_seq_printf(s, "}\n");
920 if (!ret)
921 return TRACE_TYPE_PARTIAL_LINE;
922 } else {
923 ret = trace_seq_printf(s, "} (%ps)\n", (void *)trace->func);
924 if (!ret)
925 return TRACE_TYPE_PARTIAL_LINE;
926 }
893 927
894 /* Overrun */ 928 /* Overrun */
895 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { 929 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) {