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#include "util/cache.h"
  12
  13#include "util/annotate.h"
  14#include "util/color.h"
  15#include <linux/list.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#include "arch/common.h"
  37
  38#include <linux/bitmap.h>
  39
  40struct perf_report {
  41        struct perf_tool        tool;
  42        struct perf_session     *session;
  43        bool                    force, use_tui, use_gtk, 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        bool                    mem_mode;
  50        struct perf_read_values show_threads_values;
  51        const char              *pretty_printing_style;
  52        symbol_filter_t         annotate_init;
  53        const char              *cpu_list;
  54        const char              *symbol_filter_str;
  55        float                   min_percent;
  56        DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
  57};
  58
  59static int perf_report_config(const char *var, const char *value, void *cb)
  60{
  61        if (!strcmp(var, "report.group")) {
  62                symbol_conf.event_group = perf_config_bool(var, value);
  63                return 0;
  64        }
  65        if (!strcmp(var, "report.percent-limit")) {
  66                struct perf_report *rep = cb;
  67                rep->min_percent = strtof(value, NULL);
  68                return 0;
  69        }
  70
  71        return perf_default_config(var, value, cb);
  72}
  73
  74static int perf_report__add_mem_hist_entry(struct perf_tool *tool,
  75                                           struct addr_location *al,
  76                                           struct perf_sample *sample,
  77                                           struct perf_evsel *evsel,
  78                                           struct machine *machine,
  79                                           union perf_event *event)
  80{
  81        struct perf_report *rep = container_of(tool, struct perf_report, tool);
  82        struct symbol *parent = NULL;
  83        u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
  84        int err = 0;
  85        struct hist_entry *he;
  86        struct mem_info *mi, *mx;
  87        uint64_t cost;
  88
  89        if ((sort__has_parent || symbol_conf.use_callchain) &&
  90            sample->callchain) {
  91                err = machine__resolve_callchain(machine, evsel, al->thread,
  92                                                 sample, &parent);
  93                if (err)
  94                        return err;
  95        }
  96
  97        mi = machine__resolve_mem(machine, al->thread, sample, cpumode);
  98        if (!mi)
  99                return -ENOMEM;
 100
 101        if (rep->hide_unresolved && !al->sym)
 102                return 0;
 103
 104        cost = sample->weight;
 105        if (!cost)
 106                cost = 1;
 107
 108        /*
 109         * must pass period=weight in order to get the correct
 110         * sorting from hists__collapse_resort() which is solely
 111         * based on periods. We want sorting be done on nr_events * weight
 112         * and this is indirectly achieved by passing period=weight here
 113         * and the he_stat__add_period() function.
 114         */
 115        he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost);
 116        if (!he)
 117                return -ENOMEM;
 118
 119        /*
 120         * In the TUI browser, we are doing integrated annotation,
 121         * so we don't allocate the extra space needed because the stdio
 122         * code will not use it.
 123         */
 124        if (sort__has_sym && he->ms.sym && use_browser > 0) {
 125                struct annotation *notes = symbol__annotation(he->ms.sym);
 126
 127                assert(evsel != NULL);
 128
 129                if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
 130                        goto out;
 131
 132                err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
 133                if (err)
 134                        goto out;
 135        }
 136
 137        if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) {
 138                struct annotation *notes;
 139
 140                mx = he->mem_info;
 141
 142                notes = symbol__annotation(mx->daddr.sym);
 143                if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0)
 144                        goto out;
 145
 146                err = symbol__inc_addr_samples(mx->daddr.sym,
 147                                               mx->daddr.map,
 148                                               evsel->idx,
 149                                               mx->daddr.al_addr);
 150                if (err)
 151                        goto out;
 152        }
 153
 154        evsel->hists.stats.total_period += cost;
 155        hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 156        err = 0;
 157
 158        if (symbol_conf.use_callchain) {
 159                err = callchain_append(he->callchain,
 160                                       &callchain_cursor,
 161                                       sample->period);
 162        }
 163out:
 164        return err;
 165}
 166
 167static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
 168                                        struct addr_location *al,
 169                                        struct perf_sample *sample,
 170                                        struct perf_evsel *evsel,
 171                                      struct machine *machine)
 172{
 173        struct perf_report *rep = container_of(tool, struct perf_report, tool);
 174        struct symbol *parent = NULL;
 175        int err = 0;
 176        unsigned i;
 177        struct hist_entry *he;
 178        struct branch_info *bi, *bx;
 179
 180        if ((sort__has_parent || symbol_conf.use_callchain)
 181            && sample->callchain) {
 182                err = machine__resolve_callchain(machine, evsel, al->thread,
 183                                                 sample, &parent);
 184                if (err)
 185                        return err;
 186        }
 187
 188        bi = machine__resolve_bstack(machine, al->thread,
 189                                     sample->branch_stack);
 190        if (!bi)
 191                return -ENOMEM;
 192
 193        for (i = 0; i < sample->branch_stack->nr; i++) {
 194                if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
 195                        continue;
 196
 197                err = -ENOMEM;
 198
 199                /*
 200                 * The report shows the percentage of total branches captured
 201                 * and not events sampled. Thus we use a pseudo period of 1.
 202                 */
 203                he = __hists__add_branch_entry(&evsel->hists, al, parent,
 204                                &bi[i], 1, 1);
 205                if (he) {
 206                        struct annotation *notes;
 207                        bx = he->branch_info;
 208                        if (bx->from.sym && use_browser == 1 && sort__has_sym) {
 209                                notes = symbol__annotation(bx->from.sym);
 210                                if (!notes->src
 211                                    && symbol__alloc_hist(bx->from.sym) < 0)
 212                                        goto out;
 213
 214                                err = symbol__inc_addr_samples(bx->from.sym,
 215                                                               bx->from.map,
 216                                                               evsel->idx,
 217                                                               bx->from.al_addr);
 218                                if (err)
 219                                        goto out;
 220                        }
 221
 222                        if (bx->to.sym && use_browser == 1 && sort__has_sym) {
 223                                notes = symbol__annotation(bx->to.sym);
 224                                if (!notes->src
 225                                    && symbol__alloc_hist(bx->to.sym) < 0)
 226                                        goto out;
 227
 228                                err = symbol__inc_addr_samples(bx->to.sym,
 229                                                               bx->to.map,
 230                                                               evsel->idx,
 231                                                               bx->to.al_addr);
 232                                if (err)
 233                                        goto out;
 234                        }
 235                        evsel->hists.stats.total_period += 1;
 236                        hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 237                } else
 238                        goto out;
 239        }
 240        err = 0;
 241out:
 242        free(bi);
 243        return err;
 244}
 245
 246static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
 247                                      struct addr_location *al,
 248                                      struct perf_sample *sample,
 249                                      struct machine *machine)
 250{
 251        struct symbol *parent = NULL;
 252        int err = 0;
 253        struct hist_entry *he;
 254
 255        if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
 256                err = machine__resolve_callchain(machine, evsel, al->thread,
 257                                                 sample, &parent);
 258                if (err)
 259                        return err;
 260        }
 261
 262        he = __hists__add_entry(&evsel->hists, al, parent, sample->period,
 263                                        sample->weight);
 264        if (he == NULL)
 265                return -ENOMEM;
 266
 267        if (symbol_conf.use_callchain) {
 268                err = callchain_append(he->callchain,
 269                                       &callchain_cursor,
 270                                       sample->period);
 271                if (err)
 272                        return err;
 273        }
 274        /*
 275         * Only in the TUI browser we are doing integrated annotation,
 276         * so we don't allocated the extra space needed because the stdio
 277         * code will not use it.
 278         */
 279        if (he->ms.sym != NULL && use_browser == 1 && sort__has_sym) {
 280                struct annotation *notes = symbol__annotation(he->ms.sym);
 281
 282                assert(evsel != NULL);
 283
 284                err = -ENOMEM;
 285                if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
 286                        goto out;
 287
 288                err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
 289        }
 290
 291        evsel->hists.stats.total_period += sample->period;
 292        hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 293out:
 294        return err;
 295}
 296
 297
 298static int process_sample_event(struct perf_tool *tool,
 299                                union perf_event *event,
 300                                struct perf_sample *sample,
 301                                struct perf_evsel *evsel,
 302                                struct machine *machine)
 303{
 304        struct perf_report *rep = container_of(tool, struct perf_report, tool);
 305        struct addr_location al;
 306        int ret;
 307
 308        if (perf_event__preprocess_sample(event, machine, &al, sample,
 309                                          rep->annotate_init) < 0) {
 310                fprintf(stderr, "problem processing %d event, skipping it.\n",
 311                        event->header.type);
 312                return -1;
 313        }
 314
 315        if (al.filtered || (rep->hide_unresolved && al.sym == NULL))
 316                return 0;
 317
 318        if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
 319                return 0;
 320
 321        if (sort__mode == SORT_MODE__BRANCH) {
 322                ret = perf_report__add_branch_hist_entry(tool, &al, sample,
 323                                                         evsel, machine);
 324                if (ret < 0)
 325                        pr_debug("problem adding lbr entry, skipping event\n");
 326        } else if (rep->mem_mode == 1) {
 327                ret = perf_report__add_mem_hist_entry(tool, &al, sample,
 328                                                      evsel, machine, event);
 329                if (ret < 0)
 330                        pr_debug("problem adding mem entry, skipping event\n");
 331        } else {
 332                if (al.map != NULL)
 333                        al.map->dso->hit = 1;
 334
 335                ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine);
 336                if (ret < 0)
 337                        pr_debug("problem incrementing symbol period, skipping event\n");
 338        }
 339        return ret;
 340}
 341
 342static int process_read_event(struct perf_tool *tool,
 343                              union perf_event *event,
 344                              struct perf_sample *sample __maybe_unused,
 345                              struct perf_evsel *evsel,
 346                              struct machine *machine __maybe_unused)
 347{
 348        struct perf_report *rep = container_of(tool, struct perf_report, tool);
 349
 350        if (rep->show_threads) {
 351                const char *name = evsel ? perf_evsel__name(evsel) : "unknown";
 352                perf_read_values_add_value(&rep->show_threads_values,
 353                                           event->read.pid, event->read.tid,
 354                                           event->read.id,
 355                                           name,
 356                                           event->read.value);
 357        }
 358
 359        dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
 360                    evsel ? perf_evsel__name(evsel) : "FAIL",
 361                    event->read.value);
 362
 363        return 0;
 364}
 365
 366/* For pipe mode, sample_type is not currently set */
 367static int perf_report__setup_sample_type(struct perf_report *rep)
 368{
 369        struct perf_session *self = rep->session;
 370        u64 sample_type = perf_evlist__sample_type(self->evlist);
 371
 372        if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
 373                if (sort__has_parent) {
 374                        ui__error("Selected --sort parent, but no "
 375                                    "callchain data. Did you call "
 376                                    "'perf record' without -g?\n");
 377                        return -EINVAL;
 378                }
 379                if (symbol_conf.use_callchain) {
 380                        ui__error("Selected -g but no callchain data. Did "
 381                                    "you call 'perf record' without -g?\n");
 382                        return -1;
 383                }
 384        } else if (!rep->dont_use_callchains &&
 385                   callchain_param.mode != CHAIN_NONE &&
 386                   !symbol_conf.use_callchain) {
 387                        symbol_conf.use_callchain = true;
 388                        if (callchain_register_param(&callchain_param) < 0) {
 389                                ui__error("Can't register callchain params.\n");
 390                                return -EINVAL;
 391                        }
 392        }
 393
 394        if (sort__mode == SORT_MODE__BRANCH) {
 395                if (!self->fd_pipe &&
 396                    !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
 397                        ui__error("Selected -b but no branch data. "
 398                                  "Did you call perf record without -b?\n");
 399                        return -1;
 400                }
 401        }
 402
 403        return 0;
 404}
 405
 406extern volatile int session_done;
 407
 408static void sig_handler(int sig __maybe_unused)
 409{
 410        session_done = 1;
 411}
 412
 413static size_t hists__fprintf_nr_sample_events(struct perf_report *rep,
 414                                              struct hists *self,
 415                                              const char *evname, FILE *fp)
 416{
 417        size_t ret;
 418        char unit;
 419        unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
 420        u64 nr_events = self->stats.total_period;
 421        struct perf_evsel *evsel = hists_to_evsel(self);
 422        char buf[512];
 423        size_t size = sizeof(buf);
 424
 425        if (perf_evsel__is_group_event(evsel)) {
 426                struct perf_evsel *pos;
 427
 428                perf_evsel__group_desc(evsel, buf, size);
 429                evname = buf;
 430
 431                for_each_group_member(pos, evsel) {
 432                        nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
 433                        nr_events += pos->hists.stats.total_period;
 434                }
 435        }
 436
 437        nr_samples = convert_unit(nr_samples, &unit);
 438        ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
 439        if (evname != NULL)
 440                ret += fprintf(fp, " of event '%s'", evname);
 441
 442        if (rep->mem_mode) {
 443                ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events);
 444                ret += fprintf(fp, "\n# Sort order   : %s", sort_order);
 445        } else
 446                ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
 447        return ret + fprintf(fp, "\n#\n");
 448}
 449
 450static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
 451                                         struct perf_report *rep,
 452                                         const char *help)
 453{
 454        struct perf_evsel *pos;
 455
 456        list_for_each_entry(pos, &evlist->entries, node) {
 457                struct hists *hists = &pos->hists;
 458                const char *evname = perf_evsel__name(pos);
 459
 460                if (symbol_conf.event_group &&
 461                    !perf_evsel__is_group_leader(pos))
 462                        continue;
 463
 464                hists__fprintf_nr_sample_events(rep, hists, evname, stdout);
 465                hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout);
 466                fprintf(stdout, "\n\n");
 467        }
 468
 469        if (sort_order == default_sort_order &&
 470            parent_pattern == default_parent_pattern) {
 471                fprintf(stdout, "#\n# (%s)\n#\n", help);
 472
 473                if (rep->show_threads) {
 474                        bool style = !strcmp(rep->pretty_printing_style, "raw");
 475                        perf_read_values_display(stdout, &rep->show_threads_values,
 476                                                 style);
 477                        perf_read_values_destroy(&rep->show_threads_values);
 478                }
 479        }
 480
 481        return 0;
 482}
 483
 484static int __cmd_report(struct perf_report *rep)
 485{
 486        int ret = -EINVAL;
 487        u64 nr_samples;
 488        struct perf_session *session = rep->session;
 489        struct perf_evsel *pos;
 490        struct map *kernel_map;
 491        struct kmap *kernel_kmap;
 492        const char *help = "For a higher level overview, try: perf report --sort comm,dso";
 493
 494        signal(SIGINT, sig_handler);
 495
 496        if (rep->cpu_list) {
 497                ret = perf_session__cpu_bitmap(session, rep->cpu_list,
 498                                               rep->cpu_bitmap);
 499                if (ret)
 500                        goto out_delete;
 501        }
 502
 503        if (use_browser <= 0)
 504                perf_session__fprintf_info(session, stdout, rep->show_full_info);
 505
 506        if (rep->show_threads)
 507                perf_read_values_init(&rep->show_threads_values);
 508
 509        ret = perf_report__setup_sample_type(rep);
 510        if (ret)
 511                goto out_delete;
 512
 513        ret = perf_session__process_events(session, &rep->tool);
 514        if (ret)
 515                goto out_delete;
 516
 517        kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION];
 518        kernel_kmap = map__kmap(kernel_map);
 519        if (kernel_map == NULL ||
 520            (kernel_map->dso->hit &&
 521             (kernel_kmap->ref_reloc_sym == NULL ||
 522              kernel_kmap->ref_reloc_sym->addr == 0))) {
 523                const char *desc =
 524                    "As no suitable kallsyms nor vmlinux was found, kernel samples\n"
 525                    "can't be resolved.";
 526
 527                if (kernel_map) {
 528                        const struct dso *kdso = kernel_map->dso;
 529                        if (!RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION])) {
 530                                desc = "If some relocation was applied (e.g. "
 531                                       "kexec) symbols may be misresolved.";
 532                        }
 533                }
 534
 535                ui__warning(
 536"Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n"
 537"Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n"
 538"Samples in kernel modules can't be resolved as well.\n\n",
 539                desc);
 540        }
 541
 542        if (verbose > 3)
 543                perf_session__fprintf(session, stdout);
 544
 545        if (verbose > 2)
 546                perf_session__fprintf_dsos(session, stdout);
 547
 548        if (dump_trace) {
 549                perf_session__fprintf_nr_events(session, stdout);
 550                goto out_delete;
 551        }
 552
 553        nr_samples = 0;
 554        list_for_each_entry(pos, &session->evlist->entries, node) {
 555                struct hists *hists = &pos->hists;
 556
 557                if (pos->idx == 0)
 558                        hists->symbol_filter_str = rep->symbol_filter_str;
 559
 560                hists__collapse_resort(hists);
 561                nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
 562
 563                /* Non-group events are considered as leader */
 564                if (symbol_conf.event_group &&
 565                    !perf_evsel__is_group_leader(pos)) {
 566                        struct hists *leader_hists = &pos->leader->hists;
 567
 568                        hists__match(leader_hists, hists);
 569                        hists__link(leader_hists, hists);
 570                }
 571        }
 572
 573        if (nr_samples == 0) {
 574                ui__error("The %s file has no samples!\n", session->filename);
 575                goto out_delete;
 576        }
 577
 578        list_for_each_entry(pos, &session->evlist->entries, node)
 579                hists__output_resort(&pos->hists);
 580
 581        if (use_browser > 0) {
 582                if (use_browser == 1) {
 583                        ret = perf_evlist__tui_browse_hists(session->evlist,
 584                                                        help, NULL,
 585                                                        rep->min_percent,
 586                                                        &session->header.env);
 587                        /*
 588                         * Usually "ret" is the last pressed key, and we only
 589                         * care if the key notifies us to switch data file.
 590                         */
 591                        if (ret != K_SWITCH_INPUT_DATA)
 592                                ret = 0;
 593
 594                } else if (use_browser == 2) {
 595                        perf_evlist__gtk_browse_hists(session->evlist, help,
 596                                                      NULL, rep->min_percent);
 597                }
 598        } else
 599                perf_evlist__tty_browse_hists(session->evlist, rep, help);
 600
 601out_delete:
 602        /*
 603         * Speed up the exit process, for large files this can
 604         * take quite a while.
 605         *
 606         * XXX Enable this when using valgrind or if we ever
 607         * librarize this command.
 608         *
 609         * Also experiment with obstacks to see how much speed
 610         * up we'll get here.
 611         *
 612         * perf_session__delete(session);
 613         */
 614        return ret;
 615}
 616
 617static int
 618parse_callchain_opt(const struct option *opt, const char *arg, int unset)
 619{
 620        struct perf_report *rep = (struct perf_report *)opt->value;
 621        char *tok, *tok2;
 622        char *endptr;
 623
 624        /*
 625         * --no-call-graph
 626         */
 627        if (unset) {
 628                rep->dont_use_callchains = true;
 629                return 0;
 630        }
 631
 632        symbol_conf.use_callchain = true;
 633
 634        if (!arg)
 635                return 0;
 636
 637        tok = strtok((char *)arg, ",");
 638        if (!tok)
 639                return -1;
 640
 641        /* get the output mode */
 642        if (!strncmp(tok, "graph", strlen(arg)))
 643                callchain_param.mode = CHAIN_GRAPH_ABS;
 644
 645        else if (!strncmp(tok, "flat", strlen(arg)))
 646                callchain_param.mode = CHAIN_FLAT;
 647
 648        else if (!strncmp(tok, "fractal", strlen(arg)))
 649                callchain_param.mode = CHAIN_GRAPH_REL;
 650
 651        else if (!strncmp(tok, "none", strlen(arg))) {
 652                callchain_param.mode = CHAIN_NONE;
 653                symbol_conf.use_callchain = false;
 654
 655                return 0;
 656        }
 657
 658        else
 659                return -1;
 660
 661        /* get the min percentage */
 662        tok = strtok(NULL, ",");
 663        if (!tok)
 664                goto setup;
 665
 666        callchain_param.min_percent = strtod(tok, &endptr);
 667        if (tok == endptr)
 668                return -1;
 669
 670        /* get the print limit */
 671        tok2 = strtok(NULL, ",");
 672        if (!tok2)
 673                goto setup;
 674
 675        if (tok2[0] != 'c') {
 676                callchain_param.print_limit = strtoul(tok2, &endptr, 0);
 677                tok2 = strtok(NULL, ",");
 678                if (!tok2)
 679                        goto setup;
 680        }
 681
 682        /* get the call chain order */
 683        if (!strcmp(tok2, "caller"))
 684                callchain_param.order = ORDER_CALLER;
 685        else if (!strcmp(tok2, "callee"))
 686                callchain_param.order = ORDER_CALLEE;
 687        else
 688                return -1;
 689setup:
 690        if (callchain_register_param(&callchain_param) < 0) {
 691                fprintf(stderr, "Can't register callchain params\n");
 692                return -1;
 693        }
 694        return 0;
 695}
 696
 697static int
 698parse_branch_mode(const struct option *opt __maybe_unused,
 699                  const char *str __maybe_unused, int unset)
 700{
 701        int *branch_mode = opt->value;
 702
 703        *branch_mode = !unset;
 704        return 0;
 705}
 706
 707static int
 708parse_percent_limit(const struct option *opt, const char *str,
 709                    int unset __maybe_unused)
 710{
 711        struct perf_report *rep = opt->value;
 712
 713        rep->min_percent = strtof(str, NULL);
 714        return 0;
 715}
 716
 717int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
 718{
 719        struct perf_session *session;
 720        struct stat st;
 721        bool has_br_stack = false;
 722        int branch_mode = -1;
 723        int ret = -1;
 724        char callchain_default_opt[] = "fractal,0.5,callee";
 725        const char * const report_usage[] = {
 726                "perf report [<options>]",
 727                NULL
 728        };
 729        struct perf_report report = {
 730                .tool = {
 731                        .sample          = process_sample_event,
 732                        .mmap            = perf_event__process_mmap,
 733                        .comm            = perf_event__process_comm,
 734                        .exit            = perf_event__process_exit,
 735                        .fork            = perf_event__process_fork,
 736                        .lost            = perf_event__process_lost,
 737                        .read            = process_read_event,
 738                        .attr            = perf_event__process_attr,
 739                        .event_type      = perf_event__process_event_type,
 740                        .tracing_data    = perf_event__process_tracing_data,
 741                        .build_id        = perf_event__process_build_id,
 742                        .ordered_samples = true,
 743                        .ordering_requires_timestamps = true,
 744                },
 745                .pretty_printing_style   = "normal",
 746        };
 747        const struct option options[] = {
 748        OPT_STRING('i', "input", &input_name, "file",
 749                    "input file name"),
 750        OPT_INCR('v', "verbose", &verbose,
 751                    "be more verbose (show symbol address, etc)"),
 752        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
 753                    "dump raw trace in ASCII"),
 754        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 755                   "file", "vmlinux pathname"),
 756        OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
 757                   "file", "kallsyms pathname"),
 758        OPT_BOOLEAN('f', "force", &report.force, "don't complain, do it"),
 759        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
 760                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
 761        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
 762                    "Show a column with the number of samples"),
 763        OPT_BOOLEAN('T', "threads", &report.show_threads,
 764                    "Show per-thread event counters"),
 765        OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
 766                   "pretty printing style key: normal raw"),
 767        OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
 768        OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),
 769        OPT_BOOLEAN(0, "stdio", &report.use_stdio,
 770                    "Use the stdio interface"),
 771        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
 772                   "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline,"
 773                   " dso_to, dso_from, symbol_to, symbol_from, mispredict,"
 774                   " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, "
 775                   "snoop, locked"),
 776        OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
 777                    "Show sample percentage for different cpu modes"),
 778        OPT_STRING('p', "parent", &parent_pattern, "regex",
 779                   "regex filter to identify parent, see: '--sort parent'"),
 780        OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
 781                    "Only display entries with parent-match"),
 782        OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
 783                     "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit and callchain order. "
 784                     "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
 785        OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
 786                    "alias for inverted call graph"),
 787        OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
 788                   "only consider symbols in these dsos"),
 789        OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
 790                   "only consider symbols in these comms"),
 791        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
 792                   "only consider these symbols"),
 793        OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter",
 794                   "only show symbols that (partially) match with this filter"),
 795        OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
 796                   "width[,width...]",
 797                   "don't try to adjust column width, use these fixed values"),
 798        OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
 799                   "separator for columns, no spaces will be added between "
 800                   "columns '.' is reserved."),
 801        OPT_BOOLEAN('U', "hide-unresolved", &report.hide_unresolved,
 802                    "Only display entries resolved to a symbol"),
 803        OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 804                    "Look for files with symbols relative to this directory"),
 805        OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
 806                   "list of cpus to profile"),
 807        OPT_BOOLEAN('I', "show-info", &report.show_full_info,
 808                    "Display extended information about perf.data file"),
 809        OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
 810                    "Interleave source code with assembly code (default)"),
 811        OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
 812                    "Display raw encoding of assembly instructions (default)"),
 813        OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
 814                   "Specify disassembler style (e.g. -M intel for intel syntax)"),
 815        OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
 816                    "Show a column with the sum of periods"),
 817        OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
 818                    "Show event group information together"),
 819        OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "",
 820                    "use branch records for histogram filling", parse_branch_mode),
 821        OPT_STRING(0, "objdump", &objdump_path, "path",
 822                   "objdump binary to use for disassembly and annotations"),
 823        OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
 824                    "Disable symbol demangling"),
 825        OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
 826        OPT_CALLBACK(0, "percent-limit", &report, "percent",
 827                     "Don't show entries under that percent", parse_percent_limit),
 828        OPT_END()
 829        };
 830
 831        perf_config(perf_report_config, &report);
 832
 833        argc = parse_options(argc, argv, options, report_usage, 0);
 834
 835        if (report.use_stdio)
 836                use_browser = 0;
 837        else if (report.use_tui)
 838                use_browser = 1;
 839        else if (report.use_gtk)
 840                use_browser = 2;
 841
 842        if (report.inverted_callchain)
 843                callchain_param.order = ORDER_CALLER;
 844
 845        if (!input_name || !strlen(input_name)) {
 846                if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
 847                        input_name = "-";
 848                else
 849                        input_name = "perf.data";
 850        }
 851
 852        if (strcmp(input_name, "-") != 0)
 853                setup_browser(true);
 854        else {
 855                use_browser = 0;
 856                perf_hpp__column_enable(PERF_HPP__OVERHEAD);
 857                perf_hpp__init();
 858        }
 859
 860repeat:
 861        session = perf_session__new(input_name, O_RDONLY,
 862                                    report.force, false, &report.tool);
 863        if (session == NULL)
 864                return -ENOMEM;
 865
 866        report.session = session;
 867
 868        has_br_stack = perf_header__has_feat(&session->header,
 869                                             HEADER_BRANCH_STACK);
 870
 871        if (branch_mode == -1 && has_br_stack)
 872                sort__mode = SORT_MODE__BRANCH;
 873
 874        /* sort__mode could be NORMAL if --no-branch-stack */
 875        if (sort__mode == SORT_MODE__BRANCH) {
 876                /*
 877                 * if no sort_order is provided, then specify
 878                 * branch-mode specific order
 879                 */
 880                if (sort_order == default_sort_order)
 881                        sort_order = "comm,dso_from,symbol_from,"
 882                                     "dso_to,symbol_to";
 883
 884        }
 885        if (report.mem_mode) {
 886                if (sort__mode == SORT_MODE__BRANCH) {
 887                        fprintf(stderr, "branch and mem mode incompatible\n");
 888                        goto error;
 889                }
 890                sort__mode = SORT_MODE__MEMORY;
 891
 892                /*
 893                 * if no sort_order is provided, then specify
 894                 * branch-mode specific order
 895                 */
 896                if (sort_order == default_sort_order)
 897                        sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked";
 898        }
 899
 900        if (setup_sorting() < 0)
 901                usage_with_options(report_usage, options);
 902
 903        /*
 904         * Only in the TUI browser we are doing integrated annotation,
 905         * so don't allocate extra space that won't be used in the stdio
 906         * implementation.
 907         */
 908        if (use_browser == 1 && sort__has_sym) {
 909                symbol_conf.priv_size = sizeof(struct annotation);
 910                report.annotate_init  = symbol__annotate_init;
 911                /*
 912                 * For searching by name on the "Browse map details".
 913                 * providing it only in verbose mode not to bloat too
 914                 * much struct symbol.
 915                 */
 916                if (verbose) {
 917                        /*
 918                         * XXX: Need to provide a less kludgy way to ask for
 919                         * more space per symbol, the u32 is for the index on
 920                         * the ui browser.
 921                         * See symbol__browser_index.
 922                         */
 923                        symbol_conf.priv_size += sizeof(u32);
 924                        symbol_conf.sort_by_name = true;
 925                }
 926        }
 927
 928        if (symbol__init() < 0)
 929                goto error;
 930
 931        if (parent_pattern != default_parent_pattern) {
 932                if (sort_dimension__add("parent") < 0)
 933                        goto error;
 934
 935                /*
 936                 * Only show the parent fields if we explicitly
 937                 * sort that way. If we only use parent machinery
 938                 * for filtering, we don't want it.
 939                 */
 940                if (!strstr(sort_order, "parent"))
 941                        sort_parent.elide = 1;
 942        }
 943
 944        if (argc) {
 945                /*
 946                 * Special case: if there's an argument left then assume that
 947                 * it's a symbol filter:
 948                 */
 949                if (argc > 1)
 950                        usage_with_options(report_usage, options);
 951
 952                report.symbol_filter_str = argv[0];
 953        }
 954
 955        sort__setup_elide(stdout);
 956
 957        ret = __cmd_report(&report);
 958        if (ret == K_SWITCH_INPUT_DATA) {
 959                perf_session__delete(session);
 960                goto repeat;
 961        } else
 962                ret = 0;
 963
 964error:
 965        perf_session__delete(session);
 966        return ret;
 967}
 968
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.