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 40 struct 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 59 static 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 74 static 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 } 163 out: 164 return err; 165 } 166 167 static 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; 241 out: 242 free(bi); 243 return err; 244 } 245 246 static 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); 293 out: 294 return err; 295 } 296 297 298 static 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 342 static 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 */ 367 static 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 406 extern volatile int session_done; 407 408 static void sig_handler(int sig __maybe_unused) 409 { 410 session_done = 1; 411 } 412 413 static 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 450 static 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 484 static 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 601 out_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 617 static int 618 parse_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; 689 setup: 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 697 static int 698 parse_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 707 static int 708 parse_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 717 int 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 860 repeat: 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 964 error: 965 perf_session__delete(session); 966 return ret; 967 } 968