linux/tools/perf/builtin-annotate.c
<<
>>
Prefs
   1/*
   2 * builtin-annotate.c
   3 *
   4 * Builtin annotate command: Analyze the perf.data input file,
   5 * look up and read DSOs and symbol information and display
   6 * a histogram of results, along various sorting keys.
   7 */
   8#include "builtin.h"
   9
  10#include "util/util.h"
  11
  12#include "util/color.h"
  13#include <linux/list.h>
  14#include "util/cache.h"
  15#include <linux/rbtree.h>
  16#include "util/symbol.h"
  17
  18#include "perf.h"
  19#include "util/debug.h"
  20
  21#include "util/event.h"
  22#include "util/parse-options.h"
  23#include "util/parse-events.h"
  24#include "util/thread.h"
  25#include "util/sort.h"
  26#include "util/hist.h"
  27#include "util/session.h"
  28
  29static char             const *input_name = "perf.data";
  30
  31static bool             force, use_tui, use_stdio;
  32
  33static bool             full_paths;
  34
  35static bool             print_line;
  36
  37static const char *sym_hist_filter;
  38
  39static int hists__add_entry(struct hists *self, struct addr_location *al)
  40{
  41        struct hist_entry *he;
  42
  43        if (sym_hist_filter != NULL &&
  44            (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
  45                /* We're only interested in a symbol named sym_hist_filter */
  46                if (al->sym != NULL) {
  47                        rb_erase(&al->sym->rb_node,
  48                                 &al->map->dso->symbols[al->map->type]);
  49                        symbol__delete(al->sym);
  50                }
  51                return 0;
  52        }
  53
  54        he = __hists__add_entry(self, al, NULL, 1);
  55        if (he == NULL)
  56                return -ENOMEM;
  57
  58        return hist_entry__inc_addr_samples(he, al->addr);
  59}
  60
  61static int process_sample_event(event_t *event, struct perf_session *session)
  62{
  63        struct addr_location al;
  64        struct sample_data data;
  65
  66        if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) {
  67                pr_warning("problem processing %d event, skipping it.\n",
  68                           event->header.type);
  69                return -1;
  70        }
  71
  72        if (!al.filtered && hists__add_entry(&session->hists, &al)) {
  73                pr_warning("problem incrementing symbol count, "
  74                           "skipping event\n");
  75                return -1;
  76        }
  77
  78        return 0;
  79}
  80
  81static int objdump_line__print(struct objdump_line *self,
  82                               struct list_head *head,
  83                               struct hist_entry *he, u64 len)
  84{
  85        struct symbol *sym = he->ms.sym;
  86        static const char *prev_line;
  87        static const char *prev_color;
  88
  89        if (self->offset != -1) {
  90                const char *path = NULL;
  91                unsigned int hits = 0;
  92                double percent = 0.0;
  93                const char *color;
  94                struct sym_priv *priv = symbol__priv(sym);
  95                struct sym_ext *sym_ext = priv->ext;
  96                struct sym_hist *h = priv->hist;
  97                s64 offset = self->offset;
  98                struct objdump_line *next = objdump__get_next_ip_line(head, self);
  99
 100                while (offset < (s64)len &&
 101                       (next == NULL || offset < next->offset)) {
 102                        if (sym_ext) {
 103                                if (path == NULL)
 104                                        path = sym_ext[offset].path;
 105                                percent += sym_ext[offset].percent;
 106                        } else
 107                                hits += h->ip[offset];
 108
 109                        ++offset;
 110                }
 111
 112                if (sym_ext == NULL && h->sum)
 113                        percent = 100.0 * hits / h->sum;
 114
 115                color = get_percent_color(percent);
 116
 117                /*
 118                 * Also color the filename and line if needed, with
 119                 * the same color than the percentage. Don't print it
 120                 * twice for close colored ip with the same filename:line
 121                 */
 122                if (path) {
 123                        if (!prev_line || strcmp(prev_line, path)
 124                                       || color != prev_color) {
 125                                color_fprintf(stdout, color, " %s", path);
 126                                prev_line = path;
 127                                prev_color = color;
 128                        }
 129                }
 130
 131                color_fprintf(stdout, color, " %7.2f", percent);
 132                printf(" :      ");
 133                color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
 134        } else {
 135                if (!*self->line)
 136                        printf("         :\n");
 137                else
 138                        printf("         :      %s\n", self->line);
 139        }
 140
 141        return 0;
 142}
 143
 144static struct rb_root root_sym_ext;
 145
 146static void insert_source_line(struct sym_ext *sym_ext)
 147{
 148        struct sym_ext *iter;
 149        struct rb_node **p = &root_sym_ext.rb_node;
 150        struct rb_node *parent = NULL;
 151
 152        while (*p != NULL) {
 153                parent = *p;
 154                iter = rb_entry(parent, struct sym_ext, node);
 155
 156                if (sym_ext->percent > iter->percent)
 157                        p = &(*p)->rb_left;
 158                else
 159                        p = &(*p)->rb_right;
 160        }
 161
 162        rb_link_node(&sym_ext->node, parent, p);
 163        rb_insert_color(&sym_ext->node, &root_sym_ext);
 164}
 165
 166static void free_source_line(struct hist_entry *he, int len)
 167{
 168        struct sym_priv *priv = symbol__priv(he->ms.sym);
 169        struct sym_ext *sym_ext = priv->ext;
 170        int i;
 171
 172        if (!sym_ext)
 173                return;
 174
 175        for (i = 0; i < len; i++)
 176                free(sym_ext[i].path);
 177        free(sym_ext);
 178
 179        priv->ext = NULL;
 180        root_sym_ext = RB_ROOT;
 181}
 182
 183/* Get the filename:line for the colored entries */
 184static void
 185get_source_line(struct hist_entry *he, int len, const char *filename)
 186{
 187        struct symbol *sym = he->ms.sym;
 188        u64 start;
 189        int i;
 190        char cmd[PATH_MAX * 2];
 191        struct sym_ext *sym_ext;
 192        struct sym_priv *priv = symbol__priv(sym);
 193        struct sym_hist *h = priv->hist;
 194
 195        if (!h->sum)
 196                return;
 197
 198        sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
 199        if (!priv->ext)
 200                return;
 201
 202        start = he->ms.map->unmap_ip(he->ms.map, sym->start);
 203
 204        for (i = 0; i < len; i++) {
 205                char *path = NULL;
 206                size_t line_len;
 207                u64 offset;
 208                FILE *fp;
 209
 210                sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
 211                if (sym_ext[i].percent <= 0.5)
 212                        continue;
 213
 214                offset = start + i;
 215                sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
 216                fp = popen(cmd, "r");
 217                if (!fp)
 218                        continue;
 219
 220                if (getline(&path, &line_len, fp) < 0 || !line_len)
 221                        goto next;
 222
 223                sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
 224                if (!sym_ext[i].path)
 225                        goto next;
 226
 227                strcpy(sym_ext[i].path, path);
 228                insert_source_line(&sym_ext[i]);
 229
 230        next:
 231                pclose(fp);
 232        }
 233}
 234
 235static void print_summary(const char *filename)
 236{
 237        struct sym_ext *sym_ext;
 238        struct rb_node *node;
 239
 240        printf("\nSorted summary for file %s\n", filename);
 241        printf("----------------------------------------------\n\n");
 242
 243        if (RB_EMPTY_ROOT(&root_sym_ext)) {
 244                printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
 245                return;
 246        }
 247
 248        node = rb_first(&root_sym_ext);
 249        while (node) {
 250                double percent;
 251                const char *color;
 252                char *path;
 253
 254                sym_ext = rb_entry(node, struct sym_ext, node);
 255                percent = sym_ext->percent;
 256                color = get_percent_color(percent);
 257                path = sym_ext->path;
 258
 259                color_fprintf(stdout, color, " %7.2f %s", percent, path);
 260                node = rb_next(node);
 261        }
 262}
 263
 264static void hist_entry__print_hits(struct hist_entry *self)
 265{
 266        struct symbol *sym = self->ms.sym;
 267        struct sym_priv *priv = symbol__priv(sym);
 268        struct sym_hist *h = priv->hist;
 269        u64 len = sym->end - sym->start, offset;
 270
 271        for (offset = 0; offset < len; ++offset)
 272                if (h->ip[offset] != 0)
 273                        printf("%*Lx: %Lu\n", BITS_PER_LONG / 2,
 274                               sym->start + offset, h->ip[offset]);
 275        printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum);
 276}
 277
 278static int hist_entry__tty_annotate(struct hist_entry *he)
 279{
 280        struct map *map = he->ms.map;
 281        struct dso *dso = map->dso;
 282        struct symbol *sym = he->ms.sym;
 283        const char *filename = dso->long_name, *d_filename;
 284        u64 len;
 285        LIST_HEAD(head);
 286        struct objdump_line *pos, *n;
 287
 288        if (hist_entry__annotate(he, &head, 0) < 0)
 289                return -1;
 290
 291        if (full_paths)
 292                d_filename = filename;
 293        else
 294                d_filename = basename(filename);
 295
 296        len = sym->end - sym->start;
 297
 298        if (print_line) {
 299                get_source_line(he, len, filename);
 300                print_summary(filename);
 301        }
 302
 303        printf("\n\n------------------------------------------------\n");
 304        printf(" Percent |      Source code & Disassembly of %s\n", d_filename);
 305        printf("------------------------------------------------\n");
 306
 307        if (verbose)
 308                hist_entry__print_hits(he);
 309
 310        list_for_each_entry_safe(pos, n, &head, node) {
 311                objdump_line__print(pos, &head, he, len);
 312                list_del(&pos->node);
 313                objdump_line__free(pos);
 314        }
 315
 316        if (print_line)
 317                free_source_line(he, len);
 318
 319        return 0;
 320}
 321
 322static void hists__find_annotations(struct hists *self)
 323{
 324        struct rb_node *nd = rb_first(&self->entries), *next;
 325        int key = KEY_RIGHT;
 326
 327        while (nd) {
 328                struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
 329                struct sym_priv *priv;
 330
 331                if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
 332                        goto find_next;
 333
 334                priv = symbol__priv(he->ms.sym);
 335                if (priv->hist == NULL) {
 336find_next:
 337                        if (key == KEY_LEFT)
 338                                nd = rb_prev(nd);
 339                        else
 340                                nd = rb_next(nd);
 341                        continue;
 342                }
 343
 344                if (use_browser > 0) {
 345                        key = hist_entry__tui_annotate(he);
 346                        switch (key) {
 347                        case KEY_RIGHT:
 348                                next = rb_next(nd);
 349                                break;
 350                        case KEY_LEFT:
 351                                next = rb_prev(nd);
 352                                break;
 353                        default:
 354                                return;
 355                        }
 356
 357                        if (next != NULL)
 358                                nd = next;
 359                } else {
 360                        hist_entry__tty_annotate(he);
 361                        nd = rb_next(nd);
 362                        /*
 363                         * Since we have a hist_entry per IP for the same
 364                         * symbol, free he->ms.sym->hist to signal we already
 365                         * processed this symbol.
 366                         */
 367                        free(priv->hist);
 368                        priv->hist = NULL;
 369                }
 370        }
 371}
 372
 373static struct perf_event_ops event_ops = {
 374        .sample = process_sample_event,
 375        .mmap   = event__process_mmap,
 376        .comm   = event__process_comm,
 377        .fork   = event__process_task,
 378};
 379
 380static int __cmd_annotate(void)
 381{
 382        int ret;
 383        struct perf_session *session;
 384
 385        session = perf_session__new(input_name, O_RDONLY, force, false);
 386        if (session == NULL)
 387                return -ENOMEM;
 388
 389        ret = perf_session__process_events(session, &event_ops);
 390        if (ret)
 391                goto out_delete;
 392
 393        if (dump_trace) {
 394                perf_session__fprintf_nr_events(session, stdout);
 395                goto out_delete;
 396        }
 397
 398        if (verbose > 3)
 399                perf_session__fprintf(session, stdout);
 400
 401        if (verbose > 2)
 402                perf_session__fprintf_dsos(session, stdout);
 403
 404        hists__collapse_resort(&session->hists);
 405        hists__output_resort(&session->hists);
 406        hists__find_annotations(&session->hists);
 407out_delete:
 408        perf_session__delete(session);
 409
 410        return ret;
 411}
 412
 413static const char * const annotate_usage[] = {
 414        "perf annotate [<options>] <command>",
 415        NULL
 416};
 417
 418static const struct option options[] = {
 419        OPT_STRING('i', "input", &input_name, "file",
 420                    "input file name"),
 421        OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
 422                   "only consider symbols in these dsos"),
 423        OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
 424                    "symbol to annotate"),
 425        OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
 426        OPT_INCR('v', "verbose", &verbose,
 427                    "be more verbose (show symbol address, etc)"),
 428        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
 429                    "dump raw trace in ASCII"),
 430        OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
 431        OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
 432        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 433                   "file", "vmlinux pathname"),
 434        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
 435                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
 436        OPT_BOOLEAN('l', "print-line", &print_line,
 437                    "print matching source lines (may be slow)"),
 438        OPT_BOOLEAN('P', "full-paths", &full_paths,
 439                    "Don't shorten the displayed pathnames"),
 440        OPT_END()
 441};
 442
 443int cmd_annotate(int argc, const char **argv, const char *prefix __used)
 444{
 445        argc = parse_options(argc, argv, options, annotate_usage, 0);
 446
 447        if (use_stdio)
 448                use_browser = 0;
 449        else if (use_tui)
 450                use_browser = 1;
 451
 452        setup_browser();
 453
 454        symbol_conf.priv_size = sizeof(struct sym_priv);
 455        symbol_conf.try_vmlinux_path = true;
 456
 457        if (symbol__init() < 0)
 458                return -1;
 459
 460        setup_sorting(annotate_usage, options);
 461
 462        if (argc) {
 463                /*
 464                 * Special case: if there's an argument left then assume tha
 465                 * it's a symbol filter:
 466                 */
 467                if (argc > 1)
 468                        usage_with_options(annotate_usage, options);
 469
 470                sym_hist_filter = argv[0];
 471        }
 472
 473        if (field_sep && *field_sep == '.') {
 474                pr_err("'.' is the only non valid --field-separator argument\n");
 475                return -1;
 476        }
 477
 478        return __cmd_annotate();
 479}
 480
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.