]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/trace/trace_functions_graph.c
Merge branch 'x86/core' into tracing/ftrace
[karo-tx-linux.git] / kernel / trace / trace_functions_graph.c
index c97594d826bc56e1c481975a0bb9b1c1034f1b75..35257be6a9d6fb2a3352995d78a0e95abee65e94 100644 (file)
@@ -50,13 +50,87 @@ static struct tracer_flags tracer_flags = {
 /* pid on the last trace processed */
 
 
+/* Add a function return address to the trace stack on thread info.*/
+int
+ftrace_push_return_trace(unsigned long ret, unsigned long long time,
+                        unsigned long func, int *depth)
+{
+       int index;
+
+       if (!current->ret_stack)
+               return -EBUSY;
+
+       /* The return trace stack is full */
+       if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) {
+               atomic_inc(&current->trace_overrun);
+               return -EBUSY;
+       }
+
+       index = ++current->curr_ret_stack;
+       barrier();
+       current->ret_stack[index].ret = ret;
+       current->ret_stack[index].func = func;
+       current->ret_stack[index].calltime = time;
+       *depth = index;
+
+       return 0;
+}
+
+/* Retrieve a function return address to the trace stack on thread info.*/
+void
+ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret)
+{
+       int index;
+
+       index = current->curr_ret_stack;
+
+       if (unlikely(index < 0)) {
+               ftrace_graph_stop();
+               WARN_ON(1);
+               /* Might as well panic, otherwise we have no where to go */
+               *ret = (unsigned long)panic;
+               return;
+       }
+
+       *ret = current->ret_stack[index].ret;
+       trace->func = current->ret_stack[index].func;
+       trace->calltime = current->ret_stack[index].calltime;
+       trace->overrun = atomic_read(&current->trace_overrun);
+       trace->depth = index;
+       barrier();
+       current->curr_ret_stack--;
+
+}
+
+/*
+ * Send the trace to the ring-buffer.
+ * @return the original return address.
+ */
+unsigned long ftrace_return_to_handler(void)
+{
+       struct ftrace_graph_ret trace;
+       unsigned long ret;
+
+       ftrace_pop_return_trace(&trace, &ret);
+       trace.rettime = trace_clock_local();
+       ftrace_graph_return(&trace);
+
+       if (unlikely(!ret)) {
+               ftrace_graph_stop();
+               WARN_ON(1);
+               /* Might as well panic. What else to do? */
+               ret = (unsigned long)panic;
+       }
+
+       return ret;
+}
+
 static int graph_trace_init(struct trace_array *tr)
 {
        int ret = register_ftrace_graph(&trace_graph_return,
                                        &trace_graph_entry);
        if (ret)
                return ret;
-       tracing_reset_online_cpus(tr);
        tracing_start_cmdline_record();
 
        return 0;
@@ -187,34 +261,34 @@ verif_pid(struct trace_seq *s, pid_t pid, int cpu, pid_t *last_pids_cpu)
        ret = trace_seq_printf(s,
                " ------------------------------------------\n");
        if (!ret)
-               TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_PARTIAL_LINE;
 
        ret = print_graph_cpu(s, cpu);
        if (ret == TRACE_TYPE_PARTIAL_LINE)
-               TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_PARTIAL_LINE;
 
        ret = print_graph_proc(s, prev_pid);
        if (ret == TRACE_TYPE_PARTIAL_LINE)
-               TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_PARTIAL_LINE;
 
        ret = trace_seq_printf(s, " => ");
        if (!ret)
-               TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_PARTIAL_LINE;
 
        ret = print_graph_proc(s, pid);
        if (ret == TRACE_TYPE_PARTIAL_LINE)
-               TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_PARTIAL_LINE;
 
        ret = trace_seq_printf(s,
                "\n ------------------------------------------\n\n");
        if (!ret)
-               TRACE_TYPE_PARTIAL_LINE;
+               return TRACE_TYPE_PARTIAL_LINE;
 
-       return ret;
+       return TRACE_TYPE_HANDLED;
 }
 
-static bool
-trace_branch_is_leaf(struct trace_iterator *iter,
+static struct ftrace_graph_ret_entry *
+get_return_for_leaf(struct trace_iterator *iter,
                struct ftrace_graph_ent_entry *curr)
 {
        struct ring_buffer_iter *ring_iter;
@@ -223,24 +297,33 @@ trace_branch_is_leaf(struct trace_iterator *iter,
 
        ring_iter = iter->buffer_iter[iter->cpu];
 
-       if (!ring_iter)
-               return false;
-
-       event = ring_buffer_iter_peek(ring_iter, NULL);
+       /* First peek to compare current entry and the next one */
+       if (ring_iter)
+               event = ring_buffer_iter_peek(ring_iter, NULL);
+       else {
+       /* We need to consume the current entry to see the next one */
+               ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL);
+               event = ring_buffer_peek(iter->tr->buffer, iter->cpu,
+                                       NULL);
+       }
 
        if (!event)
-               return false;
+               return NULL;
 
        next = ring_buffer_event_data(event);
 
        if (next->ent.type != TRACE_GRAPH_RET)
-               return false;
+               return NULL;
 
        if (curr->ent.pid != next->ent.pid ||
                        curr->graph_ent.func != next->ret.func)
-               return false;
+               return NULL;
+
+       /* this is a leaf, now advance the iterator */
+       if (ring_iter)
+               ring_buffer_read(ring_iter, NULL);
 
-       return true;
+       return next;
 }
 
 /* Signal a overhead of time execution to the output */
@@ -268,16 +351,35 @@ print_graph_overhead(unsigned long long duration, struct trace_seq *s)
        return trace_seq_printf(s, "  ");
 }
 
+static int print_graph_abs_time(u64 t, struct trace_seq *s)
+{
+       unsigned long usecs_rem;
+
+       usecs_rem = do_div(t, NSEC_PER_SEC);
+       usecs_rem /= 1000;
+
+       return trace_seq_printf(s, "%5lu.%06lu |  ",
+                       (unsigned long)t, usecs_rem);
+}
+
 static enum print_line_t
-print_graph_irq(struct trace_seq *s, unsigned long addr,
+print_graph_irq(struct trace_iterator *iter, unsigned long addr,
                enum trace_type type, int cpu, pid_t pid)
 {
        int ret;
+       struct trace_seq *s = &iter->seq;
 
        if (addr < (unsigned long)__irqentry_text_start ||
                addr >= (unsigned long)__irqentry_text_end)
                return TRACE_TYPE_UNHANDLED;
 
+       /* Absolute time */
+       if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) {
+               ret = print_graph_abs_time(iter->ts, s);
+               if (!ret)
+                       return TRACE_TYPE_PARTIAL_LINE;
+       }
+
        /* Cpu */
        if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
                ret = print_graph_cpu(s, cpu);
@@ -363,32 +465,18 @@ print_graph_duration(unsigned long long duration, struct trace_seq *s)
 
 }
 
-static int print_graph_abs_time(u64 t, struct trace_seq *s)
-{
-       unsigned long usecs_rem;
-
-       usecs_rem = do_div(t, 1000000000);
-       usecs_rem /= 1000;
-
-       return trace_seq_printf(s, "%5lu.%06lu |  ",
-                       (unsigned long)t, usecs_rem);
-}
-
 /* Case of a leaf function on its call entry */
 static enum print_line_t
 print_graph_entry_leaf(struct trace_iterator *iter,
-               struct ftrace_graph_ent_entry *entry, struct trace_seq *s)
+               struct ftrace_graph_ent_entry *entry,
+               struct ftrace_graph_ret_entry *ret_entry, struct trace_seq *s)
 {
-       struct ftrace_graph_ret_entry *ret_entry;
        struct ftrace_graph_ret *graph_ret;
-       struct ring_buffer_event *event;
        struct ftrace_graph_ent *call;
        unsigned long long duration;
        int ret;
        int i;
 
-       event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
-       ret_entry = ring_buffer_event_data(event);
        graph_ret = &ret_entry->ret;
        call = &entry->graph_ent;
        duration = graph_ret->rettime - graph_ret->calltime;
@@ -458,7 +546,11 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
-       return TRACE_TYPE_HANDLED;
+       /*
+        * we already consumed the current entry to check the next one
+        * and see if this is a leaf.
+        */
+       return TRACE_TYPE_NO_CONSUME;
 }
 
 static enum print_line_t
@@ -470,13 +562,14 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
        pid_t *last_entry = iter->private;
        struct trace_entry *ent = iter->ent;
        struct ftrace_graph_ent *call = &field->graph_ent;
+       struct ftrace_graph_ret_entry *leaf_ret;
 
        /* Pid */
        if (verif_pid(s, ent->pid, cpu, last_entry) == TRACE_TYPE_PARTIAL_LINE)
                return TRACE_TYPE_PARTIAL_LINE;
 
        /* Interrupt */
-       ret = print_graph_irq(s, call->func, TRACE_GRAPH_ENT, cpu, ent->pid);
+       ret = print_graph_irq(iter, call->func, TRACE_GRAPH_ENT, cpu, ent->pid);
        if (ret == TRACE_TYPE_PARTIAL_LINE)
                return TRACE_TYPE_PARTIAL_LINE;
 
@@ -505,8 +598,9 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
                        return TRACE_TYPE_PARTIAL_LINE;
        }
 
-       if (trace_branch_is_leaf(iter, field))
-               return print_graph_entry_leaf(iter, field, s);
+       leaf_ret = get_return_for_leaf(iter, field);
+       if (leaf_ret)
+               return print_graph_entry_leaf(iter, field, leaf_ret, s);
        else
                return print_graph_entry_nested(field, s, iter->ent->pid, cpu);
 
@@ -519,11 +613,11 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
        int i;
        int ret;
        int cpu = iter->cpu;
-       pid_t *last_pid = iter->private;
+       pid_t *last_pid = iter->private, pid = ent->pid;
        unsigned long long duration = trace->rettime - trace->calltime;
 
        /* Pid */
-       if (verif_pid(s, ent->pid, cpu, last_pid) == TRACE_TYPE_PARTIAL_LINE)
+       if (verif_pid(s, pid, cpu, last_pid) == TRACE_TYPE_PARTIAL_LINE)
                return TRACE_TYPE_PARTIAL_LINE;
 
        /* Absolute time */
@@ -582,7 +676,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
                        return TRACE_TYPE_PARTIAL_LINE;
        }
 
-       ret = print_graph_irq(s, trace->func, TRACE_GRAPH_RET, cpu, ent->pid);
+       ret = print_graph_irq(iter, trace->func, TRACE_GRAPH_RET, cpu, pid);
        if (ret == TRACE_TYPE_PARTIAL_LINE)
                return TRACE_TYPE_PARTIAL_LINE;
 
@@ -598,6 +692,10 @@ print_graph_comment(struct print_entry *trace, struct trace_seq *s,
        int cpu = iter->cpu;
        pid_t *last_pid = iter->private;
 
+       /* Pid */
+       if (verif_pid(s, ent->pid, cpu, last_pid) == TRACE_TYPE_PARTIAL_LINE)
+               return TRACE_TYPE_PARTIAL_LINE;
+
        /* Absolute time */
        if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) {
                ret = print_graph_abs_time(iter->ts, s);
@@ -605,10 +703,6 @@ print_graph_comment(struct print_entry *trace, struct trace_seq *s,
                        return TRACE_TYPE_PARTIAL_LINE;
        }
 
-       /* Pid */
-       if (verif_pid(s, ent->pid, cpu, last_pid) == TRACE_TYPE_PARTIAL_LINE)
-               return TRACE_TYPE_PARTIAL_LINE;
-
        /* Cpu */
        if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
                ret = print_graph_cpu(s, cpu);
@@ -648,7 +742,11 @@ print_graph_comment(struct print_entry *trace, struct trace_seq *s,
                }
 
        /* The comment */
-       ret = trace_seq_printf(s, "/* %s", trace->buf);
+       ret = trace_seq_printf(s, "/* ");
+       if (!ret)
+               return TRACE_TYPE_PARTIAL_LINE;
+
+       ret = trace_seq_bprintf(s, trace->fmt, trace->buf);
        if (!ret)
                return TRACE_TYPE_PARTIAL_LINE;
 
@@ -739,18 +837,22 @@ static void graph_trace_open(struct trace_iterator *iter)
 
 static void graph_trace_close(struct trace_iterator *iter)
 {
-       percpu_free(iter->private);
+       free_percpu(iter->private);
 }
 
 static struct tracer graph_trace __read_mostly = {
        .name           = "function_graph",
        .open           = graph_trace_open,
        .close          = graph_trace_close,
+       .wait_pipe      = poll_wait_pipe,
        .init           = graph_trace_init,
        .reset          = graph_trace_reset,
        .print_line     = print_graph_function,
        .print_header   = print_graph_headers,
        .flags          = &tracer_flags,
+#ifdef CONFIG_FTRACE_SELFTEST
+       .selftest       = trace_selftest_startup_function_graph,
+#endif
 };
 
 static __init int init_graph_trace(void)