linux/kernel/trace/trace_branch.c
<<
>>
Prefs
   1/*
   2 * unlikely profiler
   3 *
   4 * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
   5 */
   6#include <linux/kallsyms.h>
   7#include <linux/seq_file.h>
   8#include <linux/spinlock.h>
   9#include <linux/irqflags.h>
  10#include <linux/debugfs.h>
  11#include <linux/uaccess.h>
  12#include <linux/module.h>
  13#include <linux/ftrace.h>
  14#include <linux/hash.h>
  15#include <linux/fs.h>
  16#include <asm/local.h>
  17
  18#include "trace.h"
  19#include "trace_stat.h"
  20#include "trace_output.h"
  21
  22#ifdef CONFIG_BRANCH_TRACER
  23
  24static struct tracer branch_trace;
  25static int branch_tracing_enabled __read_mostly;
  26static DEFINE_MUTEX(branch_tracing_mutex);
  27
  28static struct trace_array *branch_tracer;
  29
  30static void
  31probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
  32{
  33        struct ftrace_event_call *call = &event_branch;
  34        struct trace_array *tr = branch_tracer;
  35        struct trace_array_cpu *data;
  36        struct ring_buffer_event *event;
  37        struct trace_branch *entry;
  38        struct ring_buffer *buffer;
  39        unsigned long flags;
  40        int cpu, pc;
  41        const char *p;
  42
  43        /*
  44         * I would love to save just the ftrace_likely_data pointer, but
  45         * this code can also be used by modules. Ugly things can happen
  46         * if the module is unloaded, and then we go and read the
  47         * pointer.  This is slower, but much safer.
  48         */
  49
  50        if (unlikely(!tr))
  51                return;
  52
  53        local_irq_save(flags);
  54        cpu = raw_smp_processor_id();
  55        data = per_cpu_ptr(tr->trace_buffer.data, cpu);
  56        if (atomic_inc_return(&data->disabled) != 1)
  57                goto out;
  58
  59        pc = preempt_count();
  60        buffer = tr->trace_buffer.buffer;
  61        event = trace_buffer_lock_reserve(buffer, TRACE_BRANCH,
  62                                          sizeof(*entry), flags, pc);
  63        if (!event)
  64                goto out;
  65
  66        entry   = ring_buffer_event_data(event);
  67
  68        /* Strip off the path, only save the file */
  69        p = f->file + strlen(f->file);
  70        while (p >= f->file && *p != '/')
  71                p--;
  72        p++;
  73
  74        strncpy(entry->func, f->func, TRACE_FUNC_SIZE);
  75        strncpy(entry->file, p, TRACE_FILE_SIZE);
  76        entry->func[TRACE_FUNC_SIZE] = 0;
  77        entry->file[TRACE_FILE_SIZE] = 0;
  78        entry->line = f->line;
  79        entry->correct = val == expect;
  80
  81        if (!filter_check_discard(call, entry, buffer, event))
  82                __buffer_unlock_commit(buffer, event);
  83
  84 out:
  85        atomic_dec(&data->disabled);
  86        local_irq_restore(flags);
  87}
  88
  89static inline
  90void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
  91{
  92        if (!branch_tracing_enabled)
  93                return;
  94
  95        probe_likely_condition(f, val, expect);
  96}
  97
  98int enable_branch_tracing(struct trace_array *tr)
  99{
 100        mutex_lock(&branch_tracing_mutex);
 101        branch_tracer = tr;
 102        /*
 103         * Must be seen before enabling. The reader is a condition
 104         * where we do not need a matching rmb()
 105         */
 106        smp_wmb();
 107        branch_tracing_enabled++;
 108        mutex_unlock(&branch_tracing_mutex);
 109
 110        return 0;
 111}
 112
 113void disable_branch_tracing(void)
 114{
 115        mutex_lock(&branch_tracing_mutex);
 116
 117        if (!branch_tracing_enabled)
 118                goto out_unlock;
 119
 120        branch_tracing_enabled--;
 121
 122 out_unlock:
 123        mutex_unlock(&branch_tracing_mutex);
 124}
 125
 126static void start_branch_trace(struct trace_array *tr)
 127{
 128        enable_branch_tracing(tr);
 129}
 130
 131static void stop_branch_trace(struct trace_array *tr)
 132{
 133        disable_branch_tracing();
 134}
 135
 136static int branch_trace_init(struct trace_array *tr)
 137{
 138        start_branch_trace(tr);
 139        return 0;
 140}
 141
 142static void branch_trace_reset(struct trace_array *tr)
 143{
 144        stop_branch_trace(tr);
 145}
 146
 147static enum print_line_t trace_branch_print(struct trace_iterator *iter,
 148                                            int flags, struct trace_event *event)
 149{
 150        struct trace_branch *field;
 151
 152        trace_assign_type(field, iter->ent);
 153
 154        if (trace_seq_printf(&iter->seq, "[%s] %s:%s:%d\n",
 155                             field->correct ? "  ok  " : " MISS ",
 156                             field->func,
 157                             field->file,
 158                             field->line))
 159                return TRACE_TYPE_PARTIAL_LINE;
 160
 161        return TRACE_TYPE_HANDLED;
 162}
 163
 164static void branch_print_header(struct seq_file *s)
 165{
 166        seq_puts(s, "#           TASK-PID    CPU#    TIMESTAMP  CORRECT"
 167                "  FUNC:FILE:LINE\n");
 168        seq_puts(s, "#              | |       |          |         |   "
 169                "    |\n");
 170}
 171
 172static struct trace_event_functions trace_branch_funcs = {
 173        .trace          = trace_branch_print,
 174};
 175
 176static struct trace_event trace_branch_event = {
 177        .type           = TRACE_BRANCH,
 178        .funcs          = &trace_branch_funcs,
 179};
 180
 181static struct tracer branch_trace __read_mostly =
 182{
 183        .name           = "branch",
 184        .init           = branch_trace_init,
 185        .reset          = branch_trace_reset,
 186#ifdef CONFIG_FTRACE_SELFTEST
 187        .selftest       = trace_selftest_startup_branch,
 188#endif /* CONFIG_FTRACE_SELFTEST */
 189        .print_header   = branch_print_header,
 190};
 191
 192__init static int init_branch_tracer(void)
 193{
 194        int ret;
 195
 196        ret = register_ftrace_event(&trace_branch_event);
 197        if (!ret) {
 198                printk(KERN_WARNING "Warning: could not register "
 199                                    "branch events\n");
 200                return 1;
 201        }
 202        return register_tracer(&branch_trace);
 203}
 204core_initcall(init_branch_tracer);
 205
 206#else
 207static inline
 208void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
 209{
 210}
 211#endif /* CONFIG_BRANCH_TRACER */
 212
 213void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
 214{
 215        /*
 216         * I would love to have a trace point here instead, but the
 217         * trace point code is so inundated with unlikely and likely
 218         * conditions that the recursive nightmare that exists is too
 219         * much to try to get working. At least for now.
 220         */
 221        trace_likely_condition(f, val, expect);
 222
 223        /* FIXME: Make this atomic! */
 224        if (val == expect)
 225                f->correct++;
 226        else
 227                f->incorrect++;
 228}
 229EXPORT_SYMBOL(ftrace_likely_update);
 230
 231extern unsigned long __start_annotated_branch_profile[];
 232extern unsigned long __stop_annotated_branch_profile[];
 233
 234static int annotated_branch_stat_headers(struct seq_file *m)
 235{
 236        seq_printf(m, " correct incorrect  %% ");
 237        seq_printf(m, "       Function                "
 238                              "  File              Line\n"
 239                              " ------- ---------  - "
 240                              "       --------                "
 241                              "  ----              ----\n");
 242        return 0;
 243}
 244
 245static inline long get_incorrect_percent(struct ftrace_branch_data *p)
 246{
 247        long percent;
 248
 249        if (p->correct) {
 250                percent = p->incorrect * 100;
 251                percent /= p->correct + p->incorrect;
 252        } else
 253                percent = p->incorrect ? 100 : -1;
 254
 255        return percent;
 256}
 257
 258static int branch_stat_show(struct seq_file *m, void *v)
 259{
 260        struct ftrace_branch_data *p = v;
 261        const char *f;
 262        long percent;
 263
 264        /* Only print the file, not the path */
 265        f = p->file + strlen(p->file);
 266        while (f >= p->file && *f != '/')
 267                f--;
 268        f++;
 269
 270        /*
 271         * The miss is overlayed on correct, and hit on incorrect.
 272         */
 273        percent = get_incorrect_percent(p);
 274
 275        seq_printf(m, "%8lu %8lu ",  p->correct, p->incorrect);
 276        if (percent < 0)
 277                seq_printf(m, "  X ");
 278        else
 279                seq_printf(m, "%3ld ", percent);
 280        seq_printf(m, "%-30.30s %-20.20s %d\n", p->func, f, p->line);
 281        return 0;
 282}
 283
 284static void *annotated_branch_stat_start(struct tracer_stat *trace)
 285{
 286        return __start_annotated_branch_profile;
 287}
 288
 289static void *
 290annotated_branch_stat_next(void *v, int idx)
 291{
 292        struct ftrace_branch_data *p = v;
 293
 294        ++p;
 295
 296        if ((void *)p >= (void *)__stop_annotated_branch_profile)
 297                return NULL;
 298
 299        return p;
 300}
 301
 302static int annotated_branch_stat_cmp(void *p1, void *p2)
 303{
 304        struct ftrace_branch_data *a = p1;
 305        struct ftrace_branch_data *b = p2;
 306
 307        long percent_a, percent_b;
 308
 309        percent_a = get_incorrect_percent(a);
 310        percent_b = get_incorrect_percent(b);
 311
 312        if (percent_a < percent_b)
 313                return -1;
 314        if (percent_a > percent_b)
 315                return 1;
 316
 317        if (a->incorrect < b->incorrect)
 318                return -1;
 319        if (a->incorrect > b->incorrect)
 320                return 1;
 321
 322        /*
 323         * Since the above shows worse (incorrect) cases
 324         * first, we continue that by showing best (correct)
 325         * cases last.
 326         */
 327        if (a->correct > b->correct)
 328                return -1;
 329        if (a->correct < b->correct)
 330                return 1;
 331
 332        return 0;
 333}
 334
 335static struct tracer_stat annotated_branch_stats = {
 336        .name = "branch_annotated",
 337        .stat_start = annotated_branch_stat_start,
 338        .stat_next = annotated_branch_stat_next,
 339        .stat_cmp = annotated_branch_stat_cmp,
 340        .stat_headers = annotated_branch_stat_headers,
 341        .stat_show = branch_stat_show
 342};
 343
 344__init static int init_annotated_branch_stats(void)
 345{
 346        int ret;
 347
 348        ret = register_stat_tracer(&annotated_branch_stats);
 349        if (!ret) {
 350                printk(KERN_WARNING "Warning: could not register "
 351                                    "annotated branches stats\n");
 352                return 1;
 353        }
 354        return 0;
 355}
 356fs_initcall(init_annotated_branch_stats);
 357
 358#ifdef CONFIG_PROFILE_ALL_BRANCHES
 359
 360extern unsigned long __start_branch_profile[];
 361extern unsigned long __stop_branch_profile[];
 362
 363static int all_branch_stat_headers(struct seq_file *m)
 364{
 365        seq_printf(m, "   miss      hit    %% ");
 366        seq_printf(m, "       Function                "
 367                              "  File              Line\n"
 368                              " ------- ---------  - "
 369                              "       --------                "
 370                              "  ----              ----\n");
 371        return 0;
 372}
 373
 374static void *all_branch_stat_start(struct tracer_stat *trace)
 375{
 376        return __start_branch_profile;
 377}
 378
 379static void *
 380all_branch_stat_next(void *v, int idx)
 381{
 382        struct ftrace_branch_data *p = v;
 383
 384        ++p;
 385
 386        if ((void *)p >= (void *)__stop_branch_profile)
 387                return NULL;
 388
 389        return p;
 390}
 391
 392static struct tracer_stat all_branch_stats = {
 393        .name = "branch_all",
 394        .stat_start = all_branch_stat_start,
 395        .stat_next = all_branch_stat_next,
 396        .stat_headers = all_branch_stat_headers,
 397        .stat_show = branch_stat_show
 398};
 399
 400__init static int all_annotated_branch_stats(void)
 401{
 402        int ret;
 403
 404        ret = register_stat_tracer(&all_branch_stats);
 405        if (!ret) {
 406                printk(KERN_WARNING "Warning: could not register "
 407                                    "all branches stats\n");
 408                return 1;
 409        }
 410        return 0;
 411}
 412fs_initcall(all_annotated_branch_stats);
 413#endif /* CONFIG_PROFILE_ALL_BRANCHES */
 414