trace: add non-hierarchical function_graph option
authorJamie Gennis <jgennis@google.com>
Thu, 22 Nov 2012 04:14:09 +0000 (20:14 -0800)
committerArve Hjønnevåg <arve@android.com>
Mon, 1 Jul 2013 21:16:22 +0000 (14:16 -0700)
Add the 'funcgraph-flat' option to the function_graph tracer to use the default
trace printing format rather than the hierarchical formatting normally used.

Change-Id: If2900bfb86e6f8f51379f56da4f6fabafa630909
Signed-off-by: Jamie Gennis <jgennis@google.com>
Documentation/trace/ftrace.txt
kernel/trace/trace_functions_graph.c
kernel/trace/trace_output.c

index bfe8c29b1f1d84cf5c6a9d71c3c69b20f2bdf12f..29064c3bfcd1bae1b0f39d6078a3f28e34ed3803 100644 (file)
@@ -2013,6 +2013,35 @@ will produce:
  1)   1.449 us    |             }
 
 
+You can disable the hierarchical function call formatting and instead print a
+flat list of function entry and return events.  This uses the format described
+in the Output Formatting section and respects all the trace options that
+control that formatting.  Hierarchical formatting is the default.
+
+       hierachical: echo nofuncgraph-flat > trace_options
+       flat: echo funcgraph-flat > trace_options
+
+  ie:
+
+  # tracer: function_graph
+  #
+  # entries-in-buffer/entries-written: 68355/68355   #P:2
+  #
+  #                              _-----=> irqs-off
+  #                             / _----=> need-resched
+  #                            | / _---=> hardirq/softirq
+  #                            || / _--=> preempt-depth
+  #                            ||| /     delay
+  #           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
+  #              | |       |   ||||       |         |
+                sh-1806  [001] d...   198.843443: graph_ent: func=_raw_spin_lock
+                sh-1806  [001] d...   198.843445: graph_ent: func=__raw_spin_lock
+                sh-1806  [001] d..1   198.843447: graph_ret: func=__raw_spin_lock
+                sh-1806  [001] d..1   198.843449: graph_ret: func=_raw_spin_lock
+                sh-1806  [001] d..1   198.843451: graph_ent: func=_raw_spin_unlock_irqrestore
+                sh-1806  [001] d...   198.843453: graph_ret: func=_raw_spin_unlock_irqrestore
+
+
 You might find other useful features for this tracer in the
 following "dynamic ftrace" section such as tracing only specific
 functions or tasks.
index 8388bc99f2eee165efacbef7ddde4f7f21d49ea3..28dd40c2c423e45d5eff084bf397829082986c03 100644 (file)
@@ -46,6 +46,8 @@ struct fgraph_data {
 #define TRACE_GRAPH_PRINT_DURATION     0x10
 #define TRACE_GRAPH_PRINT_ABS_TIME     0x20
 #define TRACE_GRAPH_PRINT_IRQS         0x40
+#define TRACE_GRAPH_PRINT_FLAT         0x80
+
 
 static unsigned int max_depth;
 
@@ -64,6 +66,8 @@ static struct tracer_opt trace_opts[] = {
        { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) },
        /* Display interrupts */
        { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) },
+       /* Use standard trace formatting rather than hierarchical */
+       { TRACER_OPT(funcgraph-flat, TRACE_GRAPH_PRINT_FLAT) },
        { } /* Empty entry */
 };
 
@@ -1234,6 +1238,9 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags)
        int cpu = iter->cpu;
        int ret;
 
+       if (flags & TRACE_GRAPH_PRINT_FLAT)
+               return TRACE_TYPE_UNHANDLED;
+
        if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) {
                per_cpu_ptr(data->cpu_data, cpu)->ignore = 0;
                return TRACE_TYPE_HANDLED;
@@ -1291,13 +1298,6 @@ print_graph_function(struct trace_iterator *iter)
        return print_graph_function_flags(iter, tracer_flags.val);
 }
 
-static enum print_line_t
-print_graph_function_event(struct trace_iterator *iter, int flags,
-                          struct trace_event *event)
-{
-       return print_graph_function(iter);
-}
-
 static void print_lat_header(struct seq_file *s, u32 flags)
 {
        static const char spaces[] = "                " /* 16 spaces */
@@ -1364,6 +1364,11 @@ void print_graph_headers_flags(struct seq_file *s, u32 flags)
 {
        struct trace_iterator *iter = s->private;
 
+       if (flags & TRACE_GRAPH_PRINT_FLAT) {
+               trace_default_header(s);
+               return;
+       }
+
        if (!(trace_flags & TRACE_ITER_CONTEXT_INFO))
                return;
 
@@ -1434,20 +1439,6 @@ static int func_graph_set_flag(u32 old_flags, u32 bit, int set)
        return 0;
 }
 
-static struct trace_event_functions graph_functions = {
-       .trace          = print_graph_function_event,
-};
-
-static struct trace_event graph_trace_entry_event = {
-       .type           = TRACE_GRAPH_ENT,
-       .funcs          = &graph_functions,
-};
-
-static struct trace_event graph_trace_ret_event = {
-       .type           = TRACE_GRAPH_RET,
-       .funcs          = &graph_functions
-};
-
 static struct tracer graph_trace __read_mostly = {
        .name           = "function_graph",
        .open           = graph_trace_open,
@@ -1523,16 +1514,6 @@ static __init int init_graph_trace(void)
 {
        max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1);
 
-       if (!register_ftrace_event(&graph_trace_entry_event)) {
-               pr_warning("Warning: could not register graph trace events\n");
-               return 1;
-       }
-
-       if (!register_ftrace_event(&graph_trace_ret_event)) {
-               pr_warning("Warning: could not register graph trace events\n");
-               return 1;
-       }
-
        return register_tracer(&graph_trace);
 }
 
index 31994d2845ea2aa0ec5a67295167662376718676..a68e5e34c00b022669f2b828a756517bf8d2242c 100644 (file)
@@ -1049,6 +1049,168 @@ static struct trace_event trace_fn_event = {
        .funcs          = &trace_fn_funcs,
 };
 
+/* TRACE_GRAPH_ENT */
+static enum print_line_t trace_graph_ent_trace(struct trace_iterator *iter, int flags,
+                                       struct trace_event *event)
+{
+       struct trace_seq *s = &iter->seq;
+       struct ftrace_graph_ent_entry *field;
+
+       trace_assign_type(field, iter->ent);
+
+       if (!trace_seq_puts(s, "graph_ent: func="))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       if (!seq_print_ip_sym(s, field->graph_ent.func, flags))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       if (!trace_seq_puts(s, "\n"))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ent_raw(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
+{
+       struct ftrace_graph_ent_entry *field;
+
+       trace_assign_type(field, iter->ent);
+
+       if (!trace_seq_printf(&iter->seq, "%lx %d\n",
+                             field->graph_ent.func,
+                             field->graph_ent.depth))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ent_hex(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
+{
+       struct ftrace_graph_ent_entry *field;
+       struct trace_seq *s = &iter->seq;
+
+       trace_assign_type(field, iter->ent);
+
+       SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.func);
+       SEQ_PUT_HEX_FIELD_RET(s, field->graph_ent.depth);
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ent_bin(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
+{
+       struct ftrace_graph_ent_entry *field;
+       struct trace_seq *s = &iter->seq;
+
+       trace_assign_type(field, iter->ent);
+
+       SEQ_PUT_FIELD_RET(s, field->graph_ent.func);
+       SEQ_PUT_FIELD_RET(s, field->graph_ent.depth);
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static struct trace_event_functions trace_graph_ent_funcs = {
+       .trace          = trace_graph_ent_trace,
+       .raw            = trace_graph_ent_raw,
+       .hex            = trace_graph_ent_hex,
+       .binary         = trace_graph_ent_bin,
+};
+
+static struct trace_event trace_graph_ent_event = {
+       .type           = TRACE_GRAPH_ENT,
+       .funcs          = &trace_graph_ent_funcs,
+};
+
+/* TRACE_GRAPH_RET */
+static enum print_line_t trace_graph_ret_trace(struct trace_iterator *iter, int flags,
+                                       struct trace_event *event)
+{
+       struct trace_seq *s = &iter->seq;
+       struct trace_entry *entry = iter->ent;
+       struct ftrace_graph_ret_entry *field;
+
+       trace_assign_type(field, entry);
+
+       if (!trace_seq_puts(s, "graph_ret: func="))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       if (!seq_print_ip_sym(s, field->ret.func, flags))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       if (!trace_seq_puts(s, "\n"))
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ret_raw(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
+{
+       struct ftrace_graph_ret_entry *field;
+
+       trace_assign_type(field, iter->ent);
+
+       if (!trace_seq_printf(&iter->seq, "%lx %lld %lld %ld %d\n",
+                             field->ret.func,
+                             field->ret.calltime,
+                             field->ret.rettime,
+                             field->ret.overrun,
+                             field->ret.depth));
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ret_hex(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
+{
+       struct ftrace_graph_ret_entry *field;
+       struct trace_seq *s = &iter->seq;
+
+       trace_assign_type(field, iter->ent);
+
+       SEQ_PUT_HEX_FIELD_RET(s, field->ret.func);
+       SEQ_PUT_HEX_FIELD_RET(s, field->ret.calltime);
+       SEQ_PUT_HEX_FIELD_RET(s, field->ret.rettime);
+       SEQ_PUT_HEX_FIELD_RET(s, field->ret.overrun);
+       SEQ_PUT_HEX_FIELD_RET(s, field->ret.depth);
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static enum print_line_t trace_graph_ret_bin(struct trace_iterator *iter, int flags,
+                                     struct trace_event *event)
+{
+       struct ftrace_graph_ret_entry *field;
+       struct trace_seq *s = &iter->seq;
+
+       trace_assign_type(field, iter->ent);
+
+       SEQ_PUT_FIELD_RET(s, field->ret.func);
+       SEQ_PUT_FIELD_RET(s, field->ret.calltime);
+       SEQ_PUT_FIELD_RET(s, field->ret.rettime);
+       SEQ_PUT_FIELD_RET(s, field->ret.overrun);
+       SEQ_PUT_FIELD_RET(s, field->ret.depth);
+
+       return TRACE_TYPE_HANDLED;
+}
+
+static struct trace_event_functions trace_graph_ret_funcs = {
+       .trace          = trace_graph_ret_trace,
+       .raw            = trace_graph_ret_raw,
+       .hex            = trace_graph_ret_hex,
+       .binary         = trace_graph_ret_bin,
+};
+
+static struct trace_event trace_graph_ret_event = {
+       .type           = TRACE_GRAPH_RET,
+       .funcs          = &trace_graph_ret_funcs,
+};
+
 /* TRACE_CTX an TRACE_WAKE */
 static enum print_line_t trace_ctxwake_print(struct trace_iterator *iter,
                                             char *delim)
@@ -1439,6 +1601,8 @@ static struct trace_event trace_print_event = {
 
 static struct trace_event *events[] __initdata = {
        &trace_fn_event,
+       &trace_graph_ent_event,
+       &trace_graph_ret_event,
        &trace_ctx_event,
        &trace_wake_event,
        &trace_stack_event,