diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 124 |
1 files changed, 86 insertions, 38 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 72eae7498c09..8cf8e66ba594 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -33,8 +33,10 @@ | |||
33 | #include "util/thread.h" | 33 | #include "util/thread.h" |
34 | #include "util/sort.h" | 34 | #include "util/sort.h" |
35 | #include "util/hist.h" | 35 | #include "util/hist.h" |
36 | #include "util/data.h" | ||
36 | #include "arch/common.h" | 37 | #include "arch/common.h" |
37 | 38 | ||
39 | #include <dlfcn.h> | ||
38 | #include <linux/bitmap.h> | 40 | #include <linux/bitmap.h> |
39 | 41 | ||
40 | struct perf_report { | 42 | struct perf_report { |
@@ -47,6 +49,7 @@ struct perf_report { | |||
47 | bool show_threads; | 49 | bool show_threads; |
48 | bool inverted_callchain; | 50 | bool inverted_callchain; |
49 | bool mem_mode; | 51 | bool mem_mode; |
52 | int max_stack; | ||
50 | struct perf_read_values show_threads_values; | 53 | struct perf_read_values show_threads_values; |
51 | const char *pretty_printing_style; | 54 | const char *pretty_printing_style; |
52 | const char *cpu_list; | 55 | const char *cpu_list; |
@@ -88,7 +91,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | |||
88 | if ((sort__has_parent || symbol_conf.use_callchain) && | 91 | if ((sort__has_parent || symbol_conf.use_callchain) && |
89 | sample->callchain) { | 92 | sample->callchain) { |
90 | err = machine__resolve_callchain(machine, evsel, al->thread, | 93 | err = machine__resolve_callchain(machine, evsel, al->thread, |
91 | sample, &parent, al); | 94 | sample, &parent, al, |
95 | rep->max_stack); | ||
92 | if (err) | 96 | if (err) |
93 | return err; | 97 | return err; |
94 | } | 98 | } |
@@ -111,7 +115,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | |||
111 | * and this is indirectly achieved by passing period=weight here | 115 | * and this is indirectly achieved by passing period=weight here |
112 | * and the he_stat__add_period() function. | 116 | * and the he_stat__add_period() function. |
113 | */ | 117 | */ |
114 | he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost); | 118 | he = __hists__add_entry(&evsel->hists, al, parent, NULL, mi, |
119 | cost, cost, 0); | ||
115 | if (!he) | 120 | if (!he) |
116 | return -ENOMEM; | 121 | return -ENOMEM; |
117 | 122 | ||
@@ -179,7 +184,8 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
179 | if ((sort__has_parent || symbol_conf.use_callchain) | 184 | if ((sort__has_parent || symbol_conf.use_callchain) |
180 | && sample->callchain) { | 185 | && sample->callchain) { |
181 | err = machine__resolve_callchain(machine, evsel, al->thread, | 186 | err = machine__resolve_callchain(machine, evsel, al->thread, |
182 | sample, &parent, al); | 187 | sample, &parent, al, |
188 | rep->max_stack); | ||
183 | if (err) | 189 | if (err) |
184 | return err; | 190 | return err; |
185 | } | 191 | } |
@@ -195,12 +201,16 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
195 | 201 | ||
196 | err = -ENOMEM; | 202 | err = -ENOMEM; |
197 | 203 | ||
204 | /* overwrite the 'al' to branch-to info */ | ||
205 | al->map = bi[i].to.map; | ||
206 | al->sym = bi[i].to.sym; | ||
207 | al->addr = bi[i].to.addr; | ||
198 | /* | 208 | /* |
199 | * The report shows the percentage of total branches captured | 209 | * The report shows the percentage of total branches captured |
200 | * and not events sampled. Thus we use a pseudo period of 1. | 210 | * and not events sampled. Thus we use a pseudo period of 1. |
201 | */ | 211 | */ |
202 | he = __hists__add_branch_entry(&evsel->hists, al, parent, | 212 | he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, |
203 | &bi[i], 1, 1); | 213 | 1, 1, 0); |
204 | if (he) { | 214 | if (he) { |
205 | struct annotation *notes; | 215 | struct annotation *notes; |
206 | bx = he->branch_info; | 216 | bx = he->branch_info; |
@@ -242,24 +252,28 @@ out: | |||
242 | return err; | 252 | return err; |
243 | } | 253 | } |
244 | 254 | ||
245 | static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | 255 | static int perf_evsel__add_hist_entry(struct perf_tool *tool, |
256 | struct perf_evsel *evsel, | ||
246 | struct addr_location *al, | 257 | struct addr_location *al, |
247 | struct perf_sample *sample, | 258 | struct perf_sample *sample, |
248 | struct machine *machine) | 259 | struct machine *machine) |
249 | { | 260 | { |
261 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | ||
250 | struct symbol *parent = NULL; | 262 | struct symbol *parent = NULL; |
251 | int err = 0; | 263 | int err = 0; |
252 | struct hist_entry *he; | 264 | struct hist_entry *he; |
253 | 265 | ||
254 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 266 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { |
255 | err = machine__resolve_callchain(machine, evsel, al->thread, | 267 | err = machine__resolve_callchain(machine, evsel, al->thread, |
256 | sample, &parent, al); | 268 | sample, &parent, al, |
269 | rep->max_stack); | ||
257 | if (err) | 270 | if (err) |
258 | return err; | 271 | return err; |
259 | } | 272 | } |
260 | 273 | ||
261 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period, | 274 | he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, |
262 | sample->weight); | 275 | sample->period, sample->weight, |
276 | sample->transaction); | ||
263 | if (he == NULL) | 277 | if (he == NULL) |
264 | return -ENOMEM; | 278 | return -ENOMEM; |
265 | 279 | ||
@@ -330,7 +344,8 @@ static int process_sample_event(struct perf_tool *tool, | |||
330 | if (al.map != NULL) | 344 | if (al.map != NULL) |
331 | al.map->dso->hit = 1; | 345 | al.map->dso->hit = 1; |
332 | 346 | ||
333 | ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine); | 347 | ret = perf_evsel__add_hist_entry(tool, evsel, &al, sample, |
348 | machine); | ||
334 | if (ret < 0) | 349 | if (ret < 0) |
335 | pr_debug("problem incrementing symbol period, skipping event\n"); | 350 | pr_debug("problem incrementing symbol period, skipping event\n"); |
336 | } | 351 | } |
@@ -364,10 +379,11 @@ static int process_read_event(struct perf_tool *tool, | |||
364 | /* For pipe mode, sample_type is not currently set */ | 379 | /* For pipe mode, sample_type is not currently set */ |
365 | static int perf_report__setup_sample_type(struct perf_report *rep) | 380 | static int perf_report__setup_sample_type(struct perf_report *rep) |
366 | { | 381 | { |
367 | struct perf_session *self = rep->session; | 382 | struct perf_session *session = rep->session; |
368 | u64 sample_type = perf_evlist__combined_sample_type(self->evlist); | 383 | u64 sample_type = perf_evlist__combined_sample_type(session->evlist); |
384 | bool is_pipe = perf_data_file__is_pipe(session->file); | ||
369 | 385 | ||
370 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 386 | if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
371 | if (sort__has_parent) { | 387 | if (sort__has_parent) { |
372 | ui__error("Selected --sort parent, but no " | 388 | ui__error("Selected --sort parent, but no " |
373 | "callchain data. Did you call " | 389 | "callchain data. Did you call " |
@@ -390,7 +406,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
390 | } | 406 | } |
391 | 407 | ||
392 | if (sort__mode == SORT_MODE__BRANCH) { | 408 | if (sort__mode == SORT_MODE__BRANCH) { |
393 | if (!self->fd_pipe && | 409 | if (!is_pipe && |
394 | !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { | 410 | !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { |
395 | ui__error("Selected -b but no branch data. " | 411 | ui__error("Selected -b but no branch data. " |
396 | "Did you call perf record without -b?\n"); | 412 | "Did you call perf record without -b?\n"); |
@@ -407,14 +423,14 @@ static void sig_handler(int sig __maybe_unused) | |||
407 | } | 423 | } |
408 | 424 | ||
409 | static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, | 425 | static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, |
410 | struct hists *self, | 426 | struct hists *hists, |
411 | const char *evname, FILE *fp) | 427 | const char *evname, FILE *fp) |
412 | { | 428 | { |
413 | size_t ret; | 429 | size_t ret; |
414 | char unit; | 430 | char unit; |
415 | unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 431 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
416 | u64 nr_events = self->stats.total_period; | 432 | u64 nr_events = hists->stats.total_period; |
417 | struct perf_evsel *evsel = hists_to_evsel(self); | 433 | struct perf_evsel *evsel = hists_to_evsel(hists); |
418 | char buf[512]; | 434 | char buf[512]; |
419 | size_t size = sizeof(buf); | 435 | size_t size = sizeof(buf); |
420 | 436 | ||
@@ -486,6 +502,8 @@ static int __cmd_report(struct perf_report *rep) | |||
486 | struct map *kernel_map; | 502 | struct map *kernel_map; |
487 | struct kmap *kernel_kmap; | 503 | struct kmap *kernel_kmap; |
488 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 504 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
505 | struct ui_progress prog; | ||
506 | struct perf_data_file *file = session->file; | ||
489 | 507 | ||
490 | signal(SIGINT, sig_handler); | 508 | signal(SIGINT, sig_handler); |
491 | 509 | ||
@@ -547,13 +565,19 @@ static int __cmd_report(struct perf_report *rep) | |||
547 | } | 565 | } |
548 | 566 | ||
549 | nr_samples = 0; | 567 | nr_samples = 0; |
568 | list_for_each_entry(pos, &session->evlist->entries, node) | ||
569 | nr_samples += pos->hists.nr_entries; | ||
570 | |||
571 | ui_progress__init(&prog, nr_samples, "Merging related events..."); | ||
572 | |||
573 | nr_samples = 0; | ||
550 | list_for_each_entry(pos, &session->evlist->entries, node) { | 574 | list_for_each_entry(pos, &session->evlist->entries, node) { |
551 | struct hists *hists = &pos->hists; | 575 | struct hists *hists = &pos->hists; |
552 | 576 | ||
553 | if (pos->idx == 0) | 577 | if (pos->idx == 0) |
554 | hists->symbol_filter_str = rep->symbol_filter_str; | 578 | hists->symbol_filter_str = rep->symbol_filter_str; |
555 | 579 | ||
556 | hists__collapse_resort(hists); | 580 | hists__collapse_resort(hists, &prog); |
557 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 581 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
558 | 582 | ||
559 | /* Non-group events are considered as leader */ | 583 | /* Non-group events are considered as leader */ |
@@ -565,12 +589,13 @@ static int __cmd_report(struct perf_report *rep) | |||
565 | hists__link(leader_hists, hists); | 589 | hists__link(leader_hists, hists); |
566 | } | 590 | } |
567 | } | 591 | } |
592 | ui_progress__finish(); | ||
568 | 593 | ||
569 | if (session_done()) | 594 | if (session_done()) |
570 | return 0; | 595 | return 0; |
571 | 596 | ||
572 | if (nr_samples == 0) { | 597 | if (nr_samples == 0) { |
573 | ui__error("The %s file has no samples!\n", session->filename); | 598 | ui__error("The %s file has no samples!\n", file->path); |
574 | return 0; | 599 | return 0; |
575 | } | 600 | } |
576 | 601 | ||
@@ -591,8 +616,19 @@ static int __cmd_report(struct perf_report *rep) | |||
591 | ret = 0; | 616 | ret = 0; |
592 | 617 | ||
593 | } else if (use_browser == 2) { | 618 | } else if (use_browser == 2) { |
594 | perf_evlist__gtk_browse_hists(session->evlist, help, | 619 | int (*hist_browser)(struct perf_evlist *, |
595 | NULL, rep->min_percent); | 620 | const char *, |
621 | struct hist_browser_timer *, | ||
622 | float min_pcnt); | ||
623 | |||
624 | hist_browser = dlsym(perf_gtk_handle, | ||
625 | "perf_evlist__gtk_browse_hists"); | ||
626 | if (hist_browser == NULL) { | ||
627 | ui__error("GTK browser not found!\n"); | ||
628 | return ret; | ||
629 | } | ||
630 | hist_browser(session->evlist, help, NULL, | ||
631 | rep->min_percent); | ||
596 | } | 632 | } |
597 | } else | 633 | } else |
598 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 634 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
@@ -757,6 +793,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
757 | .ordered_samples = true, | 793 | .ordered_samples = true, |
758 | .ordering_requires_timestamps = true, | 794 | .ordering_requires_timestamps = true, |
759 | }, | 795 | }, |
796 | .max_stack = PERF_MAX_STACK_DEPTH, | ||
760 | .pretty_printing_style = "normal", | 797 | .pretty_printing_style = "normal", |
761 | }; | 798 | }; |
762 | const struct option options[] = { | 799 | const struct option options[] = { |
@@ -787,7 +824,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
787 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," | 824 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," |
788 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," | 825 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," |
789 | " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " | 826 | " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " |
790 | "snoop, locked"), | 827 | "snoop, locked, abort, in_tx, transaction"), |
791 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 828 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
792 | "Show sample percentage for different cpu modes"), | 829 | "Show sample percentage for different cpu modes"), |
793 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 830 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
@@ -797,6 +834,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
797 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", | 834 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", |
798 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " | 835 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " |
799 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), | 836 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), |
837 | OPT_INTEGER(0, "max-stack", &report.max_stack, | ||
838 | "Set the maximum stack depth when parsing the callchain, " | ||
839 | "anything beyond the specified depth will be ignored. " | ||
840 | "Default: " __stringify(PERF_MAX_STACK_DEPTH)), | ||
800 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, | 841 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, |
801 | "alias for inverted call graph"), | 842 | "alias for inverted call graph"), |
802 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", | 843 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", |
@@ -845,6 +886,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
845 | "Don't show entries under that percent", parse_percent_limit), | 886 | "Don't show entries under that percent", parse_percent_limit), |
846 | OPT_END() | 887 | OPT_END() |
847 | }; | 888 | }; |
889 | struct perf_data_file file = { | ||
890 | .mode = PERF_DATA_MODE_READ, | ||
891 | }; | ||
848 | 892 | ||
849 | perf_config(perf_report_config, &report); | 893 | perf_config(perf_report_config, &report); |
850 | 894 | ||
@@ -867,16 +911,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
867 | input_name = "perf.data"; | 911 | input_name = "perf.data"; |
868 | } | 912 | } |
869 | 913 | ||
870 | if (strcmp(input_name, "-") != 0) | 914 | file.path = input_name; |
871 | setup_browser(true); | 915 | file.force = report.force; |
872 | else { | ||
873 | use_browser = 0; | ||
874 | perf_hpp__init(); | ||
875 | } | ||
876 | 916 | ||
877 | repeat: | 917 | repeat: |
878 | session = perf_session__new(input_name, O_RDONLY, | 918 | session = perf_session__new(&file, false, &report.tool); |
879 | report.force, false, &report.tool); | ||
880 | if (session == NULL) | 919 | if (session == NULL) |
881 | return -ENOMEM; | 920 | return -ENOMEM; |
882 | 921 | ||
@@ -914,8 +953,22 @@ repeat: | |||
914 | sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; | 953 | sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; |
915 | } | 954 | } |
916 | 955 | ||
917 | if (setup_sorting() < 0) | 956 | if (setup_sorting() < 0) { |
918 | usage_with_options(report_usage, options); | 957 | parse_options_usage(report_usage, options, "s", 1); |
958 | goto error; | ||
959 | } | ||
960 | |||
961 | if (parent_pattern != default_parent_pattern) { | ||
962 | if (sort_dimension__add("parent") < 0) | ||
963 | goto error; | ||
964 | } | ||
965 | |||
966 | if (strcmp(input_name, "-") != 0) | ||
967 | setup_browser(true); | ||
968 | else { | ||
969 | use_browser = 0; | ||
970 | perf_hpp__init(); | ||
971 | } | ||
919 | 972 | ||
920 | /* | 973 | /* |
921 | * Only in the TUI browser we are doing integrated annotation, | 974 | * Only in the TUI browser we are doing integrated annotation, |
@@ -946,11 +999,6 @@ repeat: | |||
946 | if (symbol__init() < 0) | 999 | if (symbol__init() < 0) |
947 | goto error; | 1000 | goto error; |
948 | 1001 | ||
949 | if (parent_pattern != default_parent_pattern) { | ||
950 | if (sort_dimension__add("parent") < 0) | ||
951 | goto error; | ||
952 | } | ||
953 | |||
954 | if (argc) { | 1002 | if (argc) { |
955 | /* | 1003 | /* |
956 | * Special case: if there's an argument left then assume that | 1004 | * Special case: if there's an argument left then assume that |