diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 94 |
1 files changed, 52 insertions, 42 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3662047cc6b1..72eae7498c09 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -49,7 +49,6 @@ struct perf_report { | |||
49 | bool mem_mode; | 49 | bool mem_mode; |
50 | struct perf_read_values show_threads_values; | 50 | struct perf_read_values show_threads_values; |
51 | const char *pretty_printing_style; | 51 | const char *pretty_printing_style; |
52 | symbol_filter_t annotate_init; | ||
53 | const char *cpu_list; | 52 | const char *cpu_list; |
54 | const char *symbol_filter_str; | 53 | const char *symbol_filter_str; |
55 | float min_percent; | 54 | float min_percent; |
@@ -89,7 +88,7 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | |||
89 | if ((sort__has_parent || symbol_conf.use_callchain) && | 88 | if ((sort__has_parent || symbol_conf.use_callchain) && |
90 | sample->callchain) { | 89 | sample->callchain) { |
91 | err = machine__resolve_callchain(machine, evsel, al->thread, | 90 | err = machine__resolve_callchain(machine, evsel, al->thread, |
92 | sample, &parent); | 91 | sample, &parent, al); |
93 | if (err) | 92 | if (err) |
94 | return err; | 93 | return err; |
95 | } | 94 | } |
@@ -180,7 +179,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
180 | if ((sort__has_parent || symbol_conf.use_callchain) | 179 | if ((sort__has_parent || symbol_conf.use_callchain) |
181 | && sample->callchain) { | 180 | && sample->callchain) { |
182 | err = machine__resolve_callchain(machine, evsel, al->thread, | 181 | err = machine__resolve_callchain(machine, evsel, al->thread, |
183 | sample, &parent); | 182 | sample, &parent, al); |
184 | if (err) | 183 | if (err) |
185 | return err; | 184 | return err; |
186 | } | 185 | } |
@@ -254,7 +253,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
254 | 253 | ||
255 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 254 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { |
256 | err = machine__resolve_callchain(machine, evsel, al->thread, | 255 | err = machine__resolve_callchain(machine, evsel, al->thread, |
257 | sample, &parent); | 256 | sample, &parent, al); |
258 | if (err) | 257 | if (err) |
259 | return err; | 258 | return err; |
260 | } | 259 | } |
@@ -305,8 +304,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
305 | struct addr_location al; | 304 | struct addr_location al; |
306 | int ret; | 305 | int ret; |
307 | 306 | ||
308 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 307 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
309 | rep->annotate_init) < 0) { | ||
310 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 308 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
311 | event->header.type); | 309 | event->header.type); |
312 | return -1; | 310 | return -1; |
@@ -367,7 +365,7 @@ static int process_read_event(struct perf_tool *tool, | |||
367 | static int perf_report__setup_sample_type(struct perf_report *rep) | 365 | static int perf_report__setup_sample_type(struct perf_report *rep) |
368 | { | 366 | { |
369 | struct perf_session *self = rep->session; | 367 | struct perf_session *self = rep->session; |
370 | u64 sample_type = perf_evlist__sample_type(self->evlist); | 368 | u64 sample_type = perf_evlist__combined_sample_type(self->evlist); |
371 | 369 | ||
372 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 370 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
373 | if (sort__has_parent) { | 371 | if (sort__has_parent) { |
@@ -403,8 +401,6 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
403 | return 0; | 401 | return 0; |
404 | } | 402 | } |
405 | 403 | ||
406 | extern volatile int session_done; | ||
407 | |||
408 | static void sig_handler(int sig __maybe_unused) | 404 | static void sig_handler(int sig __maybe_unused) |
409 | { | 405 | { |
410 | session_done = 1; | 406 | session_done = 1; |
@@ -497,7 +493,7 @@ static int __cmd_report(struct perf_report *rep) | |||
497 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, | 493 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, |
498 | rep->cpu_bitmap); | 494 | rep->cpu_bitmap); |
499 | if (ret) | 495 | if (ret) |
500 | goto out_delete; | 496 | return ret; |
501 | } | 497 | } |
502 | 498 | ||
503 | if (use_browser <= 0) | 499 | if (use_browser <= 0) |
@@ -508,11 +504,11 @@ static int __cmd_report(struct perf_report *rep) | |||
508 | 504 | ||
509 | ret = perf_report__setup_sample_type(rep); | 505 | ret = perf_report__setup_sample_type(rep); |
510 | if (ret) | 506 | if (ret) |
511 | goto out_delete; | 507 | return ret; |
512 | 508 | ||
513 | ret = perf_session__process_events(session, &rep->tool); | 509 | ret = perf_session__process_events(session, &rep->tool); |
514 | if (ret) | 510 | if (ret) |
515 | goto out_delete; | 511 | return ret; |
516 | 512 | ||
517 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; | 513 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; |
518 | kernel_kmap = map__kmap(kernel_map); | 514 | kernel_kmap = map__kmap(kernel_map); |
@@ -547,7 +543,7 @@ static int __cmd_report(struct perf_report *rep) | |||
547 | 543 | ||
548 | if (dump_trace) { | 544 | if (dump_trace) { |
549 | perf_session__fprintf_nr_events(session, stdout); | 545 | perf_session__fprintf_nr_events(session, stdout); |
550 | goto out_delete; | 546 | return 0; |
551 | } | 547 | } |
552 | 548 | ||
553 | nr_samples = 0; | 549 | nr_samples = 0; |
@@ -570,9 +566,12 @@ static int __cmd_report(struct perf_report *rep) | |||
570 | } | 566 | } |
571 | } | 567 | } |
572 | 568 | ||
569 | if (session_done()) | ||
570 | return 0; | ||
571 | |||
573 | if (nr_samples == 0) { | 572 | if (nr_samples == 0) { |
574 | ui__error("The %s file has no samples!\n", session->filename); | 573 | ui__error("The %s file has no samples!\n", session->filename); |
575 | goto out_delete; | 574 | return 0; |
576 | } | 575 | } |
577 | 576 | ||
578 | list_for_each_entry(pos, &session->evlist->entries, node) | 577 | list_for_each_entry(pos, &session->evlist->entries, node) |
@@ -598,19 +597,6 @@ static int __cmd_report(struct perf_report *rep) | |||
598 | } else | 597 | } else |
599 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 598 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
600 | 599 | ||
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; | 600 | return ret; |
615 | } | 601 | } |
616 | 602 | ||
@@ -680,12 +666,23 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
680 | } | 666 | } |
681 | 667 | ||
682 | /* get the call chain order */ | 668 | /* get the call chain order */ |
683 | if (!strcmp(tok2, "caller")) | 669 | if (!strncmp(tok2, "caller", strlen("caller"))) |
684 | callchain_param.order = ORDER_CALLER; | 670 | callchain_param.order = ORDER_CALLER; |
685 | else if (!strcmp(tok2, "callee")) | 671 | else if (!strncmp(tok2, "callee", strlen("callee"))) |
686 | callchain_param.order = ORDER_CALLEE; | 672 | callchain_param.order = ORDER_CALLEE; |
687 | else | 673 | else |
688 | return -1; | 674 | return -1; |
675 | |||
676 | /* Get the sort key */ | ||
677 | tok2 = strtok(NULL, ","); | ||
678 | if (!tok2) | ||
679 | goto setup; | ||
680 | if (!strncmp(tok2, "function", strlen("function"))) | ||
681 | callchain_param.key = CCKEY_FUNCTION; | ||
682 | else if (!strncmp(tok2, "address", strlen("address"))) | ||
683 | callchain_param.key = CCKEY_ADDRESS; | ||
684 | else | ||
685 | return -1; | ||
689 | setup: | 686 | setup: |
690 | if (callchain_register_param(&callchain_param) < 0) { | 687 | if (callchain_register_param(&callchain_param) < 0) { |
691 | fprintf(stderr, "Can't register callchain params\n"); | 688 | fprintf(stderr, "Can't register callchain params\n"); |
@@ -694,6 +691,24 @@ setup: | |||
694 | return 0; | 691 | return 0; |
695 | } | 692 | } |
696 | 693 | ||
694 | int | ||
695 | report_parse_ignore_callees_opt(const struct option *opt __maybe_unused, | ||
696 | const char *arg, int unset __maybe_unused) | ||
697 | { | ||
698 | if (arg) { | ||
699 | int err = regcomp(&ignore_callees_regex, arg, REG_EXTENDED); | ||
700 | if (err) { | ||
701 | char buf[BUFSIZ]; | ||
702 | regerror(err, &ignore_callees_regex, buf, sizeof(buf)); | ||
703 | pr_err("Invalid --ignore-callees regex: %s\n%s", arg, buf); | ||
704 | return -1; | ||
705 | } | ||
706 | have_ignore_callees = 1; | ||
707 | } | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | |||
697 | static int | 712 | static int |
698 | parse_branch_mode(const struct option *opt __maybe_unused, | 713 | parse_branch_mode(const struct option *opt __maybe_unused, |
699 | const char *str __maybe_unused, int unset) | 714 | const char *str __maybe_unused, int unset) |
@@ -730,13 +745,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
730 | .tool = { | 745 | .tool = { |
731 | .sample = process_sample_event, | 746 | .sample = process_sample_event, |
732 | .mmap = perf_event__process_mmap, | 747 | .mmap = perf_event__process_mmap, |
748 | .mmap2 = perf_event__process_mmap2, | ||
733 | .comm = perf_event__process_comm, | 749 | .comm = perf_event__process_comm, |
734 | .exit = perf_event__process_exit, | 750 | .exit = perf_event__process_exit, |
735 | .fork = perf_event__process_fork, | 751 | .fork = perf_event__process_fork, |
736 | .lost = perf_event__process_lost, | 752 | .lost = perf_event__process_lost, |
737 | .read = process_read_event, | 753 | .read = process_read_event, |
738 | .attr = perf_event__process_attr, | 754 | .attr = perf_event__process_attr, |
739 | .event_type = perf_event__process_event_type, | ||
740 | .tracing_data = perf_event__process_tracing_data, | 755 | .tracing_data = perf_event__process_tracing_data, |
741 | .build_id = perf_event__process_build_id, | 756 | .build_id = perf_event__process_build_id, |
742 | .ordered_samples = true, | 757 | .ordered_samples = true, |
@@ -780,10 +795,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
780 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 795 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
781 | "Only display entries with parent-match"), | 796 | "Only display entries with parent-match"), |
782 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", | 797 | 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. " | 798 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " |
784 | "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), | 799 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), |
785 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, | 800 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, |
786 | "alias for inverted call graph"), | 801 | "alias for inverted call graph"), |
802 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", | ||
803 | "ignore callees of these functions in call graphs", | ||
804 | report_parse_ignore_callees_opt), | ||
787 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 805 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
788 | "only consider symbols in these dsos"), | 806 | "only consider symbols in these dsos"), |
789 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 807 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
@@ -853,7 +871,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
853 | setup_browser(true); | 871 | setup_browser(true); |
854 | else { | 872 | else { |
855 | use_browser = 0; | 873 | use_browser = 0; |
856 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
857 | perf_hpp__init(); | 874 | perf_hpp__init(); |
858 | } | 875 | } |
859 | 876 | ||
@@ -907,7 +924,8 @@ repeat: | |||
907 | */ | 924 | */ |
908 | if (use_browser == 1 && sort__has_sym) { | 925 | if (use_browser == 1 && sort__has_sym) { |
909 | symbol_conf.priv_size = sizeof(struct annotation); | 926 | symbol_conf.priv_size = sizeof(struct annotation); |
910 | report.annotate_init = symbol__annotate_init; | 927 | machines__set_symbol_filter(&session->machines, |
928 | symbol__annotate_init); | ||
911 | /* | 929 | /* |
912 | * For searching by name on the "Browse map details". | 930 | * For searching by name on the "Browse map details". |
913 | * providing it only in verbose mode not to bloat too | 931 | * providing it only in verbose mode not to bloat too |
@@ -931,14 +949,6 @@ repeat: | |||
931 | if (parent_pattern != default_parent_pattern) { | 949 | if (parent_pattern != default_parent_pattern) { |
932 | if (sort_dimension__add("parent") < 0) | 950 | if (sort_dimension__add("parent") < 0) |
933 | goto error; | 951 | 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 | } | 952 | } |
943 | 953 | ||
944 | if (argc) { | 954 | if (argc) { |