aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Documentation/perf-record.txt30
-rw-r--r--tools/perf/Documentation/perf-report.txt10
-rw-r--r--tools/perf/builtin-record.c95
-rw-r--r--tools/perf/builtin-report.c178
-rw-r--r--tools/perf/perf.h18
-rw-r--r--tools/perf/util/event.h1
-rw-r--r--tools/perf/util/evsel.c14
-rw-r--r--tools/perf/util/header.c207
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/hist.c122
-rw-r--r--tools/perf/util/hist.h11
-rw-r--r--tools/perf/util/session.c77
-rw-r--r--tools/perf/util/session.h4
-rw-r--r--tools/perf/util/sort.c287
-rw-r--r--tools/perf/util/sort.h11
-rw-r--r--tools/perf/util/symbol.h20
-rw-r--r--tools/perf/util/ui/browsers/hists.c102
17 files changed, 1023 insertions, 166 deletions
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index a5766b4b0125..a1386b2fff00 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -152,6 +152,36 @@ an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must ha
152corresponding events, i.e., they always refer to events defined earlier on the command 152corresponding events, i.e., they always refer to events defined earlier on the command
153line. 153line.
154 154
155-b::
156--branch-any::
157Enable taken branch stack sampling. Any type of taken branch may be sampled.
158This is a shortcut for --branch-filter any. See --branch-filter for more infos.
159
160-j::
161--branch-filter::
162Enable taken branch stack sampling. Each sample captures a series of consecutive
163taken branches. The number of branches captured with each sample depends on the
164underlying hardware, the type of branches of interest, and the executed code.
165It is possible to select the types of branches captured by enabling filters. The
166following filters are defined:
167
168 - any: any type of branches
169 - any_call: any function call or system call
170 - any_ret: any function return or system call return
171 - any_ind: any indirect branch
172 - u: only when the branch target is at the user level
173 - k: only when the branch target is in the kernel
174 - hv: only when the target is at the hypervisor level
175
176+
177The option requires at least one branch type among any, any_call, any_ret, ind_call.
178The privilege levels may be ommitted, in which case, the privilege levels of the associated
179event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege
180levels are subject to permissions. When sampling on multiple events, branch stack sampling
181is enabled for all the sampling events. The sampled branch type is the same for all events.
182The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k
183Note that this feature may not be available on all processors.
184
155SEE ALSO 185SEE ALSO
156-------- 186--------
157linkperf:perf-stat[1], linkperf:perf-list[1] 187linkperf:perf-stat[1], linkperf:perf-list[1]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 9b430e98712e..87feeee8b90c 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -153,6 +153,16 @@ OPTIONS
153 information which may be very large and thus may clutter the display. 153 information which may be very large and thus may clutter the display.
154 It currently includes: cpu and numa topology of the host system. 154 It currently includes: cpu and numa topology of the host system.
155 155
156-b::
157--branch-stack::
158 Use the addresses of sampled taken branches instead of the instruction
159 address to build the histograms. To generate meaningful output, the
160 perf.data file must have been obtained using perf record -b or
161 perf record --branch-filter xxx where xxx is a branch filter option.
162 perf report is able to auto-detect whether a perf.data file contains
163 branch stacks and it will automatically switch to the branch view mode,
164 unless --no-branch-stack is used.
165
156SEE ALSO 166SEE ALSO
157-------- 167--------
158linkperf:perf-stat[1], linkperf:perf-annotate[1] 168linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 75d230fef202..be4e1eee782e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -473,6 +473,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
473 if (!have_tracepoints(&evsel_list->entries)) 473 if (!have_tracepoints(&evsel_list->entries))
474 perf_header__clear_feat(&session->header, HEADER_TRACE_INFO); 474 perf_header__clear_feat(&session->header, HEADER_TRACE_INFO);
475 475
476 if (!rec->opts.branch_stack)
477 perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
478
476 if (!rec->file_new) { 479 if (!rec->file_new) {
477 err = perf_session__read_header(session, output); 480 err = perf_session__read_header(session, output);
478 if (err < 0) 481 if (err < 0)
@@ -638,6 +641,90 @@ out_delete_session:
638 return err; 641 return err;
639} 642}
640 643
644#define BRANCH_OPT(n, m) \
645 { .name = n, .mode = (m) }
646
647#define BRANCH_END { .name = NULL }
648
649struct branch_mode {
650 const char *name;
651 int mode;
652};
653
654static const struct branch_mode branch_modes[] = {
655 BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
656 BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
657 BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
658 BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
659 BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
660 BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
661 BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
662 BRANCH_END
663};
664
665static int
666parse_branch_stack(const struct option *opt, const char *str, int unset)
667{
668#define ONLY_PLM \
669 (PERF_SAMPLE_BRANCH_USER |\
670 PERF_SAMPLE_BRANCH_KERNEL |\
671 PERF_SAMPLE_BRANCH_HV)
672
673 uint64_t *mode = (uint64_t *)opt->value;
674 const struct branch_mode *br;
675 char *s, *os = NULL, *p;
676 int ret = -1;
677
678 if (unset)
679 return 0;
680
681 /*
682 * cannot set it twice, -b + --branch-filter for instance
683 */
684 if (*mode)
685 return -1;
686
687 /* str may be NULL in case no arg is passed to -b */
688 if (str) {
689 /* because str is read-only */
690 s = os = strdup(str);
691 if (!s)
692 return -1;
693
694 for (;;) {
695 p = strchr(s, ',');
696 if (p)
697 *p = '\0';
698
699 for (br = branch_modes; br->name; br++) {
700 if (!strcasecmp(s, br->name))
701 break;
702 }
703 if (!br->name) {
704 ui__warning("unknown branch filter %s,"
705 " check man page\n", s);
706 goto error;
707 }
708
709 *mode |= br->mode;
710
711 if (!p)
712 break;
713
714 s = p + 1;
715 }
716 }
717 ret = 0;
718
719 /* default to any branch */
720 if ((*mode & ~ONLY_PLM) == 0) {
721 *mode = PERF_SAMPLE_BRANCH_ANY;
722 }
723error:
724 free(os);
725 return ret;
726}
727
641static const char * const record_usage[] = { 728static const char * const record_usage[] = {
642 "perf record [<options>] [<command>]", 729 "perf record [<options>] [<command>]",
643 "perf record [<options>] -- <command> [<options>]", 730 "perf record [<options>] -- <command> [<options>]",
@@ -727,6 +814,14 @@ const struct option record_options[] = {
727 "monitor event in cgroup name only", 814 "monitor event in cgroup name only",
728 parse_cgroups), 815 parse_cgroups),
729 OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), 816 OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"),
817
818 OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack,
819 "branch any", "sample any taken branches",
820 parse_branch_stack),
821
822 OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack,
823 "branch filter mask", "branch stack filter modes",
824 parse_branch_stack),
730 OPT_END() 825 OPT_END()
731}; 826};
732 827
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 25d34d483e49..8e91c6eba18a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -53,6 +53,82 @@ struct perf_report {
53 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 53 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
54}; 54};
55 55
56static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
57 struct addr_location *al,
58 struct perf_sample *sample,
59 struct perf_evsel *evsel,
60 struct machine *machine)
61{
62 struct perf_report *rep = container_of(tool, struct perf_report, tool);
63 struct symbol *parent = NULL;
64 int err = 0;
65 unsigned i;
66 struct hist_entry *he;
67 struct branch_info *bi, *bx;
68
69 if ((sort__has_parent || symbol_conf.use_callchain)
70 && sample->callchain) {
71 err = machine__resolve_callchain(machine, evsel, al->thread,
72 sample->callchain, &parent);
73 if (err)
74 return err;
75 }
76
77 bi = machine__resolve_bstack(machine, al->thread,
78 sample->branch_stack);
79 if (!bi)
80 return -ENOMEM;
81
82 for (i = 0; i < sample->branch_stack->nr; i++) {
83 if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
84 continue;
85 /*
86 * The report shows the percentage of total branches captured
87 * and not events sampled. Thus we use a pseudo period of 1.
88 */
89 he = __hists__add_branch_entry(&evsel->hists, al, parent,
90 &bi[i], 1);
91 if (he) {
92 struct annotation *notes;
93 err = -ENOMEM;
94 bx = he->branch_info;
95 if (bx->from.sym && use_browser > 0) {
96 notes = symbol__annotation(bx->from.sym);
97 if (!notes->src
98 && symbol__alloc_hist(bx->from.sym) < 0)
99 goto out;
100
101 err = symbol__inc_addr_samples(bx->from.sym,
102 bx->from.map,
103 evsel->idx,
104 bx->from.al_addr);
105 if (err)
106 goto out;
107 }
108
109 if (bx->to.sym && use_browser > 0) {
110 notes = symbol__annotation(bx->to.sym);
111 if (!notes->src
112 && symbol__alloc_hist(bx->to.sym) < 0)
113 goto out;
114
115 err = symbol__inc_addr_samples(bx->to.sym,
116 bx->to.map,
117 evsel->idx,
118 bx->to.al_addr);
119 if (err)
120 goto out;
121 }
122 evsel->hists.stats.total_period += 1;
123 hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
124 err = 0;
125 } else
126 return -ENOMEM;
127 }
128out:
129 return err;
130}
131
56static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, 132static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
57 struct addr_location *al, 133 struct addr_location *al,
58 struct perf_sample *sample, 134 struct perf_sample *sample,
@@ -126,14 +202,21 @@ static int process_sample_event(struct perf_tool *tool,
126 if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) 202 if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
127 return 0; 203 return 0;
128 204
129 if (al.map != NULL) 205 if (sort__branch_mode == 1) {
130 al.map->dso->hit = 1; 206 if (perf_report__add_branch_hist_entry(tool, &al, sample,
207 evsel, machine)) {
208 pr_debug("problem adding lbr entry, skipping event\n");
209 return -1;
210 }
211 } else {
212 if (al.map != NULL)
213 al.map->dso->hit = 1;
131 214
132 if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) { 215 if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
133 pr_debug("problem incrementing symbol period, skipping event\n"); 216 pr_debug("problem incrementing symbol period, skipping event\n");
134 return -1; 217 return -1;
218 }
135 } 219 }
136
137 return 0; 220 return 0;
138} 221}
139 222
@@ -188,6 +271,15 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
188 } 271 }
189 } 272 }
190 273
274 if (sort__branch_mode == 1) {
275 if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
276 fprintf(stderr, "selected -b but no branch data."
277 " Did you call perf record without"
278 " -b?\n");
279 return -1;
280 }
281 }
282
191 return 0; 283 return 0;
192} 284}
193 285
@@ -246,7 +338,7 @@ static int __cmd_report(struct perf_report *rep)
246{ 338{
247 int ret = -EINVAL; 339 int ret = -EINVAL;
248 u64 nr_samples; 340 u64 nr_samples;
249 struct perf_session *session; 341 struct perf_session *session = rep->session;
250 struct perf_evsel *pos; 342 struct perf_evsel *pos;
251 struct map *kernel_map; 343 struct map *kernel_map;
252 struct kmap *kernel_kmap; 344 struct kmap *kernel_kmap;
@@ -254,13 +346,6 @@ static int __cmd_report(struct perf_report *rep)
254 346
255 signal(SIGINT, sig_handler); 347 signal(SIGINT, sig_handler);
256 348
257 session = perf_session__new(rep->input_name, O_RDONLY,
258 rep->force, false, &rep->tool);
259 if (session == NULL)
260 return -ENOMEM;
261
262 rep->session = session;
263
264 if (rep->cpu_list) { 349 if (rep->cpu_list) {
265 ret = perf_session__cpu_bitmap(session, rep->cpu_list, 350 ret = perf_session__cpu_bitmap(session, rep->cpu_list,
266 rep->cpu_bitmap); 351 rep->cpu_bitmap);
@@ -427,9 +512,19 @@ setup:
427 return 0; 512 return 0;
428} 513}
429 514
515static int
516parse_branch_mode(const struct option *opt __used, const char *str __used, int unset)
517{
518 sort__branch_mode = !unset;
519 return 0;
520}
521
430int cmd_report(int argc, const char **argv, const char *prefix __used) 522int cmd_report(int argc, const char **argv, const char *prefix __used)
431{ 523{
524 struct perf_session *session;
432 struct stat st; 525 struct stat st;
526 bool has_br_stack = false;
527 int ret = -1;
433 char callchain_default_opt[] = "fractal,0.5,callee"; 528 char callchain_default_opt[] = "fractal,0.5,callee";
434 const char * const report_usage[] = { 529 const char * const report_usage[] = {
435 "perf report [<options>]", 530 "perf report [<options>]",
@@ -477,7 +572,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
477 OPT_BOOLEAN(0, "stdio", &report.use_stdio, 572 OPT_BOOLEAN(0, "stdio", &report.use_stdio,
478 "Use the stdio interface"), 573 "Use the stdio interface"),
479 OPT_STRING('s', "sort", &sort_order, "key[,key2...]", 574 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
480 "sort by key(s): pid, comm, dso, symbol, parent"), 575 "sort by key(s): pid, comm, dso, symbol, parent, dso_to,"
576 " dso_from, symbol_to, symbol_from, mispredict"),
481 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, 577 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
482 "Show sample percentage for different cpu modes"), 578 "Show sample percentage for different cpu modes"),
483 OPT_STRING('p', "parent", &parent_pattern, "regex", 579 OPT_STRING('p', "parent", &parent_pattern, "regex",
@@ -517,6 +613,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
517 "Specify disassembler style (e.g. -M intel for intel syntax)"), 613 "Specify disassembler style (e.g. -M intel for intel syntax)"),
518 OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, 614 OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
519 "Show a column with the sum of periods"), 615 "Show a column with the sum of periods"),
616 OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
617 "use branch records for histogram filling", parse_branch_mode),
520 OPT_END() 618 OPT_END()
521 }; 619 };
522 620
@@ -536,11 +634,36 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
536 else 634 else
537 report.input_name = "perf.data"; 635 report.input_name = "perf.data";
538 } 636 }
637 session = perf_session__new(report.input_name, O_RDONLY,
638 report.force, false, &report.tool);
639 if (session == NULL)
640 return -ENOMEM;
539 641
540 if (strcmp(report.input_name, "-") != 0) 642 report.session = session;
643
644 has_br_stack = perf_header__has_feat(&session->header,
645 HEADER_BRANCH_STACK);
646
647 if (sort__branch_mode == -1 && has_br_stack)
648 sort__branch_mode = 1;
649
650 /* sort__branch_mode could be 0 if --no-branch-stack */
651 if (sort__branch_mode == 1) {
652 /*
653 * if no sort_order is provided, then specify
654 * branch-mode specific order
655 */
656 if (sort_order == default_sort_order)
657 sort_order = "comm,dso_from,symbol_from,"
658 "dso_to,symbol_to";
659
660 }
661
662 if (strcmp(report.input_name, "-") != 0) {
541 setup_browser(true); 663 setup_browser(true);
542 else 664 } else {
543 use_browser = 0; 665 use_browser = 0;
666 }
544 667
545 /* 668 /*
546 * Only in the newt browser we are doing integrated annotation, 669 * Only in the newt browser we are doing integrated annotation,
@@ -568,13 +691,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
568 } 691 }
569 692
570 if (symbol__init() < 0) 693 if (symbol__init() < 0)
571 return -1; 694 goto error;
572 695
573 setup_sorting(report_usage, options); 696 setup_sorting(report_usage, options);
574 697
575 if (parent_pattern != default_parent_pattern) { 698 if (parent_pattern != default_parent_pattern) {
576 if (sort_dimension__add("parent") < 0) 699 if (sort_dimension__add("parent") < 0)
577 return -1; 700 goto error;
578 701
579 /* 702 /*
580 * Only show the parent fields if we explicitly 703 * Only show the parent fields if we explicitly
@@ -592,9 +715,20 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
592 if (argc) 715 if (argc)
593 usage_with_options(report_usage, options); 716 usage_with_options(report_usage, options);
594 717
595 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
596 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); 718 sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
597 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
598 719
599 return __cmd_report(&report); 720 if (sort__branch_mode == 1) {
721 sort_entry__setup_elide(&sort_dso_from, symbol_conf.dso_from_list, "dso_from", stdout);
722 sort_entry__setup_elide(&sort_dso_to, symbol_conf.dso_to_list, "dso_to", stdout);
723 sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout);
724 sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout);
725 } else {
726 sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
727 sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
728 }
729
730 ret = __cmd_report(&report);
731error:
732 perf_session__delete(session);
733 return ret;
600} 734}
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index f0227e93665d..eec392e48067 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -179,6 +179,23 @@ struct ip_callchain {
179 u64 ips[0]; 179 u64 ips[0];
180}; 180};
181 181
182struct branch_flags {
183 u64 mispred:1;
184 u64 predicted:1;
185 u64 reserved:62;
186};
187
188struct branch_entry {
189 u64 from;
190 u64 to;
191 struct branch_flags flags;
192};
193
194struct branch_stack {
195 u64 nr;
196 struct branch_entry entries[0];
197};
198
182extern bool perf_host, perf_guest; 199extern bool perf_host, perf_guest;
183extern const char perf_version_string[]; 200extern const char perf_version_string[];
184 201
@@ -205,6 +222,7 @@ struct perf_record_opts {
205 unsigned int freq; 222 unsigned int freq;
206 unsigned int mmap_pages; 223 unsigned int mmap_pages;
207 unsigned int user_freq; 224 unsigned int user_freq;
225 int branch_stack;
208 u64 default_interval; 226 u64 default_interval;
209 u64 user_interval; 227 u64 user_interval;
210 const char *cpu_list; 228 const char *cpu_list;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index cbdeaad9c5e5..1b197280c621 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -81,6 +81,7 @@ struct perf_sample {
81 u32 raw_size; 81 u32 raw_size;
82 void *raw_data; 82 void *raw_data;
83 struct ip_callchain *callchain; 83 struct ip_callchain *callchain;
84 struct branch_stack *branch_stack;
84}; 85};
85 86
86#define BUILD_ID_SIZE 20 87#define BUILD_ID_SIZE 20
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 302d49a9f985..f421f7cbc0d3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -126,6 +126,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
126 attr->watermark = 0; 126 attr->watermark = 0;
127 attr->wakeup_events = 1; 127 attr->wakeup_events = 1;
128 } 128 }
129 if (opts->branch_stack) {
130 attr->sample_type |= PERF_SAMPLE_BRANCH_STACK;
131 attr->branch_sample_type = opts->branch_stack;
132 }
129 133
130 attr->mmap = track; 134 attr->mmap = track;
131 attr->comm = track; 135 attr->comm = track;
@@ -576,6 +580,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
576 data->raw_data = (void *) pdata; 580 data->raw_data = (void *) pdata;
577 } 581 }
578 582
583 if (type & PERF_SAMPLE_BRANCH_STACK) {
584 u64 sz;
585
586 data->branch_stack = (struct branch_stack *)array;
587 array++; /* nr */
588
589 sz = data->branch_stack->nr * sizeof(struct branch_entry);
590 sz /= sizeof(u64);
591 array += sz;
592 }
579 return 0; 593 return 0;
580} 594}
581 595
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 9f867d96c6a5..0d9b6da86a39 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1023,6 +1023,12 @@ write_it:
1023 return do_write_string(fd, buffer); 1023 return do_write_string(fd, buffer);
1024} 1024}
1025 1025
1026static int write_branch_stack(int fd __used, struct perf_header *h __used,
1027 struct perf_evlist *evlist __used)
1028{
1029 return 0;
1030}
1031
1026static void print_hostname(struct perf_header *ph, int fd, FILE *fp) 1032static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
1027{ 1033{
1028 char *str = do_read_string(fd, ph); 1034 char *str = do_read_string(fd, ph);
@@ -1144,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
1144 uint64_t id; 1150 uint64_t id;
1145 void *buf = NULL; 1151 void *buf = NULL;
1146 char *str; 1152 char *str;
1147 u32 nre, sz, nr, i, j, msz; 1153 u32 nre, sz, nr, i, j;
1148 int ret; 1154 ssize_t ret;
1155 size_t msz;
1149 1156
1150 /* number of events */ 1157 /* number of events */
1151 ret = read(fd, &nre, sizeof(nre)); 1158 ret = read(fd, &nre, sizeof(nre));
@@ -1162,25 +1169,23 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
1162 if (ph->needs_swap) 1169 if (ph->needs_swap)
1163 sz = bswap_32(sz); 1170 sz = bswap_32(sz);
1164 1171
1165 /*
1166 * ensure it is at least to our ABI rev
1167 */
1168 if (sz < (u32)sizeof(attr))
1169 goto error;
1170
1171 memset(&attr, 0, sizeof(attr)); 1172 memset(&attr, 0, sizeof(attr));
1172 1173
1173 /* read entire region to sync up to next field */ 1174 /* buffer to hold on file attr struct */
1174 buf = malloc(sz); 1175 buf = malloc(sz);
1175 if (!buf) 1176 if (!buf)
1176 goto error; 1177 goto error;
1177 1178
1178 msz = sizeof(attr); 1179 msz = sizeof(attr);
1179 if (sz < msz) 1180 if (sz < (ssize_t)msz)
1180 msz = sz; 1181 msz = sz;
1181 1182
1182 for (i = 0 ; i < nre; i++) { 1183 for (i = 0 ; i < nre; i++) {
1183 1184
1185 /*
1186 * must read entire on-file attr struct to
1187 * sync up with layout.
1188 */
1184 ret = read(fd, buf, sz); 1189 ret = read(fd, buf, sz);
1185 if (ret != (ssize_t)sz) 1190 if (ret != (ssize_t)sz)
1186 goto error; 1191 goto error;
@@ -1316,6 +1321,12 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
1316 free(str); 1321 free(str);
1317} 1322}
1318 1323
1324static void print_branch_stack(struct perf_header *ph __used, int fd __used,
1325 FILE *fp)
1326{
1327 fprintf(fp, "# contains samples with branch stack\n");
1328}
1329
1319static int __event_process_build_id(struct build_id_event *bev, 1330static int __event_process_build_id(struct build_id_event *bev,
1320 char *filename, 1331 char *filename,
1321 struct perf_session *session) 1332 struct perf_session *session)
@@ -1520,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
1520 FEAT_OPA(HEADER_CMDLINE, cmdline), 1531 FEAT_OPA(HEADER_CMDLINE, cmdline),
1521 FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), 1532 FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
1522 FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), 1533 FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
1534 FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
1523}; 1535};
1524 1536
1525struct header_print_data { 1537struct header_print_data {
@@ -1804,35 +1816,101 @@ out_free:
1804 return err; 1816 return err;
1805} 1817}
1806 1818
1807static int check_magic_endian(u64 *magic, struct perf_file_header *header, 1819static const int attr_file_abi_sizes[] = {
1808 struct perf_header *ph) 1820 [0] = PERF_ATTR_SIZE_VER0,
1821 [1] = PERF_ATTR_SIZE_VER1,
1822 0,
1823};
1824
1825/*
1826 * In the legacy file format, the magic number is not used to encode endianness.
1827 * hdr_sz was used to encode endianness. But given that hdr_sz can vary based
1828 * on ABI revisions, we need to try all combinations for all endianness to
1829 * detect the endianness.
1830 */
1831static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph)
1809{ 1832{
1810 int ret; 1833 uint64_t ref_size, attr_size;
1834 int i;
1811 1835
1812 /* check for legacy format */ 1836 for (i = 0 ; attr_file_abi_sizes[i]; i++) {
1813 ret = memcmp(magic, __perf_magic1, sizeof(*magic)); 1837 ref_size = attr_file_abi_sizes[i]
1814 if (ret == 0) { 1838 + sizeof(struct perf_file_section);
1815 pr_debug("legacy perf.data format\n"); 1839 if (hdr_sz != ref_size) {
1816 if (!header) 1840 attr_size = bswap_64(hdr_sz);
1817 return -1; 1841 if (attr_size != ref_size)
1842 continue;
1818 1843
1819 if (header->attr_size != sizeof(struct perf_file_attr)) { 1844 ph->needs_swap = true;
1820 u64 attr_size = bswap_64(header->attr_size); 1845 }
1846 pr_debug("ABI%d perf.data file detected, need_swap=%d\n",
1847 i,
1848 ph->needs_swap);
1849 return 0;
1850 }
1851 /* could not determine endianness */
1852 return -1;
1853}
1821 1854
1822 if (attr_size != sizeof(struct perf_file_attr)) 1855#define PERF_PIPE_HDR_VER0 16
1823 return -1; 1856
1857static const size_t attr_pipe_abi_sizes[] = {
1858 [0] = PERF_PIPE_HDR_VER0,
1859 0,
1860};
1861
1862/*
1863 * In the legacy pipe format, there is an implicit assumption that endiannesss
1864 * between host recording the samples, and host parsing the samples is the
1865 * same. This is not always the case given that the pipe output may always be
1866 * redirected into a file and analyzed on a different machine with possibly a
1867 * different endianness and perf_event ABI revsions in the perf tool itself.
1868 */
1869static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph)
1870{
1871 u64 attr_size;
1872 int i;
1873
1874 for (i = 0 ; attr_pipe_abi_sizes[i]; i++) {
1875 if (hdr_sz != attr_pipe_abi_sizes[i]) {
1876 attr_size = bswap_64(hdr_sz);
1877 if (attr_size != hdr_sz)
1878 continue;
1824 1879
1825 ph->needs_swap = true; 1880 ph->needs_swap = true;
1826 } 1881 }
1882 pr_debug("Pipe ABI%d perf.data file detected\n", i);
1827 return 0; 1883 return 0;
1828 } 1884 }
1885 return -1;
1886}
1887
1888static int check_magic_endian(u64 magic, uint64_t hdr_sz,
1889 bool is_pipe, struct perf_header *ph)
1890{
1891 int ret;
1892
1893 /* check for legacy format */
1894 ret = memcmp(&magic, __perf_magic1, sizeof(magic));
1895 if (ret == 0) {
1896 pr_debug("legacy perf.data format\n");
1897 if (is_pipe)
1898 return try_all_pipe_abis(hdr_sz, ph);
1899
1900 return try_all_file_abis(hdr_sz, ph);
1901 }
1902 /*
1903 * the new magic number serves two purposes:
1904 * - unique number to identify actual perf.data files
1905 * - encode endianness of file
1906 */
1829 1907
1830 /* check magic number with same endianness */ 1908 /* check magic number with one endianness */
1831 if (*magic == __perf_magic2) 1909 if (magic == __perf_magic2)
1832 return 0; 1910 return 0;
1833 1911
1834 /* check magic number but opposite endianness */ 1912 /* check magic number with opposite endianness */
1835 if (*magic != __perf_magic2_sw) 1913 if (magic != __perf_magic2_sw)
1836 return -1; 1914 return -1;
1837 1915
1838 ph->needs_swap = true; 1916 ph->needs_swap = true;
@@ -1851,8 +1929,11 @@ int perf_file_header__read(struct perf_file_header *header,
1851 if (ret <= 0) 1929 if (ret <= 0)
1852 return -1; 1930 return -1;
1853 1931
1854 if (check_magic_endian(&header->magic, header, ph) < 0) 1932 if (check_magic_endian(header->magic,
1933 header->attr_size, false, ph) < 0) {
1934 pr_debug("magic/endian check failed\n");
1855 return -1; 1935 return -1;
1936 }
1856 1937
1857 if (ph->needs_swap) { 1938 if (ph->needs_swap) {
1858 mem_bswap_64(header, offsetof(struct perf_file_header, 1939 mem_bswap_64(header, offsetof(struct perf_file_header,
@@ -1939,21 +2020,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
1939 if (ret <= 0) 2020 if (ret <= 0)
1940 return -1; 2021 return -1;
1941 2022
1942 if (check_magic_endian(&header->magic, NULL, ph) < 0) 2023 if (check_magic_endian(header->magic, header->size, true, ph) < 0) {
2024 pr_debug("endian/magic failed\n");
1943 return -1; 2025 return -1;
2026 }
2027
2028 if (ph->needs_swap)
2029 header->size = bswap_64(header->size);
1944 2030
1945 if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) 2031 if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0)
1946 return -1; 2032 return -1;
1947 2033
1948 if (header->size != sizeof(*header)) {
1949 u64 size = bswap_64(header->size);
1950
1951 if (size != sizeof(*header))
1952 return -1;
1953
1954 ph->needs_swap = true;
1955 }
1956
1957 return 0; 2034 return 0;
1958} 2035}
1959 2036
@@ -1973,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
1973 return 0; 2050 return 0;
1974} 2051}
1975 2052
2053static int read_attr(int fd, struct perf_header *ph,
2054 struct perf_file_attr *f_attr)
2055{
2056 struct perf_event_attr *attr = &f_attr->attr;
2057 size_t sz, left;
2058 size_t our_sz = sizeof(f_attr->attr);
2059 int ret;
2060
2061 memset(f_attr, 0, sizeof(*f_attr));
2062
2063 /* read minimal guaranteed structure */
2064 ret = readn(fd, attr, PERF_ATTR_SIZE_VER0);
2065 if (ret <= 0) {
2066 pr_debug("cannot read %d bytes of header attr\n",
2067 PERF_ATTR_SIZE_VER0);
2068 return -1;
2069 }
2070
2071 /* on file perf_event_attr size */
2072 sz = attr->size;
2073
2074 if (ph->needs_swap)
2075 sz = bswap_32(sz);
2076
2077 if (sz == 0) {
2078 /* assume ABI0 */
2079 sz = PERF_ATTR_SIZE_VER0;
2080 } else if (sz > our_sz) {
2081 pr_debug("file uses a more recent and unsupported ABI"
2082 " (%zu bytes extra)\n", sz - our_sz);
2083 return -1;
2084 }
2085 /* what we have not yet read and that we know about */
2086 left = sz - PERF_ATTR_SIZE_VER0;
2087 if (left) {
2088 void *ptr = attr;
2089 ptr += PERF_ATTR_SIZE_VER0;
2090
2091 ret = readn(fd, ptr, left);
2092 }
2093 /* read perf_file_section, ids are read in caller */
2094 ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids));
2095
2096 return ret <= 0 ? -1 : 0;
2097}
2098
1976int perf_session__read_header(struct perf_session *session, int fd) 2099int perf_session__read_header(struct perf_session *session, int fd)
1977{ 2100{
1978 struct perf_header *header = &session->header; 2101 struct perf_header *header = &session->header;
@@ -1988,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd)
1988 if (session->fd_pipe) 2111 if (session->fd_pipe)
1989 return perf_header__read_pipe(session, fd); 2112 return perf_header__read_pipe(session, fd);
1990 2113
1991 if (perf_file_header__read(&f_header, header, fd) < 0) { 2114 if (perf_file_header__read(&f_header, header, fd) < 0)
1992 pr_debug("incompatible file format\n");
1993 return -EINVAL; 2115 return -EINVAL;
1994 }
1995 2116
1996 nr_attrs = f_header.attrs.size / sizeof(f_attr); 2117 nr_attrs = f_header.attrs.size / f_header.attr_size;
1997 lseek(fd, f_header.attrs.offset, SEEK_SET); 2118 lseek(fd, f_header.attrs.offset, SEEK_SET);
1998 2119
1999 for (i = 0; i < nr_attrs; i++) { 2120 for (i = 0; i < nr_attrs; i++) {
2000 struct perf_evsel *evsel; 2121 struct perf_evsel *evsel;
2001 off_t tmp; 2122 off_t tmp;
2002 2123
2003 if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) 2124 if (read_attr(fd, header, &f_attr) < 0)
2004 goto out_errno; 2125 goto out_errno;
2005 2126
2006 if (header->needs_swap) 2127 if (header->needs_swap)
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index e68f617d082f..21a6be09c129 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -27,7 +27,7 @@ enum {
27 HEADER_EVENT_DESC, 27 HEADER_EVENT_DESC,
28 HEADER_CPU_TOPOLOGY, 28 HEADER_CPU_TOPOLOGY,
29 HEADER_NUMA_TOPOLOGY, 29 HEADER_NUMA_TOPOLOGY,
30 30 HEADER_BRANCH_STACK,
31 HEADER_LAST_FEATURE, 31 HEADER_LAST_FEATURE,
32 HEADER_FEAT_BITS = 256, 32 HEADER_FEAT_BITS = 256,
33}; 33};
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 6f505d1abac7..8380c3db1c92 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists)
50 hists__set_col_len(hists, col, 0); 50 hists__set_col_len(hists, col, 0);
51} 51}
52 52
53static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
54{
55 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
56
57 if (hists__col_len(hists, dso) < unresolved_col_width &&
58 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
59 !symbol_conf.dso_list)
60 hists__set_col_len(hists, dso, unresolved_col_width);
61}
62
53static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) 63static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
54{ 64{
65 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
55 u16 len; 66 u16 len;
56 67
57 if (h->ms.sym) 68 if (h->ms.sym)
58 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); 69 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
59 else { 70 else
60 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 71 hists__set_unres_dso_col_len(hists, HISTC_DSO);
61
62 if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
63 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
64 !symbol_conf.dso_list)
65 hists__set_col_len(hists, HISTC_DSO,
66 unresolved_col_width);
67 }
68 72
69 len = thread__comm_len(h->thread); 73 len = thread__comm_len(h->thread);
70 if (hists__new_col_len(hists, HISTC_COMM, len)) 74 if (hists__new_col_len(hists, HISTC_COMM, len))
@@ -74,6 +78,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
74 len = dso__name_len(h->ms.map->dso); 78 len = dso__name_len(h->ms.map->dso);
75 hists__new_col_len(hists, HISTC_DSO, len); 79 hists__new_col_len(hists, HISTC_DSO, len);
76 } 80 }
81
82 if (h->branch_info) {
83 int symlen;
84 /*
85 * +4 accounts for '[x] ' priv level info
86 * +2 account of 0x prefix on raw addresses
87 */
88 if (h->branch_info->from.sym) {
89 symlen = (int)h->branch_info->from.sym->namelen + 4;
90 hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
91
92 symlen = dso__name_len(h->branch_info->from.map->dso);
93 hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
94 } else {
95 symlen = unresolved_col_width + 4 + 2;
96 hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
97 hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
98 }
99
100 if (h->branch_info->to.sym) {
101 symlen = (int)h->branch_info->to.sym->namelen + 4;
102 hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
103
104 symlen = dso__name_len(h->branch_info->to.map->dso);
105 hists__new_col_len(hists, HISTC_DSO_TO, symlen);
106 } else {
107 symlen = unresolved_col_width + 4 + 2;
108 hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
109 hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
110 }
111 }
77} 112}
78 113
79static void hist_entry__add_cpumode_period(struct hist_entry *he, 114static void hist_entry__add_cpumode_period(struct hist_entry *he,
@@ -195,26 +230,14 @@ static u8 symbol__parent_filter(const struct symbol *parent)
195 return 0; 230 return 0;
196} 231}
197 232
198struct hist_entry *__hists__add_entry(struct hists *hists, 233static struct hist_entry *add_hist_entry(struct hists *hists,
234 struct hist_entry *entry,
199 struct addr_location *al, 235 struct addr_location *al,
200 struct symbol *sym_parent, u64 period) 236 u64 period)
201{ 237{
202 struct rb_node **p; 238 struct rb_node **p;
203 struct rb_node *parent = NULL; 239 struct rb_node *parent = NULL;
204 struct hist_entry *he; 240 struct hist_entry *he;
205 struct hist_entry entry = {
206 .thread = al->thread,
207 .ms = {
208 .map = al->map,
209 .sym = al->sym,
210 },
211 .cpu = al->cpu,
212 .ip = al->addr,
213 .level = al->level,
214 .period = period,
215 .parent = sym_parent,
216 .filtered = symbol__parent_filter(sym_parent),
217 };
218 int cmp; 241 int cmp;
219 242
220 pthread_mutex_lock(&hists->lock); 243 pthread_mutex_lock(&hists->lock);
@@ -225,7 +248,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
225 parent = *p; 248 parent = *p;
226 he = rb_entry(parent, struct hist_entry, rb_node_in); 249 he = rb_entry(parent, struct hist_entry, rb_node_in);
227 250
228 cmp = hist_entry__cmp(&entry, he); 251 cmp = hist_entry__cmp(entry, he);
229 252
230 if (!cmp) { 253 if (!cmp) {
231 he->period += period; 254 he->period += period;
@@ -239,7 +262,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
239 p = &(*p)->rb_right; 262 p = &(*p)->rb_right;
240 } 263 }
241 264
242 he = hist_entry__new(&entry); 265 he = hist_entry__new(entry);
243 if (!he) 266 if (!he)
244 goto out_unlock; 267 goto out_unlock;
245 268
@@ -252,6 +275,51 @@ out_unlock:
252 return he; 275 return he;
253} 276}
254 277
278struct hist_entry *__hists__add_branch_entry(struct hists *self,
279 struct addr_location *al,
280 struct symbol *sym_parent,
281 struct branch_info *bi,
282 u64 period)
283{
284 struct hist_entry entry = {
285 .thread = al->thread,
286 .ms = {
287 .map = bi->to.map,
288 .sym = bi->to.sym,
289 },
290 .cpu = al->cpu,
291 .ip = bi->to.addr,
292 .level = al->level,
293 .period = period,
294 .parent = sym_parent,
295 .filtered = symbol__parent_filter(sym_parent),
296 .branch_info = bi,
297 };
298
299 return add_hist_entry(self, &entry, al, period);
300}
301
302struct hist_entry *__hists__add_entry(struct hists *self,
303 struct addr_location *al,
304 struct symbol *sym_parent, u64 period)
305{
306 struct hist_entry entry = {
307 .thread = al->thread,
308 .ms = {
309 .map = al->map,
310 .sym = al->sym,
311 },
312 .cpu = al->cpu,
313 .ip = al->addr,
314 .level = al->level,
315 .period = period,
316 .parent = sym_parent,
317 .filtered = symbol__parent_filter(sym_parent),
318 };
319
320 return add_hist_entry(self, &entry, al, period);
321}
322
255int64_t 323int64_t
256hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) 324hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
257{ 325{
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 48e5acd1e862..9413f3e31fea 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -42,6 +42,11 @@ enum hist_column {
42 HISTC_COMM, 42 HISTC_COMM,
43 HISTC_PARENT, 43 HISTC_PARENT,
44 HISTC_CPU, 44 HISTC_CPU,
45 HISTC_MISPREDICT,
46 HISTC_SYMBOL_FROM,
47 HISTC_SYMBOL_TO,
48 HISTC_DSO_FROM,
49 HISTC_DSO_TO,
45 HISTC_NR_COLS, /* Last entry */ 50 HISTC_NR_COLS, /* Last entry */
46}; 51};
47 52
@@ -74,6 +79,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
74 struct hists *hists); 79 struct hists *hists);
75void hist_entry__free(struct hist_entry *); 80void hist_entry__free(struct hist_entry *);
76 81
82struct hist_entry *__hists__add_branch_entry(struct hists *self,
83 struct addr_location *al,
84 struct symbol *sym_parent,
85 struct branch_info *bi,
86 u64 period);
87
77void hists__output_resort(struct hists *self); 88void hists__output_resort(struct hists *self);
78void hists__output_resort_threaded(struct hists *hists); 89void hists__output_resort_threaded(struct hists *hists);
79void hists__collapse_resort(struct hists *self); 90void hists__collapse_resort(struct hists *self);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 9f833cf9c6a9..002ebbf59f48 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force)
24 self->fd = STDIN_FILENO; 24 self->fd = STDIN_FILENO;
25 25
26 if (perf_session__read_header(self, self->fd) < 0) 26 if (perf_session__read_header(self, self->fd) < 0)
27 pr_err("incompatible file format"); 27 pr_err("incompatible file format (rerun with -v to learn more)");
28 28
29 return 0; 29 return 0;
30 } 30 }
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force)
56 } 56 }
57 57
58 if (perf_session__read_header(self, self->fd) < 0) { 58 if (perf_session__read_header(self, self->fd) < 0) {
59 pr_err("incompatible file format"); 59 pr_err("incompatible file format (rerun with -v to learn more)");
60 goto out_close; 60 goto out_close;
61 } 61 }
62 62
@@ -229,6 +229,64 @@ static bool symbol__match_parent_regex(struct symbol *sym)
229 return 0; 229 return 0;
230} 230}
231 231
232static const u8 cpumodes[] = {
233 PERF_RECORD_MISC_USER,
234 PERF_RECORD_MISC_KERNEL,
235 PERF_RECORD_MISC_GUEST_USER,
236 PERF_RECORD_MISC_GUEST_KERNEL
237};
238#define NCPUMODES (sizeof(cpumodes)/sizeof(u8))
239
240static void ip__resolve_ams(struct machine *self, struct thread *thread,
241 struct addr_map_symbol *ams,
242 u64 ip)
243{
244 struct addr_location al;
245 size_t i;
246 u8 m;
247
248 memset(&al, 0, sizeof(al));
249
250 for (i = 0; i < NCPUMODES; i++) {
251 m = cpumodes[i];
252 /*
253 * We cannot use the header.misc hint to determine whether a
254 * branch stack address is user, kernel, guest, hypervisor.
255 * Branches may straddle the kernel/user/hypervisor boundaries.
256 * Thus, we have to try consecutively until we find a match
257 * or else, the symbol is unknown
258 */
259 thread__find_addr_location(thread, self, m, MAP__FUNCTION,
260 ip, &al, NULL);
261 if (al.sym)
262 goto found;
263 }
264found:
265 ams->addr = ip;
266 ams->al_addr = al.addr;
267 ams->sym = al.sym;
268 ams->map = al.map;
269}
270
271struct branch_info *machine__resolve_bstack(struct machine *self,
272 struct thread *thr,
273 struct branch_stack *bs)
274{
275 struct branch_info *bi;
276 unsigned int i;
277
278 bi = calloc(bs->nr, sizeof(struct branch_info));
279 if (!bi)
280 return NULL;
281
282 for (i = 0; i < bs->nr; i++) {
283 ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);
284 ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);
285 bi[i].flags = bs->entries[i].flags;
286 }
287 return bi;
288}
289
232int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, 290int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
233 struct thread *thread, 291 struct thread *thread,
234 struct ip_callchain *chain, 292 struct ip_callchain *chain,
@@ -697,6 +755,18 @@ static void callchain__printf(struct perf_sample *sample)
697 i, sample->callchain->ips[i]); 755 i, sample->callchain->ips[i]);
698} 756}
699 757
758static void branch_stack__printf(struct perf_sample *sample)
759{
760 uint64_t i;
761
762 printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
763
764 for (i = 0; i < sample->branch_stack->nr; i++)
765 printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n",
766 i, sample->branch_stack->entries[i].from,
767 sample->branch_stack->entries[i].to);
768}
769
700static void perf_session__print_tstamp(struct perf_session *session, 770static void perf_session__print_tstamp(struct perf_session *session,
701 union perf_event *event, 771 union perf_event *event,
702 struct perf_sample *sample) 772 struct perf_sample *sample)
@@ -744,6 +814,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
744 814
745 if (session->sample_type & PERF_SAMPLE_CALLCHAIN) 815 if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
746 callchain__printf(sample); 816 callchain__printf(sample);
817
818 if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
819 branch_stack__printf(sample);
747} 820}
748 821
749static struct machine * 822static struct machine *
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index c8d90178e7de..7a5434c00565 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel
73 struct ip_callchain *chain, 73 struct ip_callchain *chain,
74 struct symbol **parent); 74 struct symbol **parent);
75 75
76struct branch_info *machine__resolve_bstack(struct machine *self,
77 struct thread *thread,
78 struct branch_stack *bs);
79
76bool perf_session__has_traces(struct perf_session *self, const char *msg); 80bool perf_session__has_traces(struct perf_session *self, const char *msg);
77 81
78void mem_bswap_64(void *src, int byte_size); 82void mem_bswap_64(void *src, int byte_size);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 16da30d8d765..88dbcf6f9575 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -8,6 +8,7 @@ const char default_sort_order[] = "comm,dso,symbol";
8const char *sort_order = default_sort_order; 8const char *sort_order = default_sort_order;
9int sort__need_collapse = 0; 9int sort__need_collapse = 0;
10int sort__has_parent = 0; 10int sort__has_parent = 0;
11int sort__branch_mode = -1; /* -1 = means not set */
11 12
12enum sort_type sort__first_dimension; 13enum sort_type sort__first_dimension;
13 14
@@ -94,6 +95,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
94 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); 95 return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
95} 96}
96 97
98static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
99{
100 struct dso *dso_l = map_l ? map_l->dso : NULL;
101 struct dso *dso_r = map_r ? map_r->dso : NULL;
102 const char *dso_name_l, *dso_name_r;
103
104 if (!dso_l || !dso_r)
105 return cmp_null(dso_l, dso_r);
106
107 if (verbose) {
108 dso_name_l = dso_l->long_name;
109 dso_name_r = dso_r->long_name;
110 } else {
111 dso_name_l = dso_l->short_name;
112 dso_name_r = dso_r->short_name;
113 }
114
115 return strcmp(dso_name_l, dso_name_r);
116}
117
97struct sort_entry sort_comm = { 118struct sort_entry sort_comm = {
98 .se_header = "Command", 119 .se_header = "Command",
99 .se_cmp = sort__comm_cmp, 120 .se_cmp = sort__comm_cmp,
@@ -107,36 +128,74 @@ struct sort_entry sort_comm = {
107static int64_t 128static int64_t
108sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) 129sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
109{ 130{
110 struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; 131 return _sort__dso_cmp(left->ms.map, right->ms.map);
111 struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; 132}
112 const char *dso_name_l, *dso_name_r;
113 133
114 if (!dso_l || !dso_r)
115 return cmp_null(dso_l, dso_r);
116 134
117 if (verbose) { 135static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
118 dso_name_l = dso_l->long_name; 136 u64 ip_l, u64 ip_r)
119 dso_name_r = dso_r->long_name; 137{
120 } else { 138 if (!sym_l || !sym_r)
121 dso_name_l = dso_l->short_name; 139 return cmp_null(sym_l, sym_r);
122 dso_name_r = dso_r->short_name; 140
141 if (sym_l == sym_r)
142 return 0;
143
144 if (sym_l)
145 ip_l = sym_l->start;
146 if (sym_r)
147 ip_r = sym_r->start;
148
149 return (int64_t)(ip_r - ip_l);
150}
151
152static int _hist_entry__dso_snprintf(struct map *map, char *bf,
153 size_t size, unsigned int width)
154{
155 if (map && map->dso) {
156 const char *dso_name = !verbose ? map->dso->short_name :
157 map->dso->long_name;
158 return repsep_snprintf(bf, size, "%-*s", width, dso_name);
123 } 159 }
124 160
125 return strcmp(dso_name_l, dso_name_r); 161 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
126} 162}
127 163
128static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, 164static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
129 size_t size, unsigned int width) 165 size_t size, unsigned int width)
130{ 166{
131 if (self->ms.map && self->ms.map->dso) { 167 return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
132 const char *dso_name = !verbose ? self->ms.map->dso->short_name : 168}
133 self->ms.map->dso->long_name; 169
134 return repsep_snprintf(bf, size, "%-*s", width, dso_name); 170static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
171 u64 ip, char level, char *bf, size_t size,
172 unsigned int width __used)
173{
174 size_t ret = 0;
175
176 if (verbose) {
177 char o = map ? dso__symtab_origin(map->dso) : '!';
178 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
179 BITS_PER_LONG / 4, ip, o);
135 } 180 }
136 181
137 return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); 182 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
183 if (sym)
184 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
185 width - ret,
186 sym->name);
187 else {
188 size_t len = BITS_PER_LONG / 4;
189 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
190 len, ip);
191 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
192 width - ret, "");
193 }
194
195 return ret;
138} 196}
139 197
198
140struct sort_entry sort_dso = { 199struct sort_entry sort_dso = {
141 .se_header = "Shared Object", 200 .se_header = "Shared Object",
142 .se_cmp = sort__dso_cmp, 201 .se_cmp = sort__dso_cmp,
@@ -144,8 +203,14 @@ struct sort_entry sort_dso = {
144 .se_width_idx = HISTC_DSO, 203 .se_width_idx = HISTC_DSO,
145}; 204};
146 205
147/* --sort symbol */ 206static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
207 size_t size, unsigned int width __used)
208{
209 return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
210 self->level, bf, size, width);
211}
148 212
213/* --sort symbol */
149static int64_t 214static int64_t
150sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) 215sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
151{ 216{
@@ -163,31 +228,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
163 ip_l = left->ms.sym->start; 228 ip_l = left->ms.sym->start;
164 ip_r = right->ms.sym->start; 229 ip_r = right->ms.sym->start;
165 230
166 return (int64_t)(ip_r - ip_l); 231 return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
167}
168
169static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
170 size_t size, unsigned int width __used)
171{
172 size_t ret = 0;
173
174 if (verbose) {
175 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
176 ret += repsep_snprintf(bf, size, "%-#*llx %c ",
177 BITS_PER_LONG / 4, self->ip, o);
178 }
179
180 if (!sort_dso.elide)
181 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
182
183 if (self->ms.sym)
184 ret += repsep_snprintf(bf + ret, size - ret, "%s",
185 self->ms.sym->name);
186 else
187 ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
188 BITS_PER_LONG / 4, self->ip);
189
190 return ret;
191} 232}
192 233
193struct sort_entry sort_sym = { 234struct sort_entry sort_sym = {
@@ -246,19 +287,155 @@ struct sort_entry sort_cpu = {
246 .se_width_idx = HISTC_CPU, 287 .se_width_idx = HISTC_CPU,
247}; 288};
248 289
290static int64_t
291sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
292{
293 return _sort__dso_cmp(left->branch_info->from.map,
294 right->branch_info->from.map);
295}
296
297static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
298 size_t size, unsigned int width)
299{
300 return _hist_entry__dso_snprintf(self->branch_info->from.map,
301 bf, size, width);
302}
303
304struct sort_entry sort_dso_from = {
305 .se_header = "Source Shared Object",
306 .se_cmp = sort__dso_from_cmp,
307 .se_snprintf = hist_entry__dso_from_snprintf,
308 .se_width_idx = HISTC_DSO_FROM,
309};
310
311static int64_t
312sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
313{
314 return _sort__dso_cmp(left->branch_info->to.map,
315 right->branch_info->to.map);
316}
317
318static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
319 size_t size, unsigned int width)
320{
321 return _hist_entry__dso_snprintf(self->branch_info->to.map,
322 bf, size, width);
323}
324
325static int64_t
326sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
327{
328 struct addr_map_symbol *from_l = &left->branch_info->from;
329 struct addr_map_symbol *from_r = &right->branch_info->from;
330
331 if (!from_l->sym && !from_r->sym)
332 return right->level - left->level;
333
334 return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
335 from_r->addr);
336}
337
338static int64_t
339sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
340{
341 struct addr_map_symbol *to_l = &left->branch_info->to;
342 struct addr_map_symbol *to_r = &right->branch_info->to;
343
344 if (!to_l->sym && !to_r->sym)
345 return right->level - left->level;
346
347 return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
348}
349
350static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
351 size_t size, unsigned int width __used)
352{
353 struct addr_map_symbol *from = &self->branch_info->from;
354 return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
355 self->level, bf, size, width);
356
357}
358
359static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
360 size_t size, unsigned int width __used)
361{
362 struct addr_map_symbol *to = &self->branch_info->to;
363 return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
364 self->level, bf, size, width);
365
366}
367
368struct sort_entry sort_dso_to = {
369 .se_header = "Target Shared Object",
370 .se_cmp = sort__dso_to_cmp,
371 .se_snprintf = hist_entry__dso_to_snprintf,
372 .se_width_idx = HISTC_DSO_TO,
373};
374
375struct sort_entry sort_sym_from = {
376 .se_header = "Source Symbol",
377 .se_cmp = sort__sym_from_cmp,
378 .se_snprintf = hist_entry__sym_from_snprintf,
379 .se_width_idx = HISTC_SYMBOL_FROM,
380};
381
382struct sort_entry sort_sym_to = {
383 .se_header = "Target Symbol",
384 .se_cmp = sort__sym_to_cmp,
385 .se_snprintf = hist_entry__sym_to_snprintf,
386 .se_width_idx = HISTC_SYMBOL_TO,
387};
388
389static int64_t
390sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
391{
392 const unsigned char mp = left->branch_info->flags.mispred !=
393 right->branch_info->flags.mispred;
394 const unsigned char p = left->branch_info->flags.predicted !=
395 right->branch_info->flags.predicted;
396
397 return mp || p;
398}
399
400static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
401 size_t size, unsigned int width){
402 static const char *out = "N/A";
403
404 if (self->branch_info->flags.predicted)
405 out = "N";
406 else if (self->branch_info->flags.mispred)
407 out = "Y";
408
409 return repsep_snprintf(bf, size, "%-*s", width, out);
410}
411
412struct sort_entry sort_mispredict = {
413 .se_header = "Branch Mispredicted",
414 .se_cmp = sort__mispredict_cmp,
415 .se_snprintf = hist_entry__mispredict_snprintf,
416 .se_width_idx = HISTC_MISPREDICT,
417};
418
249struct sort_dimension { 419struct sort_dimension {
250 const char *name; 420 const char *name;
251 struct sort_entry *entry; 421 struct sort_entry *entry;
252 int taken; 422 int taken;
253}; 423};
254 424
425#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
426
255static struct sort_dimension sort_dimensions[] = { 427static struct sort_dimension sort_dimensions[] = {
256 { .name = "pid", .entry = &sort_thread, }, 428 DIM(SORT_PID, "pid", sort_thread),
257 { .name = "comm", .entry = &sort_comm, }, 429 DIM(SORT_COMM, "comm", sort_comm),
258 { .name = "dso", .entry = &sort_dso, }, 430 DIM(SORT_DSO, "dso", sort_dso),
259 { .name = "symbol", .entry = &sort_sym, }, 431 DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
260 { .name = "parent", .entry = &sort_parent, }, 432 DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
261 { .name = "cpu", .entry = &sort_cpu, }, 433 DIM(SORT_SYM, "symbol", sort_sym),
434 DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
435 DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
436 DIM(SORT_PARENT, "parent", sort_parent),
437 DIM(SORT_CPU, "cpu", sort_cpu),
438 DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
262}; 439};
263 440
264int sort_dimension__add(const char *tok) 441int sort_dimension__add(const char *tok)
@@ -270,7 +447,6 @@ int sort_dimension__add(const char *tok)
270 447
271 if (strncasecmp(tok, sd->name, strlen(tok))) 448 if (strncasecmp(tok, sd->name, strlen(tok)))
272 continue; 449 continue;
273
274 if (sd->entry == &sort_parent) { 450 if (sd->entry == &sort_parent) {
275 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); 451 int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
276 if (ret) { 452 if (ret) {
@@ -302,6 +478,16 @@ int sort_dimension__add(const char *tok)
302 sort__first_dimension = SORT_PARENT; 478 sort__first_dimension = SORT_PARENT;
303 else if (!strcmp(sd->name, "cpu")) 479 else if (!strcmp(sd->name, "cpu"))
304 sort__first_dimension = SORT_CPU; 480 sort__first_dimension = SORT_CPU;
481 else if (!strcmp(sd->name, "symbol_from"))
482 sort__first_dimension = SORT_SYM_FROM;
483 else if (!strcmp(sd->name, "symbol_to"))
484 sort__first_dimension = SORT_SYM_TO;
485 else if (!strcmp(sd->name, "dso_from"))
486 sort__first_dimension = SORT_DSO_FROM;
487 else if (!strcmp(sd->name, "dso_to"))
488 sort__first_dimension = SORT_DSO_TO;
489 else if (!strcmp(sd->name, "mispredict"))
490 sort__first_dimension = SORT_MISPREDICT;
305 } 491 }
306 492
307 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 493 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
@@ -309,7 +495,6 @@ int sort_dimension__add(const char *tok)
309 495
310 return 0; 496 return 0;
311 } 497 }
312
313 return -ESRCH; 498 return -ESRCH;
314} 499}
315 500
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 3f67ae395752..472aa5a63a58 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -31,11 +31,16 @@ extern const char *parent_pattern;
31extern const char default_sort_order[]; 31extern const char default_sort_order[];
32extern int sort__need_collapse; 32extern int sort__need_collapse;
33extern int sort__has_parent; 33extern int sort__has_parent;
34extern int sort__branch_mode;
34extern char *field_sep; 35extern char *field_sep;
35extern struct sort_entry sort_comm; 36extern struct sort_entry sort_comm;
36extern struct sort_entry sort_dso; 37extern struct sort_entry sort_dso;
37extern struct sort_entry sort_sym; 38extern struct sort_entry sort_sym;
38extern struct sort_entry sort_parent; 39extern struct sort_entry sort_parent;
40extern struct sort_entry sort_dso_from;
41extern struct sort_entry sort_dso_to;
42extern struct sort_entry sort_sym_from;
43extern struct sort_entry sort_sym_to;
39extern enum sort_type sort__first_dimension; 44extern enum sort_type sort__first_dimension;
40 45
41/** 46/**
@@ -72,6 +77,7 @@ struct hist_entry {
72 struct hist_entry *pair; 77 struct hist_entry *pair;
73 struct rb_root sorted_chain; 78 struct rb_root sorted_chain;
74 }; 79 };
80 struct branch_info *branch_info;
75 struct callchain_root callchain[0]; 81 struct callchain_root callchain[0];
76}; 82};
77 83
@@ -82,6 +88,11 @@ enum sort_type {
82 SORT_SYM, 88 SORT_SYM,
83 SORT_PARENT, 89 SORT_PARENT,
84 SORT_CPU, 90 SORT_CPU,
91 SORT_DSO_FROM,
92 SORT_DSO_TO,
93 SORT_SYM_FROM,
94 SORT_SYM_TO,
95 SORT_MISPREDICT,
85}; 96};
86 97
87/* 98/*
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 2a683d4fc918..ac49ef208a5f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -5,6 +5,7 @@
5#include <stdbool.h> 5#include <stdbool.h>
6#include <stdint.h> 6#include <stdint.h>
7#include "map.h" 7#include "map.h"
8#include "../perf.h"
8#include <linux/list.h> 9#include <linux/list.h>
9#include <linux/rbtree.h> 10#include <linux/rbtree.h>
10#include <stdio.h> 11#include <stdio.h>
@@ -96,7 +97,11 @@ struct symbol_conf {
96 *col_width_list_str; 97 *col_width_list_str;
97 struct strlist *dso_list, 98 struct strlist *dso_list,
98 *comm_list, 99 *comm_list,
99 *sym_list; 100 *sym_list,
101 *dso_from_list,
102 *dso_to_list,
103 *sym_from_list,
104 *sym_to_list;
100 const char *symfs; 105 const char *symfs;
101}; 106};
102 107
@@ -120,6 +125,19 @@ struct map_symbol {
120 bool has_children; 125 bool has_children;
121}; 126};
122 127
128struct addr_map_symbol {
129 struct map *map;
130 struct symbol *sym;
131 u64 addr;
132 u64 al_addr;
133};
134
135struct branch_info {
136 struct addr_map_symbol from;
137 struct addr_map_symbol to;
138 struct branch_flags flags;
139};
140
123struct addr_location { 141struct addr_location {
124 struct thread *thread; 142 struct thread *thread;
125 struct map *map; 143 struct map *map;
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index bfba0490c098..de8ece8bcce3 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -805,8 +805,11 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
805 self->hists = hists; 805 self->hists = hists;
806 self->b.refresh = hist_browser__refresh; 806 self->b.refresh = hist_browser__refresh;
807 self->b.seek = ui_browser__hists_seek; 807 self->b.seek = ui_browser__hists_seek;
808 self->b.use_navkeypressed = true, 808 self->b.use_navkeypressed = true;
809 self->has_symbols = sort_sym.list.next != NULL; 809 if (sort__branch_mode == 1)
810 self->has_symbols = sort_sym_from.list.next != NULL;
811 else
812 self->has_symbols = sort_sym.list.next != NULL;
810 } 813 }
811 814
812 return self; 815 return self;
@@ -853,6 +856,16 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
853 return printed; 856 return printed;
854} 857}
855 858
859static inline void free_popup_options(char **options, int n)
860{
861 int i;
862
863 for (i = 0; i < n; ++i) {
864 free(options[i]);
865 options[i] = NULL;
866 }
867}
868
856static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, 869static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
857 const char *helpline, const char *ev_name, 870 const char *helpline, const char *ev_name,
858 bool left_exits, 871 bool left_exits,
@@ -861,7 +874,10 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
861{ 874{
862 struct hists *self = &evsel->hists; 875 struct hists *self = &evsel->hists;
863 struct hist_browser *browser = hist_browser__new(self); 876 struct hist_browser *browser = hist_browser__new(self);
877 struct branch_info *bi;
864 struct pstack *fstack; 878 struct pstack *fstack;
879 char *options[16];
880 int nr_options = 0;
865 int key = -1; 881 int key = -1;
866 882
867 if (browser == NULL) 883 if (browser == NULL)
@@ -873,13 +889,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
873 889
874 ui_helpline__push(helpline); 890 ui_helpline__push(helpline);
875 891
892 memset(options, 0, sizeof(options));
893
876 while (1) { 894 while (1) {
877 const struct thread *thread = NULL; 895 const struct thread *thread = NULL;
878 const struct dso *dso = NULL; 896 const struct dso *dso = NULL;
879 char *options[16]; 897 int choice = 0,
880 int nr_options = 0, choice = 0, i,
881 annotate = -2, zoom_dso = -2, zoom_thread = -2, 898 annotate = -2, zoom_dso = -2, zoom_thread = -2,
882 browse_map = -2; 899 annotate_f = -2, annotate_t = -2, browse_map = -2;
900
901 nr_options = 0;
883 902
884 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); 903 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
885 904
@@ -887,7 +906,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
887 thread = hist_browser__selected_thread(browser); 906 thread = hist_browser__selected_thread(browser);
888 dso = browser->selection->map ? browser->selection->map->dso : NULL; 907 dso = browser->selection->map ? browser->selection->map->dso : NULL;
889 } 908 }
890
891 switch (key) { 909 switch (key) {
892 case K_TAB: 910 case K_TAB:
893 case K_UNTAB: 911 case K_UNTAB:
@@ -902,7 +920,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
902 if (!browser->has_symbols) { 920 if (!browser->has_symbols) {
903 ui_browser__warning(&browser->b, delay_secs * 2, 921 ui_browser__warning(&browser->b, delay_secs * 2,
904 "Annotation is only available for symbolic views, " 922 "Annotation is only available for symbolic views, "
905 "include \"sym\" in --sort to use it."); 923 "include \"sym*\" in --sort to use it.");
906 continue; 924 continue;
907 } 925 }
908 926
@@ -972,12 +990,34 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
972 if (!browser->has_symbols) 990 if (!browser->has_symbols)
973 goto add_exit_option; 991 goto add_exit_option;
974 992
975 if (browser->selection != NULL && 993 if (sort__branch_mode == 1) {
976 browser->selection->sym != NULL && 994 bi = browser->he_selection->branch_info;
977 !browser->selection->map->dso->annotate_warned && 995 if (browser->selection != NULL &&
978 asprintf(&options[nr_options], "Annotate %s", 996 bi &&
979 browser->selection->sym->name) > 0) 997 bi->from.sym != NULL &&
980 annotate = nr_options++; 998 !bi->from.map->dso->annotate_warned &&
999 asprintf(&options[nr_options], "Annotate %s",
1000 bi->from.sym->name) > 0)
1001 annotate_f = nr_options++;
1002
1003 if (browser->selection != NULL &&
1004 bi &&
1005 bi->to.sym != NULL &&
1006 !bi->to.map->dso->annotate_warned &&
1007 (bi->to.sym != bi->from.sym ||
1008 bi->to.map->dso != bi->from.map->dso) &&
1009 asprintf(&options[nr_options], "Annotate %s",
1010 bi->to.sym->name) > 0)
1011 annotate_t = nr_options++;
1012 } else {
1013
1014 if (browser->selection != NULL &&
1015 browser->selection->sym != NULL &&
1016 !browser->selection->map->dso->annotate_warned &&
1017 asprintf(&options[nr_options], "Annotate %s",
1018 browser->selection->sym->name) > 0)
1019 annotate = nr_options++;
1020 }
981 1021
982 if (thread != NULL && 1022 if (thread != NULL &&
983 asprintf(&options[nr_options], "Zoom %s %s(%d) thread", 1023 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
@@ -998,25 +1038,39 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
998 browse_map = nr_options++; 1038 browse_map = nr_options++;
999add_exit_option: 1039add_exit_option:
1000 options[nr_options++] = (char *)"Exit"; 1040 options[nr_options++] = (char *)"Exit";
1001 1041retry_popup_menu:
1002 choice = ui__popup_menu(nr_options, options); 1042 choice = ui__popup_menu(nr_options, options);
1003 1043
1004 for (i = 0; i < nr_options - 1; ++i)
1005 free(options[i]);
1006
1007 if (choice == nr_options - 1) 1044 if (choice == nr_options - 1)
1008 break; 1045 break;
1009 1046
1010 if (choice == -1) 1047 if (choice == -1) {
1048 free_popup_options(options, nr_options - 1);
1011 continue; 1049 continue;
1050 }
1012 1051
1013 if (choice == annotate) { 1052 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1014 struct hist_entry *he; 1053 struct hist_entry *he;
1015 int err; 1054 int err;
1016do_annotate: 1055do_annotate:
1017 he = hist_browser__selected_entry(browser); 1056 he = hist_browser__selected_entry(browser);
1018 if (he == NULL) 1057 if (he == NULL)
1019 continue; 1058 continue;
1059
1060 /*
1061 * we stash the branch_info symbol + map into the
1062 * the ms so we don't have to rewrite all the annotation
1063 * code to use branch_info.
1064 * in branch mode, the ms struct is not used
1065 */
1066 if (choice == annotate_f) {
1067 he->ms.sym = he->branch_info->from.sym;
1068 he->ms.map = he->branch_info->from.map;
1069 } else if (choice == annotate_t) {
1070 he->ms.sym = he->branch_info->to.sym;
1071 he->ms.map = he->branch_info->to.map;
1072 }
1073
1020 /* 1074 /*
1021 * Don't let this be freed, say, by hists__decay_entry. 1075 * Don't let this be freed, say, by hists__decay_entry.
1022 */ 1076 */
@@ -1024,9 +1078,18 @@ do_annotate:
1024 err = hist_entry__tui_annotate(he, evsel->idx, 1078 err = hist_entry__tui_annotate(he, evsel->idx,
1025 timer, arg, delay_secs); 1079 timer, arg, delay_secs);
1026 he->used = false; 1080 he->used = false;
1081 /*
1082 * offer option to annotate the other branch source or target
1083 * (if they exists) when returning from annotate
1084 */
1085 if ((err == 'q' || err == CTRL('c'))
1086 && annotate_t != -2 && annotate_f != -2)
1087 goto retry_popup_menu;
1088
1027 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); 1089 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1028 if (err) 1090 if (err)
1029 ui_browser__handle_resize(&browser->b); 1091 ui_browser__handle_resize(&browser->b);
1092
1030 } else if (choice == browse_map) 1093 } else if (choice == browse_map)
1031 map__browse(browser->selection->map); 1094 map__browse(browser->selection->map);
1032 else if (choice == zoom_dso) { 1095 else if (choice == zoom_dso) {
@@ -1072,6 +1135,7 @@ out_free_stack:
1072 pstack__delete(fstack); 1135 pstack__delete(fstack);
1073out: 1136out:
1074 hist_browser__delete(browser); 1137 hist_browser__delete(browser);
1138 free_popup_options(options, nr_options - 1);
1075 return key; 1139 return key;
1076} 1140}
1077 1141