linux/tools/perf/builtin-report.c
<<
>>
Prefs
   1/*
   2 * builtin-report.c
   3 *
   4 * Builtin report 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/annotate.h"
  13#include "util/color.h"
  14#include <linux/list.h>
  15#include "util/cache.h"
  16#include <linux/rbtree.h>
  17#include "util/symbol.h"
  18#include "util/callchain.h"
  19#include "util/strlist.h"
  20#include "util/values.h"
  21
  22#include "perf.h"
  23#include "util/debug.h"
  24#include "util/evlist.h"
  25#include "util/evsel.h"
  26#include "util/header.h"
  27#include "util/session.h"
  28#include "util/tool.h"
  29
  30#include "util/parse-options.h"
  31#include "util/parse-events.h"
  32
  33#include "util/thread.h"
  34#include "util/sort.h"
  35#include "util/hist.h"
  36
  37#include <linux/bitmap.h>
  38
  39struct perf_report {
  40        struct perf_tool        tool;
  41        struct perf_session     *session;
  42        char const              *input_name;
  43        bool                    force, use_tui, use_stdio;
  44        bool                    hide_unresolved;
  45        bool                    dont_use_callchains;
  46        bool                    show_full_info;
  47        bool                    show_threads;
  48        bool                    inverted_callchain;
  49        struct perf_read_values show_threads_values;
  50        const char              *pretty_printing_style;
  51        symbol_filter_t         annotate_init;
  52        const char              *cpu_list;
  53        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
  54};
  55
  56static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
  57                                      struct addr_location *al,
  58                                      struct perf_sample *sample,
  59                                      struct machine *machine)
  60{
  61        struct symbol *parent = NULL;
  62        int err = 0;
  63        struct hist_entry *he;
  64
  65        if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
  66                err = machine__resolve_callchain(machine, evsel, al->thread,
  67                                                 sample->callchain, &parent);
  68                if (err)
  69                        return err;
  70        }
  71
  72        he = __hists__add_entry(&evsel->hists, al, parent, sample->period);
  73        if (he == NULL)
  74                return -ENOMEM;
  75
  76        if (symbol_conf.use_callchain) {
  77                err = callchain_append(he->callchain,
  78                                       &evsel->hists.callchain_cursor,
  79                                       sample->period);
  80                if (err)
  81                        return err;
  82        }
  83        /*
  84         * Only in the newt browser we are doing integrated annotation,
  85         * so we don't allocated the extra space needed because the stdio
  86         * code will not use it.
  87         */
  88        if (al->sym != NULL && use_browser > 0) {
  89                struct annotation *notes = symbol__annotation(he->ms.sym);
  90
  91                assert(evsel != NULL);
  92
  93                err = -ENOMEM;
  94                if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
  95                        goto out;
  96
  97                err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
  98        }
  99
 100        evsel->hists.stats.total_period += sample->period;
 101        hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 102out:
 103        return err;
 104}
 105
 106
 107static int process_sample_event(struct perf_tool *tool,
 108                                union perf_event *event,
 109                                struct perf_sample *sample,
 110                                struct perf_evsel *evsel,
 111                                struct machine *machine)
 112{
 113        struct perf_report *rep = container_of(tool, struct perf_report, tool);
 114        struct addr_location al;
 115
 116        if (perf_event__preprocess_sample(event, machine, &al, sample,
 117                                          rep->annotate_init) < 0) {
 118                fprintf(stderr, "problem processing %d event, skipping it.\n",
 119                        event->header.type);
 120                return -1;
 121        }
 122
 123        if (al.filtered || (rep->hide_unresolved && al.sym == NULL))
 124                return 0;
 125
 126        if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
 127                return 0;
 128
 129        if (al.map != NULL)
 130                al.map->dso->hit = 1;
 131
 132        if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
 133                pr_debug("problem incrementing symbol period, skipping event\n");
 134                return -1;
 135        }
 136
 137        return 0;
 138}
 139
 140static int process_read_event(struct perf_tool *tool,
 141                              union perf_event *event,
 142                              struct perf_sample *sample __used,
 143                              struct perf_evsel *evsel,
 144                              struct machine *machine __used)
 145{
 146        struct perf_report *rep = container_of(tool, struct perf_report, tool);
 147
 148        if (rep->show_threads) {
 149                const char *name = evsel ? event_name(evsel) : "unknown";
 150                perf_read_values_add_value(&rep->show_threads_values,
 151                                           event->read.pid, event->read.tid,
 152                                           event->read.id,
 153                                           name,
 154                                           event->read.value);
 155        }
 156
 157        dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
 158                    evsel ? event_name(evsel) : "FAIL",
 159                    event->read.value);
 160
 161        return 0;
 162}
 163
 164static int perf_report__setup_sample_type(struct perf_report *rep)
 165{
 166        struct perf_session *self = rep->session;
 167
 168        if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
 169                if (sort__has_parent) {
 170                        ui__warning("Selected --sort parent, but no "
 171                                    "callchain data. Did you call "
 172                                    "'perf record' without -g?\n");
 173                        return -EINVAL;
 174                }
 175                if (symbol_conf.use_callchain) {
 176                        ui__warning("Selected -g but no callchain data. Did "
 177                                    "you call 'perf record' without -g?\n");
 178                        return -1;
 179                }
 180        } else if (!rep->dont_use_callchains &&
 181                   callchain_param.mode != CHAIN_NONE &&
 182                   !symbol_conf.use_callchain) {
 183                        symbol_conf.use_callchain = true;
 184                        if (callchain_register_param(&callchain_param) < 0) {
 185                                ui__warning("Can't register callchain "
 186                                            "params.\n");
 187                                return -EINVAL;
 188                        }
 189        }
 190
 191        return 0;
 192}
 193
 194extern volatile int session_done;
 195
 196static void sig_handler(int sig __used)
 197{
 198        session_done = 1;
 199}
 200
 201static size_t hists__fprintf_nr_sample_events(struct hists *self,
 202                                              const char *evname, FILE *fp)
 203{
 204        size_t ret;
 205        char unit;
 206        unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
 207
 208        nr_events = convert_unit(nr_events, &unit);
 209        ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
 210        if (evname != NULL)
 211                ret += fprintf(fp, " %s", evname);
 212        return ret + fprintf(fp, "\n#\n");
 213}
 214
 215static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
 216                                         struct perf_report *rep,
 217                                         const char *help)
 218{
 219        struct perf_evsel *pos;
 220
 221        list_for_each_entry(pos, &evlist->entries, node) {
 222                struct hists *hists = &pos->hists;
 223                const char *evname = event_name(pos);
 224
 225                hists__fprintf_nr_sample_events(hists, evname, stdout);
 226                hists__fprintf(hists, NULL, false, true, 0, 0, stdout);
 227                fprintf(stdout, "\n\n");
 228        }
 229
 230        if (sort_order == default_sort_order &&
 231            parent_pattern == default_parent_pattern) {
 232                fprintf(stdout, "#\n# (%s)\n#\n", help);
 233
 234                if (rep->show_threads) {
 235                        bool style = !strcmp(rep->pretty_printing_style, "raw");
 236                        perf_read_values_display(stdout, &rep->show_threads_values,
 237                                                 style);
 238                        perf_read_values_destroy(&rep->show_threads_values);
 239                }
 240        }
 241
 242        return 0;
 243}
 244
 245static int __cmd_report(struct perf_report *rep)
 246{
 247        int ret = -EINVAL;
 248        u64 nr_samples;
 249        struct perf_session *session;
 250        struct perf_evsel *pos;
 251        struct map *kernel_map;
 252        struct kmap *kernel_kmap;
 253        const char *help = "For a higher level overview, try: perf report --sort comm,dso";
 254
 255        signal(SIGINT, sig_handler);
 256
 257        session = perf_session__new(rep->input_name, O_RDONLY,
 258                                    rep->force, false, &rep->tool);
 259        if (session == NULL)
 260                return -ENOMEM;
 261
 262        rep->session = session;
 263
 264        if (rep->cpu_list) {
 265                ret = perf_session__cpu_bitmap(session, rep->cpu_list,
 266                                               rep->cpu_bitmap);
 267                if (ret)
 268                        goto out_delete;
 269        }
 270
 271        if (use_browser <= 0)
 272                perf_session__fprintf_info(session, stdout, rep->show_full_info);
 273
 274        if (rep->show_threads)
 275                perf_read_values_init(&rep->show_threads_values);
 276
 277        ret = perf_report__setup_sample_type(rep);
 278        if (ret)
 279                goto out_delete;
 280
 281        ret = perf_session__process_events(session, &rep->tool);
 282        if (ret)
 283                goto out_delete;
 284
 285        kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION];
 286        kernel_kmap = map__kmap(kernel_map);
 287        if (kernel_map == NULL ||
 288            (kernel_map->dso->hit &&
 289             (kernel_kmap->ref_reloc_sym == NULL ||
 290              kernel_kmap->ref_reloc_sym->addr == 0))) {
 291                const struct dso *kdso = kernel_map->dso;
 292
 293                ui__warning(
 294"Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n"
 295"Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n"
 296"Samples in kernel modules can't be resolved as well.\n\n",
 297                            RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION]) ?
 298"As no suitable kallsyms nor vmlinux was found, kernel samples\n"
 299"can't be resolved." :
 300"If some relocation was applied (e.g. kexec) symbols may be misresolved.");
 301        }
 302
 303        if (dump_trace) {
 304                perf_session__fprintf_nr_events(session, stdout);
 305                goto out_delete;
 306        }
 307
 308        if (verbose > 3)
 309                perf_session__fprintf(session, stdout);
 310
 311        if (verbose > 2)
 312                perf_session__fprintf_dsos(session, stdout);
 313
 314        nr_samples = 0;
 315        list_for_each_entry(pos, &session->evlist->entries, node) {
 316                struct hists *hists = &pos->hists;
 317
 318                hists__collapse_resort(hists);
 319                hists__output_resort(hists);
 320                nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
 321        }
 322
 323        if (nr_samples == 0) {
 324                ui__warning("The %s file has no samples!\n", session->filename);
 325                goto out_delete;
 326        }
 327
 328        if (use_browser > 0) {
 329                perf_evlist__tui_browse_hists(session->evlist, help,
 330                                              NULL, NULL, 0);
 331        } else
 332                perf_evlist__tty_browse_hists(session->evlist, rep, help);
 333
 334out_delete:
 335        /*
 336         * Speed up the exit process, for large files this can
 337         * take quite a while.
 338         *
 339         * XXX Enable this when using valgrind or if we ever
 340         * librarize this command.
 341         *
 342         * Also experiment with obstacks to see how much speed
 343         * up we'll get here.
 344         *
 345         * perf_session__delete(session);
 346         */
 347        return ret;
 348}
 349
 350static int
 351parse_callchain_opt(const struct option *opt, const char *arg, int unset)
 352{
 353        struct perf_report *rep = (struct perf_report *)opt->value;
 354        char *tok, *tok2;
 355        char *endptr;
 356
 357        /*
 358         * --no-call-graph
 359         */
 360        if (unset) {
 361                rep->dont_use_callchains = true;
 362                return 0;
 363        }
 364
 365        symbol_conf.use_callchain = true;
 366
 367        if (!arg)
 368                return 0;
 369
 370        tok = strtok((char *)arg, ",");
 371        if (!tok)
 372                return -1;
 373
 374        /* get the output mode */
 375        if (!strncmp(tok, "graph", strlen(arg)))
 376                callchain_param.mode = CHAIN_GRAPH_ABS;
 377
 378        else if (!strncmp(tok, "flat", strlen(arg)))
 379                callchain_param.mode = CHAIN_FLAT;
 380
 381        else if (!strncmp(tok, "fractal", strlen(arg)))
 382                callchain_param.mode = CHAIN_GRAPH_REL;
 383
 384        else if (!strncmp(tok, "none", strlen(arg))) {
 385                callchain_param.mode = CHAIN_NONE;
 386                symbol_conf.use_callchain = false;
 387
 388                return 0;
 389        }
 390
 391        else
 392                return -1;
 393
 394        /* get the min percentage */
 395        tok = strtok(NULL, ",");
 396        if (!tok)
 397                goto setup;
 398
 399        callchain_param.min_percent = strtod(tok, &endptr);
 400        if (tok == endptr)
 401                return -1;
 402
 403        /* get the print limit */
 404        tok2 = strtok(NULL, ",");
 405        if (!tok2)
 406                goto setup;
 407
 408        if (tok2[0] != 'c') {
 409                callchain_param.print_limit = strtoul(tok2, &endptr, 0);
 410                tok2 = strtok(NULL, ",");
 411                if (!tok2)
 412                        goto setup;
 413        }
 414
 415        /* get the call chain order */
 416        if (!strcmp(tok2, "caller"))
 417                callchain_param.order = ORDER_CALLER;
 418        else if (!strcmp(tok2, "callee"))
 419                callchain_param.order = ORDER_CALLEE;
 420        else
 421                return -1;
 422setup:
 423        if (callchain_register_param(&callchain_param) < 0) {
 424                fprintf(stderr, "Can't register callchain params\n");
 425                return -1;
 426        }
 427        return 0;
 428}
 429
 430int cmd_report(int argc, const char **argv, const char *prefix __used)
 431{
 432        struct stat st;
 433        char callchain_default_opt[] = "fractal,0.5,callee";
 434        const char * const report_usage[] = {
 435                "perf report [<options>]",
 436                NULL
 437        };
 438        struct perf_report report = {
 439                .tool = {
 440                        .sample          = process_sample_event,
 441                        .mmap            = perf_event__process_mmap,
 442                        .comm            = perf_event__process_comm,
 443                        .exit            = perf_event__process_task,
 444                        .fork            = perf_event__process_task,
 445                        .lost            = perf_event__process_lost,
 446                        .read            = process_read_event,
 447                        .attr            = perf_event__process_attr,
 448                        .event_type      = perf_event__process_event_type,
 449                        .tracing_data    = perf_event__process_tracing_data,
 450                        .build_id        = perf_event__process_build_id,
 451                        .ordered_samples = true,
 452                        .ordering_requires_timestamps = true,
 453                },
 454                .pretty_printing_style   = "normal",
 455        };
 456        const struct option options[] = {
 457        OPT_STRING('i', "input", &report.input_name, "file",
 458                    "input file name"),
 459        OPT_INCR('v', "verbose", &verbose,
 460                    "be more verbose (show symbol address, etc)"),
 461        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
 462                    "dump raw trace in ASCII"),
 463        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 464                   "file", "vmlinux pathname"),
 465        OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
 466                   "file", "kallsyms pathname"),
 467        OPT_BOOLEAN('f', "force", &report.force, "don't complain, do it"),
 468        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
 469                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
 470        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
 471                    "Show a column with the number of samples"),
 472        OPT_BOOLEAN('T', "threads", &report.show_threads,
 473                    "Show per-thread event counters"),
 474        OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
 475                   "pretty printing style key: normal raw"),
 476        OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
 477        OPT_BOOLEAN(0, "stdio", &report.use_stdio,
 478                    "Use the stdio interface"),
 479        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
 480                   "sort by key(s): pid, comm, dso, symbol, parent"),
 481        OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
 482                    "Show sample percentage for different cpu modes"),
 483        OPT_STRING('p', "parent", &parent_pattern, "regex",
 484                   "regex filter to identify parent, see: '--sort parent'"),
 485        OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
 486                    "Only display entries with parent-match"),
 487        OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
 488                     "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit and callchain order. "
 489                     "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
 490        OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
 491                    "alias for inverted call graph"),
 492        OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
 493                   "only consider symbols in these dsos"),
 494        OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
 495                   "only consider symbols in these comms"),
 496        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
 497                   "only consider these symbols"),
 498        OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
 499                   "width[,width...]",
 500                   "don't try to adjust column width, use these fixed values"),
 501        OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
 502                   "separator for columns, no spaces will be added between "
 503                   "columns '.' is reserved."),
 504        OPT_BOOLEAN('U', "hide-unresolved", &report.hide_unresolved,
 505                    "Only display entries resolved to a symbol"),
 506        OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 507                    "Look for files with symbols relative to this directory"),
 508        OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
 509                   "list of cpus to profile"),
 510        OPT_BOOLEAN('I', "show-info", &report.show_full_info,
 511                    "Display extended information about perf.data file"),
 512        OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
 513                    "Interleave source code with assembly code (default)"),
 514        OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
 515                    "Display raw encoding of assembly instructions (default)"),
 516        OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
 517                   "Specify disassembler style (e.g. -M intel for intel syntax)"),
 518        OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
 519                    "Show a column with the sum of periods"),
 520        OPT_END()
 521        };
 522
 523        argc = parse_options(argc, argv, options, report_usage, 0);
 524
 525        if (report.use_stdio)
 526                use_browser = 0;
 527        else if (report.use_tui)
 528                use_browser = 1;
 529
 530        if (report.inverted_callchain)
 531                callchain_param.order = ORDER_CALLER;
 532
 533        if (!report.input_name || !strlen(report.input_name)) {
 534                if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
 535                        report.input_name = "-";
 536                else
 537                        report.input_name = "perf.data";
 538        }
 539
 540        if (strcmp(report.input_name, "-") != 0)
 541                setup_browser(true);
 542        else
 543                use_browser = 0;
 544
 545        /*
 546         * Only in the newt browser we are doing integrated annotation,
 547         * so don't allocate extra space that won't be used in the stdio
 548         * implementation.
 549         */
 550        if (use_browser > 0) {
 551                symbol_conf.priv_size = sizeof(struct annotation);
 552                report.annotate_init  = symbol__annotate_init;
 553                /*
 554                 * For searching by name on the "Browse map details".
 555                 * providing it only in verbose mode not to bloat too
 556                 * much struct symbol.
 557                 */
 558                if (verbose) {
 559                        /*
 560                         * XXX: Need to provide a less kludgy way to ask for
 561                         * more space per symbol, the u32 is for the index on
 562                         * the ui browser.
 563                         * See symbol__browser_index.
 564                         */
 565                        symbol_conf.priv_size += sizeof(u32);
 566                        symbol_conf.sort_by_name = true;
 567                }
 568        }
 569
 570        if (symbol__init() < 0)
 571                return -1;
 572
 573        setup_sorting(report_usage, options);
 574
 575        if (parent_pattern != default_parent_pattern) {
 576                if (sort_dimension__add("parent") < 0)
 577                        return -1;
 578
 579                /*
 580                 * Only show the parent fields if we explicitly
 581                 * sort that way. If we only use parent machinery
 582                 * for filtering, we don't want it.
 583                 */
 584                if (!strstr(sort_order, "parent"))
 585                        sort_parent.elide = 1;
 586        } else
 587                symbol_conf.exclude_other = false;
 588
 589        /*
 590         * Any (unrecognized) arguments left?
 591         */
 592        if (argc)
 593                usage_with_options(report_usage, options);
 594
 595        sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
 596        sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
 597        sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
 598
 599        return __cmd_report(&report);
 600}
 601
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.