]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - kernel/trace/trace.c
tracing: likely/unlikely branch annotation tracer
[mv-sheeva.git] / kernel / trace / trace.c
index 29ab40a764c8ce99373e068fed664f4e0f144505..83d38634bc90b40643aa3dcd5a1a18aa713e377c 100644 (file)
 unsigned long __read_mostly    tracing_max_latency = (cycle_t)ULONG_MAX;
 unsigned long __read_mostly    tracing_thresh;
 
+
+/*
+ * Kill all tracing for good (never come back).
+ * It is initialized to 1 but will turn to zero if the initialization
+ * of the tracer is successful. But that is the only place that sets
+ * this back to zero.
+ */
+int tracing_disabled = 1;
+
 static DEFINE_PER_CPU(local_t, ftrace_cpu_disabled);
 
 static inline void ftrace_disable_cpu(void)
@@ -62,8 +71,6 @@ static cpumask_t __read_mostly                tracing_buffer_mask;
 #define for_each_tracing_cpu(cpu)      \
        for_each_cpu_mask(cpu, tracing_buffer_mask)
 
-static int tracing_disabled = 1;
-
 /*
  * ftrace_dump_on_oops - variable to dump ftrace buffer on oops
  *
@@ -143,6 +150,19 @@ static DEFINE_PER_CPU(struct trace_array_cpu, max_data);
 /* tracer_enabled is used to toggle activation of a tracer */
 static int                     tracer_enabled = 1;
 
+/**
+ * tracing_is_enabled - return tracer_enabled status
+ *
+ * This function is used by other tracers to know the status
+ * of the tracer_enabled flag.  Tracers may use this function
+ * to know if it should enable their features when starting
+ * up. See irqsoff tracer for an example (start_irqsoff_tracer).
+ */
+int tracing_is_enabled(void)
+{
+       return tracer_enabled;
+}
+
 /* function tracing enabled */
 int                            ftrace_function_enabled;
 
@@ -185,7 +205,7 @@ static DEFINE_MUTEX(trace_types_lock);
 static DECLARE_WAIT_QUEUE_HEAD(trace_wait);
 
 /* trace_flags holds iter_ctrl options */
-unsigned long trace_flags = TRACE_ITER_PRINT_PARENT;
+unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK;
 
 /**
  * trace_wake_up - wake up tasks waiting for trace input
@@ -224,13 +244,6 @@ unsigned long nsecs_to_usecs(unsigned long nsecs)
        return nsecs / 1000;
 }
 
-/*
- * TRACE_ITER_SYM_MASK masks the options in trace_flags that
- * control the output of kernel symbols.
- */
-#define TRACE_ITER_SYM_MASK \
-       (TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
-
 /* These must match the bit postions in trace_iterator_flags */
 static const char *trace_options[] = {
        "print-parent",
@@ -245,6 +258,9 @@ static const char *trace_options[] = {
        "sched-tree",
        "ftrace_printk",
        "ftrace_preempt",
+#ifdef CONFIG_UNLIKELY_TRACER
+       "unlikely",
+#endif
        NULL
 };
 
@@ -517,7 +533,6 @@ int register_tracer(struct tracer *type)
        if (type->selftest) {
                struct tracer *saved_tracer = current_trace;
                struct trace_array *tr = &global_trace;
-               int saved_ctrl = tr->ctrl;
                int i;
                /*
                 * Run a selftest on this tracer.
@@ -530,13 +545,11 @@ int register_tracer(struct tracer *type)
                        tracing_reset(tr, i);
                }
                current_trace = type;
-               tr->ctrl = 0;
                /* the test is responsible for initializing and enabling */
                pr_info("Testing tracer %s: ", type->name);
                ret = type->selftest(type, tr);
                /* the test is responsible for resetting too */
                current_trace = saved_tracer;
-               tr->ctrl = saved_ctrl;
                if (ret) {
                        printk(KERN_CONT "FAILED!\n");
                        goto out;
@@ -613,6 +626,76 @@ static void trace_init_cmdlines(void)
        cmdline_idx = 0;
 }
 
+static int trace_stop_count;
+static DEFINE_SPINLOCK(tracing_start_lock);
+
+/**
+ * tracing_start - quick start of the tracer
+ *
+ * If tracing is enabled but was stopped by tracing_stop,
+ * this will start the tracer back up.
+ */
+void tracing_start(void)
+{
+       struct ring_buffer *buffer;
+       unsigned long flags;
+
+       if (tracing_disabled)
+               return;
+
+       spin_lock_irqsave(&tracing_start_lock, flags);
+       if (--trace_stop_count)
+               goto out;
+
+       if (trace_stop_count < 0) {
+               /* Someone screwed up their debugging */
+               WARN_ON_ONCE(1);
+               trace_stop_count = 0;
+               goto out;
+       }
+
+
+       buffer = global_trace.buffer;
+       if (buffer)
+               ring_buffer_record_enable(buffer);
+
+       buffer = max_tr.buffer;
+       if (buffer)
+               ring_buffer_record_enable(buffer);
+
+       ftrace_start();
+ out:
+       spin_unlock_irqrestore(&tracing_start_lock, flags);
+}
+
+/**
+ * tracing_stop - quick stop of the tracer
+ *
+ * Light weight way to stop tracing. Use in conjunction with
+ * tracing_start.
+ */
+void tracing_stop(void)
+{
+       struct ring_buffer *buffer;
+       unsigned long flags;
+
+       ftrace_stop();
+       spin_lock_irqsave(&tracing_start_lock, flags);
+       if (trace_stop_count++)
+               goto out;
+
+       buffer = global_trace.buffer;
+       if (buffer)
+               ring_buffer_record_disable(buffer);
+
+       buffer = max_tr.buffer;
+       if (buffer)
+               ring_buffer_record_disable(buffer);
+
+ out:
+       spin_unlock_irqrestore(&tracing_start_lock, flags);
+}
+
 void trace_stop_cmdline_recording(void);
 
 static void trace_save_cmdline(struct task_struct *tsk)
@@ -723,6 +806,35 @@ trace_function(struct trace_array *tr, struct trace_array_cpu *data,
        ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
 }
 
+#ifdef CONFIG_FUNCTION_RET_TRACER
+static void __trace_function_return(struct trace_array *tr,
+                               struct trace_array_cpu *data,
+                               struct ftrace_retfunc *trace,
+                               unsigned long flags,
+                               int pc)
+{
+       struct ring_buffer_event *event;
+       struct ftrace_ret_entry *entry;
+       unsigned long irq_flags;
+
+       if (unlikely(local_read(&__get_cpu_var(ftrace_cpu_disabled))))
+               return;
+
+       event = ring_buffer_lock_reserve(global_trace.buffer, sizeof(*entry),
+                                        &irq_flags);
+       if (!event)
+               return;
+       entry   = ring_buffer_event_data(event);
+       tracing_generic_entry_update(&entry->ent, flags, pc);
+       entry->ent.type                 = TRACE_FN_RET;
+       entry->ip                       = trace->func;
+       entry->parent_ip        = trace->ret;
+       entry->rettime          = trace->rettime;
+       entry->calltime         = trace->calltime;
+       ring_buffer_unlock_commit(global_trace.buffer, event, irq_flags);
+}
+#endif
+
 void
 ftrace(struct trace_array *tr, struct trace_array_cpu *data,
        unsigned long ip, unsigned long parent_ip, unsigned long flags,
@@ -873,21 +985,23 @@ ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3)
 {
        struct trace_array *tr = &global_trace;
        struct trace_array_cpu *data;
+       unsigned long flags;
        int cpu;
        int pc;
 
-       if (tracing_disabled || !tr->ctrl)
+       if (tracing_disabled)
                return;
 
        pc = preempt_count();
-       preempt_disable_notrace();
+       local_irq_save(flags);
        cpu = raw_smp_processor_id();
        data = tr->data[cpu];
 
-       if (likely(!atomic_read(&data->disabled)))
+       if (likely(atomic_inc_return(&data->disabled) == 1))
                ftrace_trace_special(tr, data, arg1, arg2, arg3, pc);
 
-       preempt_enable_notrace();
+       atomic_dec(&data->disabled);
+       local_irq_restore(flags);
 }
 
 #ifdef CONFIG_FUNCTION_TRACER
@@ -949,6 +1063,29 @@ function_trace_call(unsigned long ip, unsigned long parent_ip)
        raw_local_irq_restore(flags);
 }
 
+#ifdef CONFIG_FUNCTION_RET_TRACER
+void trace_function_return(struct ftrace_retfunc *trace)
+{
+       struct trace_array *tr = &global_trace;
+       struct trace_array_cpu *data;
+       unsigned long flags;
+       long disabled;
+       int cpu;
+       int pc;
+
+       raw_local_irq_save(flags);
+       cpu = raw_smp_processor_id();
+       data = tr->data[cpu];
+       disabled = atomic_inc_return(&data->disabled);
+       if (likely(disabled == 1)) {
+               pc = preempt_count();
+               __trace_function_return(tr, data, trace, flags, pc);
+       }
+       atomic_dec(&data->disabled);
+       raw_local_irq_restore(flags);
+}
+#endif /* CONFIG_FUNCTION_RET_TRACER */
+
 static struct ftrace_ops trace_ops __read_mostly =
 {
        .func = function_trace_call,
@@ -964,8 +1101,7 @@ void tracing_start_function_trace(void)
                trace_ops.func = function_trace_call;
 
        register_ftrace_function(&trace_ops);
-       if (tracer_enabled)
-               ftrace_function_enabled = 1;
+       ftrace_function_enabled = 1;
 }
 
 void tracing_stop_function_trace(void)
@@ -1112,10 +1248,6 @@ static void *s_start(struct seq_file *m, loff_t *pos)
 
        atomic_inc(&trace_record_cmdline_disabled);
 
-       /* let the tracer grab locks here if needed */
-       if (current_trace->start)
-               current_trace->start(iter);
-
        if (*pos != iter->pos) {
                iter->ent = NULL;
                iter->cpu = 0;
@@ -1142,28 +1274,24 @@ static void *s_start(struct seq_file *m, loff_t *pos)
 
 static void s_stop(struct seq_file *m, void *p)
 {
-       struct trace_iterator *iter = m->private;
-
        atomic_dec(&trace_record_cmdline_disabled);
-
-       /* let the tracer release locks here if needed */
-       if (current_trace && current_trace == iter->trace && iter->trace->stop)
-               iter->trace->stop(iter);
-
        mutex_unlock(&trace_types_lock);
 }
 
-#define KRETPROBE_MSG "[unknown/kretprobe'd]"
-
 #ifdef CONFIG_KRETPROBES
-static inline int kretprobed(unsigned long addr)
+static inline const char *kretprobed(const char *name)
 {
-       return addr == (unsigned long)kretprobe_trampoline;
+       static const char tramp_name[] = "kretprobe_trampoline";
+       int size = sizeof(tramp_name);
+
+       if (strncmp(tramp_name, name, size) == 0)
+               return "[unknown/kretprobe'd]";
+       return name;
 }
 #else
-static inline int kretprobed(unsigned long addr)
+static inline const char *kretprobed(const char *name)
 {
-       return 0;
+       return name;
 }
 #endif /* CONFIG_KRETPROBES */
 
@@ -1172,10 +1300,13 @@ seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
 {
 #ifdef CONFIG_KALLSYMS
        char str[KSYM_SYMBOL_LEN];
+       const char *name;
 
        kallsyms_lookup(address, NULL, NULL, NULL, str);
 
-       return trace_seq_printf(s, fmt, str);
+       name = kretprobed(str);
+
+       return trace_seq_printf(s, fmt, name);
 #endif
        return 1;
 }
@@ -1186,9 +1317,12 @@ seq_print_sym_offset(struct trace_seq *s, const char *fmt,
 {
 #ifdef CONFIG_KALLSYMS
        char str[KSYM_SYMBOL_LEN];
+       const char *name;
 
        sprint_symbol(str, address);
-       return trace_seq_printf(s, fmt, str);
+       name = kretprobed(str);
+
+       return trace_seq_printf(s, fmt, name);
 #endif
        return 1;
 }
@@ -1199,7 +1333,7 @@ seq_print_sym_offset(struct trace_seq *s, const char *fmt,
 # define IP_FMT "%016lx"
 #endif
 
-static int
+int
 seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
 {
        int ret;
@@ -1394,6 +1528,17 @@ void trace_seq_print_cont(struct trace_seq *s, struct trace_iterator *iter)
                trace_seq_putc(s, '\n');
 }
 
+static void test_cpu_buff_start(struct trace_iterator *iter)
+{
+       struct trace_seq *s = &iter->seq;
+
+       if (cpu_isset(iter->cpu, iter->started))
+               return;
+
+       cpu_set(iter->cpu, iter->started);
+       trace_seq_printf(s, "##### CPU %u buffer started ####\n", iter->cpu);
+}
+
 static enum print_line_t
 print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
 {
@@ -1413,6 +1558,8 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
        if (entry->type == TRACE_CONT)
                return TRACE_TYPE_HANDLED;
 
+       test_cpu_buff_start(iter);
+
        next_entry = find_next_entry(iter, NULL, &next_ts);
        if (!next_entry)
                next_ts = iter->ts;
@@ -1442,10 +1589,7 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
 
                seq_print_ip_sym(s, field->ip, sym_flags);
                trace_seq_puts(s, " (");
-               if (kretprobed(field->parent_ip))
-                       trace_seq_puts(s, KRETPROBE_MSG);
-               else
-                       seq_print_ip_sym(s, field->parent_ip, sym_flags);
+               seq_print_ip_sym(s, field->parent_ip, sym_flags);
                trace_seq_puts(s, ")\n");
                break;
        }
@@ -1507,6 +1651,18 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
                        trace_seq_print_cont(s, iter);
                break;
        }
+       case TRACE_UNLIKELY: {
+               struct trace_unlikely *field;
+
+               trace_assign_type(field, entry);
+
+               trace_seq_printf(s, "[%s] %s:%s:%d\n",
+                                field->correct ? "correct" : "INCORRECT",
+                                field->func,
+                                field->file,
+                                field->line);
+               break;
+       }
        default:
                trace_seq_printf(s, "Unknown type %d\n", entry->type);
        }
@@ -1531,6 +1687,8 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
        if (entry->type == TRACE_CONT)
                return TRACE_TYPE_HANDLED;
 
+       test_cpu_buff_start(iter);
+
        comm = trace_find_cmdline(iter->ent->pid);
 
        t = ns2usecs(iter->ts);
@@ -1561,12 +1719,9 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
                        ret = trace_seq_printf(s, " <-");
                        if (!ret)
                                return TRACE_TYPE_PARTIAL_LINE;
-                       if (kretprobed(field->parent_ip))
-                               ret = trace_seq_puts(s, KRETPROBE_MSG);
-                       else
-                               ret = seq_print_ip_sym(s,
-                                                      field->parent_ip,
-                                                      sym_flags);
+                       ret = seq_print_ip_sym(s,
+                                              field->parent_ip,
+                                              sym_flags);
                        if (!ret)
                                return TRACE_TYPE_PARTIAL_LINE;
                }
@@ -1643,6 +1798,22 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter)
                        trace_seq_print_cont(s, iter);
                break;
        }
+       case TRACE_FN_RET: {
+               return print_return_function(iter);
+               break;
+       }
+       case TRACE_UNLIKELY: {
+               struct trace_unlikely *field;
+
+               trace_assign_type(field, entry);
+
+               trace_seq_printf(s, "[%s] %s:%s:%d\n",
+                                field->correct ? "correct" : "INCORRECT",
+                                field->func,
+                                field->file,
+                                field->line);
+               break;
+       }
        }
        return TRACE_TYPE_HANDLED;
 }
@@ -1817,7 +1988,7 @@ static enum print_line_t print_bin_fmt(struct trace_iterator *iter)
                return TRACE_TYPE_HANDLED;
 
        SEQ_PUT_FIELD_RET(s, entry->pid);
-       SEQ_PUT_FIELD_RET(s, iter->cpu);
+       SEQ_PUT_FIELD_RET(s, entry->cpu);
        SEQ_PUT_FIELD_RET(s, iter->ts);
 
        switch (entry->type) {
@@ -1979,10 +2150,7 @@ __tracing_open(struct inode *inode, struct file *file, int *ret)
        m->private = iter;
 
        /* stop the trace while dumping */
-       if (iter->tr->ctrl) {
-               tracer_enabled = 0;
-               ftrace_function_enabled = 0;
-       }
+       tracing_stop();
 
        if (iter->trace && iter->trace->open)
                        iter->trace->open(iter);
@@ -2027,14 +2195,7 @@ int tracing_release(struct inode *inode, struct file *file)
                iter->trace->close(iter);
 
        /* reenable tracing if it was previously enabled */
-       if (iter->tr->ctrl) {
-               tracer_enabled = 1;
-               /*
-                * It is safe to enable function tracing even if it
-                * isn't used
-                */
-               ftrace_function_enabled = 1;
-       }
+       tracing_start();
        mutex_unlock(&trace_types_lock);
 
        seq_release(inode, file);
@@ -2372,11 +2533,10 @@ static ssize_t
 tracing_ctrl_read(struct file *filp, char __user *ubuf,
                  size_t cnt, loff_t *ppos)
 {
-       struct trace_array *tr = filp->private_data;
        char buf[64];
        int r;
 
-       r = sprintf(buf, "%ld\n", tr->ctrl);
+       r = sprintf(buf, "%u\n", tracer_enabled);
        return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
 }
 
@@ -2404,16 +2564,18 @@ tracing_ctrl_write(struct file *filp, const char __user *ubuf,
        val = !!val;
 
        mutex_lock(&trace_types_lock);
-       if (tr->ctrl ^ val) {
-               if (val)
+       if (tracer_enabled ^ val) {
+               if (val) {
                        tracer_enabled = 1;
-               else
+                       if (current_trace->start)
+                               current_trace->start(tr);
+                       tracing_start();
+               } else {
                        tracer_enabled = 0;
-
-               tr->ctrl = val;
-
-               if (current_trace && current_trace->ctrl_update)
-                       current_trace->ctrl_update(tr);
+                       tracing_stop();
+                       if (current_trace->stop)
+                               current_trace->stop(tr);
+               }
        }
        mutex_unlock(&trace_types_lock);
 
@@ -2457,6 +2619,7 @@ static int tracing_set_tracer(char *buf)
        if (t == current_trace)
                goto out;
 
+       trace_unlikely_disable();
        if (current_trace && current_trace->reset)
                current_trace->reset(tr);
 
@@ -2464,6 +2627,7 @@ static int tracing_set_tracer(char *buf)
        if (t->init)
                t->init(tr);
 
+       trace_unlikely_enable(tr);
  out:
        mutex_unlock(&trace_types_lock);
 
@@ -2562,6 +2726,10 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
                return -ENOMEM;
 
        mutex_lock(&trace_types_lock);
+
+       /* trace pipe does not show start of buffer */
+       cpus_setall(iter->started);
+
        iter->tr = &global_trace;
        iter->trace = current_trace;
        filp->private_data = iter;
@@ -2747,8 +2915,7 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
 {
        unsigned long val;
        char buf[64];
-       int ret;
-       struct trace_array *tr = filp->private_data;
+       int ret, cpu;
 
        if (cnt >= sizeof(buf))
                return -EINVAL;
@@ -2768,11 +2935,14 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
 
        mutex_lock(&trace_types_lock);
 
-       if (tr->ctrl) {
-               cnt = -EBUSY;
-               pr_info("ftrace: please disable tracing"
-                       " before modifying buffer size\n");
-               goto out;
+       tracing_stop();
+
+       /* disable all cpu buffers */
+       for_each_tracing_cpu(cpu) {
+               if (global_trace.data[cpu])
+                       atomic_inc(&global_trace.data[cpu]->disabled);
+               if (max_tr.data[cpu])
+                       atomic_inc(&max_tr.data[cpu]->disabled);
        }
 
        if (val != global_trace.entries) {
@@ -2806,6 +2976,14 @@ tracing_entries_write(struct file *filp, const char __user *ubuf,
        if (tracing_disabled)
                cnt = -ENOMEM;
  out:
+       for_each_tracing_cpu(cpu) {
+               if (global_trace.data[cpu])
+                       atomic_dec(&global_trace.data[cpu]->disabled);
+               if (max_tr.data[cpu])
+                       atomic_dec(&max_tr.data[cpu]->disabled);
+       }
+
+       tracing_start();
        max_tr.entries = global_trace.entries;
        mutex_unlock(&trace_types_lock);
 
@@ -2828,9 +3006,8 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
 {
        char *buf;
        char *end;
-       struct trace_array *tr = &global_trace;
 
-       if (!tr->ctrl || tracing_disabled)
+       if (tracing_disabled)
                return -EINVAL;
 
        if (cnt > TRACE_BUF_SIZE)
@@ -3059,7 +3236,7 @@ int trace_vprintk(unsigned long ip, const char *fmt, va_list args)
        unsigned long flags, irq_flags;
        int cpu, len = 0, size, pc;
 
-       if (!tr->ctrl || tracing_disabled)
+       if (tracing_disabled)
                return 0;
 
        pc = preempt_count();
@@ -3285,8 +3462,6 @@ __init static int tracer_alloc_buffers(void)
 
        register_tracer(&nop_trace);
 #ifdef CONFIG_BOOT_TRACER
-       /* We don't want to launch sched_switch tracer yet */
-       global_trace.ctrl = 0;
        register_tracer(&boot_tracer);
        current_trace = &boot_tracer;
        current_trace->init(&global_trace);
@@ -3295,7 +3470,6 @@ __init static int tracer_alloc_buffers(void)
 #endif
 
        /* All seems OK, enable tracing */
-       global_trace.ctrl = tracer_enabled;
        tracing_disabled = 0;
 
        atomic_notifier_chain_register(&panic_notifier_list,