Re: [PATCH v5 11/18] perf: add code to support PERF_SAMPLE_BRANCH_STACK

From: Stephane Eranian
Date: Tue Feb 07 2012 - 09:12:01 EST


On Mon, Feb 6, 2012 at 7:06 PM, Arnaldo Carvalho de Melo
<acme@xxxxxxxxxx> wrote:
> Em Thu, Feb 02, 2012 at 01:54:41PM +0100, Stephane Eranian escreveu:
>> From: Roberto Agostino Vitillo <ravitillo@xxxxxxx>
>>
>> This patch adds:
>> - ability to parse samples with PERF_SAMPLE_BRANCH_STACK
>> - sort on branches
>> - build histograms on branches
>
> Some comments below, mostly minor stuff, looks great work, thanks!
>
> - Arnaldo
>
>> Signed-off-by: Roberto Agostino Vitillo <ravitillo@xxxxxxx>
>> Signed-off-by: Stephane Eranian <eranian@xxxxxxxxxx>
>> ---
>> Âtools/perf/perf.h     Â|  17 ++
>> Âtools/perf/util/annotate.c | Â Â2 +-
>> Âtools/perf/util/event.h  Â|  Â1 +
>> Âtools/perf/util/evsel.c  Â|  10 ++
>> Âtools/perf/util/hist.c   |  93 +++++++++---
>> Âtools/perf/util/hist.h   |  Â7 +
>> Âtools/perf/util/session.c Â| Â 72 +++++++++
>> Âtools/perf/util/session.h Â| Â Â4 +
>> Âtools/perf/util/sort.c   | Â362 +++++++++++++++++++++++++++++++++-----------
>> Âtools/perf/util/sort.h   |  Â5 +
>> Âtools/perf/util/symbol.h  |  13 ++
>> Â11 files changed, 475 insertions(+), 111 deletions(-)
>>
>> diff --git a/tools/perf/perf.h b/tools/perf/perf.h
>> index 92af168..8b4d25d 100644
>> --- a/tools/perf/perf.h
>> +++ b/tools/perf/perf.h
>> @@ -180,6 +180,23 @@ struct ip_callchain {
>> Â Â Â u64 ips[0];
>> Â};
>>
>> +struct branch_flags {
>> + Â Â u64 mispred:1;
>> + Â Â u64 predicted:1;
>> + Â Â u64 reserved:62;
>> +};
>> +
>> +struct branch_entry {
>> + Â Â u64 Â Â Â Â Â Â Â Â Â Â Â Â Â Â from;
>> + Â Â u64 Â Â Â Â Â Â Â Â Â Â Â Â Â Â to;
>> + Â Â struct branch_flags flags;
>> +};
>> +
>> +struct branch_stack {
>> + Â Â u64 Â Â Â Â Â Â Â Â Â Â Â Â Â Â nr;
>> +   struct branch_entry   entries[0];
>> +};
>> +
>> Âextern bool perf_host, perf_guest;
>> Âextern const char perf_version_string[];
>>
>> diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
>> index 011ed26..8248d80 100644
>> --- a/tools/perf/util/annotate.c
>> +++ b/tools/perf/util/annotate.c
>> @@ -64,7 +64,7 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
>>
>> Â Â Â pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr));
>>
>> - Â Â if (addr >= sym->end)
>> + Â Â if (addr >= sym->end || addr < sym->start)
>
> This is not related to this, would be better to come in a separate patch
> with a proper explanation.
>
You mean in this patchset or separately?

>> Â Â Â Â Â Â Â return 0;
>>
>> Â Â Â offset = addr - sym->start;
>> diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
>> index cbdeaad..1b19728 100644
>> --- a/tools/perf/util/event.h
>> +++ b/tools/perf/util/event.h
>> @@ -81,6 +81,7 @@ struct perf_sample {
>> Â Â Â u32 raw_size;
>> Â Â Â void *raw_data;
>> Â Â Â struct ip_callchain *callchain;
>> + Â Â struct branch_stack *branch_stack;
>> Â};
>>
>> Â#define BUILD_ID_SIZE 20
>> diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
>> index dcfefab..6b15cda 100644
>> --- a/tools/perf/util/evsel.c
>> +++ b/tools/perf/util/evsel.c
>> @@ -575,6 +575,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
>> Â Â Â Â Â Â Â data->raw_data = (void *) pdata;
>> Â Â Â }
>>
>> + Â Â if (type & PERF_SAMPLE_BRANCH_STACK) {
>> + Â Â Â Â Â Â u64 sz;
>> +
>> + Â Â Â Â Â Â data->branch_stack = (struct branch_stack *)array;
>> + Â Â Â Â Â Â array++; /* nr */
>> +
>> + Â Â Â Â Â Â sz = data->branch_stack->nr * sizeof(struct branch_entry);
>> + Â Â Â Â Â Â sz /= sizeof(uint64_t);
>
> Consistency here: use sizeof(u64), or better yet: sizeof(sz);
>
>> + Â Â Â Â Â Â array += sz;
>> + Â Â }
>> Â Â Â return 0;
>> Â}
>>
>> diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
>> index 6f505d1..66f9936 100644
>> --- a/tools/perf/util/hist.c
>> +++ b/tools/perf/util/hist.c
>> @@ -54,9 +54,11 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
>> Â{
>> Â Â Â u16 len;
>>
>> - Â Â if (h->ms.sym)
>> - Â Â Â Â Â Â hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
>> - Â Â else {
>> + Â Â if (h->ms.sym) {
>> + Â Â Â Â Â Â int n = (int)h->ms.sym->namelen + 4;
>> + Â Â Â Â Â Â int symlen = max(n, BITS_PER_LONG / 4 + 6);
>
> What is the rationale here? Adding a comment will help
>
Will do.

>> + Â Â Â Â Â Â hists__new_col_len(hists, HISTC_SYMBOL, symlen);
>> + Â Â } else {
>> Â Â Â Â Â Â Â const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
>>
>> Â Â Â Â Â Â Â if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
>> @@ -195,26 +197,14 @@ static u8 symbol__parent_filter(const struct symbol *parent)
>> Â Â Â return 0;
>> Â}
>>
>> -struct hist_entry *__hists__add_entry(struct hists *hists,
>> +static struct hist_entry *add_hist_entry(struct hists *hists,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct hist_entry *entry,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct addr_location *al,
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct symbol *sym_parent, u64 period)
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â u64 period)
>> Â{
>> Â Â Â struct rb_node **p;
>> Â Â Â struct rb_node *parent = NULL;
>> Â Â Â struct hist_entry *he;
>> - Â Â struct hist_entry entry = {
>> - Â Â Â Â Â Â .thread = al->thread,
>> - Â Â Â Â Â Â .ms = {
>> -           .map  Â= al->map,
>> -           .sym  Â= al->sym,
>> - Â Â Â Â Â Â },
>> -       .cpu  Â= al->cpu,
>> -       .ip   = al->addr,
>> - Â Â Â Â Â Â .level Â= al->level,
>> - Â Â Â Â Â Â .period = period,
>> - Â Â Â Â Â Â .parent = sym_parent,
>> - Â Â Â Â Â Â .filtered = symbol__parent_filter(sym_parent),
>> - Â Â };
>> Â Â Â int cmp;
>>
>> Â Â Â pthread_mutex_lock(&hists->lock);
>> @@ -225,7 +215,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
>> Â Â Â Â Â Â Â parent = *p;
>> Â Â Â Â Â Â Â he = rb_entry(parent, struct hist_entry, rb_node_in);
>>
>> - Â Â Â Â Â Â cmp = hist_entry__cmp(&entry, he);
>> + Â Â Â Â Â Â cmp = hist_entry__cmp(entry, he);
>>
>> Â Â Â Â Â Â Â if (!cmp) {
>> Â Â Â Â Â Â Â Â Â Â Â he->period += period;
>> @@ -239,7 +229,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
>> Â Â Â Â Â Â Â Â Â Â Â p = &(*p)->rb_right;
>> Â Â Â }
>>
>> - Â Â he = hist_entry__new(&entry);
>> + Â Â he = hist_entry__new(entry);
>> Â Â Â if (!he)
>> Â Â Â Â Â Â Â goto out_unlock;
>>
>> @@ -252,6 +242,69 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
>> Â Â Â return he;
>> Â}
>>
>> +struct hist_entry *__hists__add_branch_entry(struct hists *self,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct addr_location *al,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct symbol *sym_parent,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct branch_info *bi,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âu64 period){
>> + Â Â struct hist_entry entry = {
>> + Â Â Â Â Â Â .thread = al->thread,
>> + Â Â Â Â Â Â .ms = {
>> +           .map  Â= bi->to.map,
>> +           .sym  Â= bi->to.sym,
>> + Â Â Â Â Â Â },
>> +       .cpu  Â= al->cpu,
>> +       .ip   = bi->to.addr,
>> + Â Â Â Â Â Â .level Â= al->level,
>> + Â Â Â Â Â Â .period = period,
>> + Â Â Â Â Â Â .parent = sym_parent,
>> + Â Â Â Â Â Â .filtered = symbol__parent_filter(sym_parent),
>> + Â Â Â Â Â Â .branch_info = bi,
>> + Â Â };
>> + Â Â struct hist_entry *he;
>> +
>> + Â Â he = add_hist_entry(self, &entry, al, period);
>> + Â Â if (!he)
>> + Â Â Â Â Â Â return NULL;
>> +
>> + Â Â /*
>> + Â Â Â* in branch mode, we do not display al->sym, al->addr
>
> Really minor nit, but start with: Â"In branch mode"
>
>> + Â Â Â* but instead what is in branch_info. The addresses and
>> + Â Â Â* symbols there may need wider columns, so make sure they
>> + Â Â Â* are taken into account.
>> + Â Â Â*
>> + Â Â Â* hists__calc_col_len() tracks the max column width, so
>> + Â Â Â* we need to call it for both the from and to addresses
>> + Â Â Â*/
>> +   entry.ip   = bi->from.addr;
>> + Â Â entry.ms.map = bi->from.map;
>> + Â Â entry.ms.sym = bi->from.sym;
>> + Â Â hists__calc_col_len(self, &entry);
>> +
>> + Â Â return he;
>> +}
>> +
>> +struct hist_entry *__hists__add_entry(struct hists *self,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct addr_location *al,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct symbol *sym_parent, u64 period)
>> +{
>> + Â Â struct hist_entry entry = {
>> + Â Â Â Â Â Â .thread = al->thread,
>> + Â Â Â Â Â Â .ms = {
>> +           .map  Â= al->map,
>> +           .sym  Â= al->sym,
>> + Â Â Â Â Â Â },
>> +       .cpu  Â= al->cpu,
>> +       .ip   = al->addr,
>> + Â Â Â Â Â Â .level Â= al->level,
>> + Â Â Â Â Â Â .period = period,
>> + Â Â Â Â Â Â .parent = sym_parent,
>> + Â Â Â Â Â Â .filtered = symbol__parent_filter(sym_parent),
>> + Â Â };
>> +
>> + Â Â return add_hist_entry(self, &entry, al, period);
>> +}
>> +
>> Âint64_t
>> Âhist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
>> Â{
>> diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
>> index 0d48613..801a04e 100644
>> --- a/tools/perf/util/hist.h
>> +++ b/tools/perf/util/hist.h
>> @@ -41,6 +41,7 @@ enum hist_column {
>> Â Â Â HISTC_COMM,
>> Â Â Â HISTC_PARENT,
>> Â Â Â HISTC_CPU,
>> + Â Â HISTC_MISPREDICT,
>> Â Â Â HISTC_NR_COLS, /* Last entry */
>> Â};
>>
>> @@ -73,6 +74,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
>> Â Â Â Â Â Â Â Â Â Â Â Âstruct hists *hists);
>> Âvoid hist_entry__free(struct hist_entry *);
>>
>> +struct hist_entry *__hists__add_branch_entry(struct hists *self,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct addr_location *al,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct symbol *sym_parent,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct branch_info *bi,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âu64 period);
>> +
>> Âvoid hists__output_resort(struct hists *self);
>> Âvoid hists__output_resort_threaded(struct hists *hists);
>> Âvoid hists__collapse_resort(struct hists *self);
>> diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
>> index 552c1c5..5ce3f31 100644
>> --- a/tools/perf/util/session.c
>> +++ b/tools/perf/util/session.c
>> @@ -229,6 +229,63 @@ static bool symbol__match_parent_regex(struct symbol *sym)
>> Â Â Â return 0;
>> Â}
>>
>> +static const u8 cpumodes[] = {
>> + Â Â PERF_RECORD_MISC_USER,
>> + Â Â PERF_RECORD_MISC_KERNEL,
>> + Â Â PERF_RECORD_MISC_GUEST_USER,
>> + Â Â PERF_RECORD_MISC_GUEST_KERNEL
>> +};
>> +#define NCPUMODES (sizeof(cpumodes)/sizeof(u8))
>> +
>> +static void ip__resolve_ams(struct machine *self, struct thread *thread,
>> + Â Â Â Â Â Â Â Â Â Â Â Â struct addr_map_symbol *ams,
>> + Â Â Â Â Â Â Â Â Â Â Â Â u64 ip)
>> +{
>> + Â Â struct addr_location al;
>> + Â Â size_t i;
>> + Â Â u8 m;
>> +
>> + Â Â memset(&al, 0, sizeof(al));
>> +
>> + Â Â for (i = 0; i < NCPUMODES; i++) {
>> + Â Â Â Â Â Â m = cpumodes[i];
>> + Â Â Â Â Â Â /*
>> + Â Â Â Â Â Â Â* we cannot use the header.misc hint to determine whether a
>
> ditto
>
>> + Â Â Â Â Â Â Â* branch stack address is user, kernel, guest, hypervisor.
>> + Â Â Â Â Â Â Â* Branches may straddle the kernel/user/hypervisor boundaries.
>> + Â Â Â Â Â Â Â* Thus, we have to try * consecutively until we find a match
>
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â^ comment reflow artifact?
>
>> + Â Â Â Â Â Â Â* or else, the symbol is unknown
>> + Â Â Â Â Â Â Â*/
>> + Â Â Â Â Â Â thread__find_addr_location(thread, self, m, MAP__FUNCTION,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â ip, &al, NULL);
>> + Â Â Â Â Â Â if (al.sym)
>> + Â Â Â Â Â Â Â Â Â Â goto found;
>> + Â Â }
>> +found:
>> + Â Â ams->addr = ip;
>> + Â Â ams->sym = al.sym;
>> + Â Â ams->map = al.map;
>> +}
>> +
>> +struct branch_info *perf_session__resolve_bstack(struct machine *self,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct thread *thr,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct branch_stack *bs)
>> +{
>> + Â Â struct branch_info *bi;
>> + Â Â unsigned int i;
>> +
>> + Â Â bi = calloc(bs->nr, sizeof(struct branch_info));
>> + Â Â if (!bi)
>> + Â Â Â Â Â Â return NULL;
>> +
>> + Â Â for (i = 0; i < bs->nr; i++) {
>> + Â Â Â Â Â Â ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);
>> + Â Â Â Â Â Â ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);
>> + Â Â Â Â Â Â bi[i].flags = bs->entries[i].flags;
>> + Â Â }
>> + Â Â return bi;
>> +}
>> +
>> Âint machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct thread *thread,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct ip_callchain *chain,
>> @@ -697,6 +754,18 @@ static void callchain__printf(struct perf_sample *sample)
>> Â Â Â Â Â Â Â Â Â Â Âi, sample->callchain->ips[i]);
>> Â}
>>
>> +static void branch_stack__printf(struct perf_sample *sample)
>> +{
>> + Â Â uint64_t i;
>> +
>> + Â Â printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
>> +
>> + Â Â for (i = 0; i < sample->branch_stack->nr; i++)
>> + Â Â Â Â Â Â printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n",
>> + Â Â Â Â Â Â Â Â Â Â i, sample->branch_stack->entries[i].from,
>> + Â Â Â Â Â Â Â Â Â Â sample->branch_stack->entries[i].to);
>> +}
>> +
>> Âstatic void perf_session__print_tstamp(struct perf_session *session,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âunion perf_event *event,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct perf_sample *sample)
>> @@ -744,6 +813,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
>>
>> Â Â Â if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
>> Â Â Â Â Â Â Â callchain__printf(sample);
>> +
>> + Â Â if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
>> + Â Â Â Â Â Â branch_stack__printf(sample);
>> Â}
>>
>> Âstatic struct machine *
>> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
>> index c8d9017..accb5dc 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
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct ip_callchain *chain,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct symbol **parent);
>>
>> +struct branch_info *perf_session__resolve_bstack(struct machine *self,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct thread *thread,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct branch_stack *bs);
>> +
>> Âbool perf_session__has_traces(struct perf_session *self, const char *msg);
>>
>> Âvoid mem_bswap_64(void *src, int byte_size);
>> diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
>> index 16da30d..1531989 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";
>> Âconst char  *sort_order = default_sort_order;
>> Âint     Âsort__need_collapse = 0;
>> Âint     Âsort__has_parent = 0;
>> +bool     sort__branch_mode;
>>
>> Âenum sort_type    sort__first_dimension;
>>
>> @@ -94,6 +95,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf,
>> Â Â Â return repsep_snprintf(bf, size, "%*s", width, self->thread->comm);
>> Â}
>>
>> +static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
>> +{
>> + Â Â struct dso *dso_l = map_l ? map_l->dso : NULL;
>> + Â Â struct dso *dso_r = map_r ? map_r->dso : NULL;
>> + Â Â const char *dso_name_l, *dso_name_r;
>> +
>> + Â Â if (!dso_l || !dso_r)
>> + Â Â Â Â Â Â return cmp_null(dso_l, dso_r);
>> +
>> + Â Â if (verbose) {
>> + Â Â Â Â Â Â dso_name_l = dso_l->long_name;
>> + Â Â Â Â Â Â dso_name_r = dso_r->long_name;
>> + Â Â } else {
>> + Â Â Â Â Â Â dso_name_l = dso_l->short_name;
>> + Â Â Â Â Â Â dso_name_r = dso_r->short_name;
>> + Â Â }
>> +
>> + Â Â return strcmp(dso_name_l, dso_name_r);
>> +}
>> +
>> Âstruct sort_entry sort_comm = {
>>    .se_header   Â= "Command",
>>    .se_cmp     = sort__comm_cmp,
>> @@ -107,36 +128,74 @@ struct sort_entry sort_comm = {
>> Âstatic int64_t
>> Âsort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
>> Â{
>> - Â Â struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL;
>> - Â Â struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL;
>> - Â Â const char *dso_name_l, *dso_name_r;
>> + Â Â return _sort__dso_cmp(left->ms.map, right->ms.map);
>> +}
>>
>> - Â Â if (!dso_l || !dso_r)
>> - Â Â Â Â Â Â return cmp_null(dso_l, dso_r);
>>
>> - Â Â if (verbose) {
>> - Â Â Â Â Â Â dso_name_l = dso_l->long_name;
>> - Â Â Â Â Â Â dso_name_r = dso_r->long_name;
>> - Â Â } else {
>> - Â Â Â Â Â Â dso_name_l = dso_l->short_name;
>> - Â Â Â Â Â Â dso_name_r = dso_r->short_name;
>> +static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r,
>
> We use double _ on the front with the same rationale as in the kernel,
> i.e. we we do a little bit less than what the non __ prefixed function
> does (locking, etc).
>
The function was extracted to be called from different contexts.
The old function kept the same name to avoid modifying many lines of code.
The _sort__sym_cmp() does the actual work, i.e., the common code.
So I don't know how to apply your comment.

>> + Â Â Â Â Â Â Â Â Â Â Â Â Â u64 ip_l, u64 ip_r)
>> +{
>> + Â Â if (!sym_l || !sym_r)
>> + Â Â Â Â Â Â return cmp_null(sym_l, sym_r);
>> +
>> + Â Â if (sym_l == sym_r)
>> + Â Â Â Â Â Â return 0;
>> +
>> + Â Â if (sym_l)
>> + Â Â Â Â Â Â ip_l = sym_l->start;
>> + Â Â if (sym_r)
>> + Â Â Â Â Â Â ip_r = sym_r->start;
>> +
>> + Â Â return (int64_t)(ip_r - ip_l);
>> +}
>> +
>> +static int _hist_entry__dso_snprintf(struct map *map, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âsize_t size, unsigned int width)
>> +{
>> + Â Â if (map && map->dso) {
>> + Â Â Â Â Â Â const char *dso_name = !verbose ? map->dso->short_name :
>> + Â Â Â Â Â Â Â Â Â Â map->dso->long_name;
>> + Â Â Â Â Â Â return repsep_snprintf(bf, size, "%-*s", width, dso_name);
>> Â Â Â }
>>
>> - Â Â return strcmp(dso_name_l, dso_name_r);
>> + Â Â return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
>> Â}
>>
>> Âstatic int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
>> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width)
>> Â{
>> - Â Â if (self->ms.map && self->ms.map->dso) {
>> - Â Â Â Â Â Â const char *dso_name = !verbose ? self->ms.map->dso->short_name :
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â self->ms.map->dso->long_name;
>> - Â Â Â Â Â Â return repsep_snprintf(bf, size, "%-*s", width, dso_name);
>> + Â Â return _hist_entry__dso_snprintf(self->ms.map, bf, size, width);
>> +}
>> +
>> +static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âu64 ip, char level, char *bf, size_t size,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âunsigned int width __used)
>> +{
>> + Â Â size_t ret = 0;
>> +
>> + Â Â if (verbose) {
>> + Â Â Â Â Â Â char o = map ? dso__symtab_origin(map->dso) : '!';
>> + Â Â Â Â Â Â ret += repsep_snprintf(bf, size, "%-#*llx %c ",
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂBITS_PER_LONG / 4, ip, o);
>> Â Â Â }
>>
>> - Â Â return repsep_snprintf(bf, size, "%-*s", width, "[unknown]");
>> + Â Â ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
>> + Â Â if (sym)
>> + Â Â Â Â Â Â ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âwidth - ret,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âsym->name);
>> + Â Â else {
>> + Â Â Â Â Â Â size_t len = BITS_PER_LONG / 4;
>> + Â Â Â Â Â Â ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âlen, ip);
>> + Â Â Â Â Â Â ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âwidth - ret, "");
>> + Â Â }
>> +
>> + Â Â return ret;
>> Â}
>>
>> +
>> Âstruct sort_entry sort_dso = {
>>    .se_header   Â= "Shared Object",
>>    .se_cmp     = sort__dso_cmp,
>> @@ -144,8 +203,14 @@ struct sort_entry sort_dso = {
>>    .se_width_idx  = HISTC_DSO,
>> Â};
>>
>> -/* --sort symbol */
>> +static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width __used)
>> +{
>> + Â Â return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âself->level, bf, size, width);
>> +}
>>
>> +/* --sort symbol */
>> Âstatic int64_t
>> Âsort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
>> Â{
>> @@ -154,40 +219,10 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
>> Â Â Â if (!left->ms.sym && !right->ms.sym)
>> Â Â Â Â Â Â Â return right->level - left->level;
>>
>> - Â Â if (!left->ms.sym || !right->ms.sym)
>> - Â Â Â Â Â Â return cmp_null(left->ms.sym, right->ms.sym);
>> -
>> - Â Â if (left->ms.sym == right->ms.sym)
>> - Â Â Â Â Â Â return 0;
>> -
>> Â Â Â ip_l = left->ms.sym->start;
>> Â Â Â ip_r = right->ms.sym->start;
>>
>> - Â Â return (int64_t)(ip_r - ip_l);
>> -}
>> -
>> -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width __used)
>> -{
>> - Â Â size_t ret = 0;
>> -
>> - Â Â if (verbose) {
>> - Â Â Â Â Â Â char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
>> - Â Â Â Â Â Â ret += repsep_snprintf(bf, size, "%-#*llx %c ",
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂBITS_PER_LONG / 4, self->ip, o);
>> - Â Â }
>> -
>> - Â Â if (!sort_dso.elide)
>> - Â Â Â Â Â Â ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
>> -
>> - Â Â if (self->ms.sym)
>> - Â Â Â Â Â Â ret += repsep_snprintf(bf + ret, size - ret, "%s",
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âself->ms.sym->name);
>> - Â Â else
>> - Â Â Â Â Â Â ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx",
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂBITS_PER_LONG / 4, self->ip);
>> -
>> - Â Â return ret;
>> + Â Â return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r);
>> Â}
>>
>> Âstruct sort_entry sort_sym = {
>> @@ -246,6 +281,135 @@ struct sort_entry sort_cpu = {
>>    .se_width_idx  = HISTC_CPU,
>> Â};
>>
>> +static int64_t
>> +sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right)
>> +{
>> + Â Â return _sort__dso_cmp(left->branch_info->from.map,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â right->branch_info->from.map);
>> +}
>> +
>> +static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width)
>> +{
>> + Â Â return _hist_entry__dso_snprintf(self->branch_info->from.map,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âbf, size, width);
>> +}
>> +
>> +struct sort_entry sort_dso_from = {
>> +   .se_header   Â= "Source Shared Object",
>> +   .se_cmp     = sort__dso_from_cmp,
>> +   .se_snprintf  Â= hist_entry__dso_from_snprintf,
>> +   .se_width_idx  = HISTC_DSO,
>> +};
>> +
>> +static int64_t
>> +sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right)
>> +{
>> + Â Â return _sort__dso_cmp(left->branch_info->to.map,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â right->branch_info->to.map);
>> +}
>> +
>> +static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âsize_t size, unsigned int width)
>> +{
>> + Â Â return _hist_entry__dso_snprintf(self->branch_info->to.map,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âbf, size, width);
>> +}
>> +
>> +static int64_t
>> +sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
>> +{
>> + Â Â struct addr_map_symbol *from_l = &left->branch_info->from;
>> + Â Â struct addr_map_symbol *from_r = &right->branch_info->from;
>> +
>> + Â Â if (!from_l->sym && !from_r->sym)
>> + Â Â Â Â Â Â return right->level - left->level;
>> +
>> + Â Â return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Âfrom_r->addr);
>> +}
>> +
>> +static int64_t
>> +sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
>> +{
>> + Â Â struct addr_map_symbol *to_l = &left->branch_info->to;
>> + Â Â struct addr_map_symbol *to_r = &right->branch_info->to;
>> +
>> + Â Â if (!to_l->sym && !to_r->sym)
>> + Â Â Â Â Â Â return right->level - left->level;
>> +
>> + Â Â return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr);
>> +}
>> +
>> +static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width __used)
>> +{
>> + Â Â struct addr_map_symbol *from = &self->branch_info->from;
>> + Â Â return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âself->level, bf, size, width);
>> +
>> +}
>> +
>> +static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width __used)
>> +{
>> + Â Â struct addr_map_symbol *to = &self->branch_info->to;
>> + Â Â return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âself->level, bf, size, width);
>> +
>> +}
>> +
>> +struct sort_entry sort_dso_to = {
>> +   .se_header   Â= "Target Shared Object",
>> +   .se_cmp     = sort__dso_to_cmp,
>> +   .se_snprintf  Â= hist_entry__dso_to_snprintf,
>> +   .se_width_idx  = HISTC_DSO,
>> +};
>> +
>> +struct sort_entry sort_sym_from = {
>> +   .se_header   Â= "Source Symbol",
>> +   .se_cmp     = sort__sym_from_cmp,
>> +   .se_snprintf  Â= hist_entry__sym_from_snprintf,
>> +   .se_width_idx  = HISTC_SYMBOL,
>> +};
>> +
>> +struct sort_entry sort_sym_to = {
>> +   .se_header   Â= "Target Symbol",
>> +   .se_cmp     = sort__sym_to_cmp,
>> +   .se_snprintf  Â= hist_entry__sym_to_snprintf,
>> +   .se_width_idx  = HISTC_SYMBOL,
>> +};
>> +
>> +static int64_t
>> +sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
>> +{
>> + Â Â const unsigned char mp = left->branch_info->flags.mispred !=
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â right->branch_info->flags.mispred;
>> + Â Â const unsigned char p = left->branch_info->flags.predicted !=
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â right->branch_info->flags.predicted;
>> +
>> + Â Â return mp || p;
>> +}
>> +
>> +static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â size_t size, unsigned int width){
>> + Â Â static const char *out = "N/A";
>> +
>> + Â Â if (self->branch_info->flags.predicted)
>> + Â Â Â Â Â Â out = "N";
>> + Â Â else if (self->branch_info->flags.mispred)
>> + Â Â Â Â Â Â out = "Y";
>> +
>> + Â Â return repsep_snprintf(bf, size, "%-*s", width, out);
>> +}
>> +
>> +struct sort_entry sort_mispredict = {
>> +   .se_header   Â= "Branch Mispredicted",
>> +   .se_cmp     = sort__mispredict_cmp,
>> +   .se_snprintf  Â= hist_entry__mispredict_snprintf,
>> +   .se_width_idx  = HISTC_MISPREDICT,
>> +};
>> +
>> Âstruct sort_dimension {
>>    const char       Â*name;
>>    struct sort_entry    *entry;
>> @@ -253,14 +417,59 @@ struct sort_dimension {
>> Â};
>>
>> Âstatic struct sort_dimension sort_dimensions[] = {
>> - Â Â { .name = "pid", Â Â Â Â.entry = &sort_thread, Â},
>> - Â Â { .name = "comm", Â Â Â .entry = &sort_comm, Â Â},
>> - Â Â { .name = "dso", Â Â Â Â.entry = &sort_dso, Â Â },
>> - Â Â { .name = "symbol", Â Â .entry = &sort_sym, Â Â },
>> - Â Â { .name = "parent", Â Â .entry = &sort_parent, Â},
>> - Â Â { .name = "cpu", Â Â Â Â.entry = &sort_cpu, Â Â },
>> + Â Â { .name = "pid", Â Â Â Â.entry = &sort_thread, Â Â Â Â Â Â Â Â Â},
>> + Â Â { .name = "comm", Â Â Â .entry = &sort_comm, Â Â Â Â Â Â Â Â Â Â},
>> + Â Â { .name = "dso", Â Â Â Â.entry = &sort_dso, Â Â Â Â Â Â Â Â Â Â },
>> + Â Â { .name = "dso_from", Â .entry = &sort_dso_from, .taken = true Â},
>> + Â Â { .name = "dso_to", Â Â .entry = &sort_dso_to, Â .taken = true Â},
>> + Â Â { .name = "symbol", Â Â .entry = &sort_sym, Â Â Â Â Â Â Â Â Â Â },
>> + Â Â { .name = "symbol_from",.entry = &sort_sym_from, .taken = true Â},
>> + Â Â { .name = "symbol_to", Â.entry = &sort_sym_to, Â .taken = true Â},
>> + Â Â { .name = "parent", Â Â .entry = &sort_parent, Â Â Â Â Â Â Â Â Â},
>> + Â Â { .name = "cpu", Â Â Â Â.entry = &sort_cpu, Â Â Â Â Â Â Â Â Â Â },
>> + Â Â { .name = "mispredict", .entry = &sort_mispredict, },
>> Â};
>>
>> +static int _sort_dimension__add(struct sort_dimension *sd)
>> +{
>> + Â Â if (sd->entry->se_collapse)
>> + Â Â Â Â Â Â sort__need_collapse = 1;
>> +
>> + Â Â if (sd->entry == &sort_parent) {
>> + Â Â Â Â Â Â int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
>> + Â Â Â Â Â Â if (ret) {
>> + Â Â Â Â Â Â Â Â Â Â char err[BUFSIZ];
>> +
>> + Â Â Â Â Â Â Â Â Â Â regerror(ret, &parent_regex, err, sizeof(err));
>> + Â Â Â Â Â Â Â Â Â Â pr_err("Invalid regex: %s\n%s", parent_pattern, err);
>> + Â Â Â Â Â Â Â Â Â Â return -EINVAL;
>> + Â Â Â Â Â Â }
>> + Â Â Â Â Â Â sort__has_parent = 1;
>> + Â Â }
>> +
>> + Â Â if (list_empty(&hist_entry__sort_list)) {
>> + Â Â Â Â Â Â if (!strcmp(sd->name, "pid"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_PID;
>> + Â Â Â Â Â Â else if (!strcmp(sd->name, "comm"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_COMM;
>> + Â Â Â Â Â Â else if (!strcmp(sd->name, "dso"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_DSO;
>> + Â Â Â Â Â Â else if (!strcmp(sd->name, "symbol"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_SYM;
>> + Â Â Â Â Â Â else if (!strcmp(sd->name, "parent"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_PARENT;
>> + Â Â Â Â Â Â else if (!strcmp(sd->name, "cpu"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_CPU;
>> + Â Â Â Â Â Â else if (!strcmp(sd->name, "mispredict"))
>> + Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_MISPREDICTED;
>> + Â Â }
>> +
>> + Â Â list_add_tail(&sd->entry->list, &hist_entry__sort_list);
>> + Â Â sd->taken = 1;
>> +
>> + Â Â return 0;
>> +}
>> +
>> Âint sort_dimension__add(const char *tok)
>> Â{
>> Â Â Â unsigned int i;
>> @@ -271,48 +480,21 @@ int sort_dimension__add(const char *tok)
>> Â Â Â Â Â Â Â if (strncasecmp(tok, sd->name, strlen(tok)))
>> Â Â Â Â Â Â Â Â Â Â Â continue;
>>
>> - Â Â Â Â Â Â if (sd->entry == &sort_parent) {
>> - Â Â Â Â Â Â Â Â Â Â int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED);
>> - Â Â Â Â Â Â Â Â Â Â if (ret) {
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â char err[BUFSIZ];
>> -
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â regerror(ret, &parent_regex, err, sizeof(err));
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â pr_err("Invalid regex: %s\n%s", parent_pattern, err);
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â return -EINVAL;
>> - Â Â Â Â Â Â Â Â Â Â }
>> - Â Â Â Â Â Â Â Â Â Â sort__has_parent = 1;
>> - Â Â Â Â Â Â }
>> -
>> Â Â Â Â Â Â Â if (sd->taken)
>> Â Â Â Â Â Â Â Â Â Â Â return 0;
>>
>> - Â Â Â Â Â Â if (sd->entry->se_collapse)
>> - Â Â Â Â Â Â Â Â Â Â sort__need_collapse = 1;
>> -
>> - Â Â Â Â Â Â if (list_empty(&hist_entry__sort_list)) {
>> - Â Â Â Â Â Â Â Â Â Â if (!strcmp(sd->name, "pid"))
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_PID;
>> - Â Â Â Â Â Â Â Â Â Â else if (!strcmp(sd->name, "comm"))
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_COMM;
>> - Â Â Â Â Â Â Â Â Â Â else if (!strcmp(sd->name, "dso"))
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_DSO;
>> - Â Â Â Â Â Â Â Â Â Â else if (!strcmp(sd->name, "symbol"))
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_SYM;
>> - Â Â Â Â Â Â Â Â Â Â else if (!strcmp(sd->name, "parent"))
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_PARENT;
>> - Â Â Â Â Â Â Â Â Â Â else if (!strcmp(sd->name, "cpu"))
>> - Â Â Â Â Â Â Â Â Â Â Â Â Â Â sort__first_dimension = SORT_CPU;
>> - Â Â Â Â Â Â }
>> -
>> - Â Â Â Â Â Â list_add_tail(&sd->entry->list, &hist_entry__sort_list);
>> - Â Â Â Â Â Â sd->taken = 1;
>>
>> - Â Â Â Â Â Â return 0;
>> + Â Â Â Â Â Â if (sort__branch_mode && (sd->entry == &sort_dso ||
>> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â sd->entry == &sort_sym)){
>> + Â Â Â Â Â Â Â Â Â Â int err = _sort_dimension__add(sd + 1);
>> + Â Â Â Â Â Â Â Â Â Â return err ?: _sort_dimension__add(sd + 2);
>> + Â Â Â Â Â Â } else if (sd->entry == &sort_mispredict && !sort__branch_mode)
>> + Â Â Â Â Â Â Â Â Â Â break;
>> + Â Â Â Â Â Â else
>> + Â Â Â Â Â Â Â Â Â Â return _sort_dimension__add(sd);
>> Â Â Â }
>> -
>> Â Â Â return -ESRCH;
>> Â}
>> -
>> Âvoid setup_sorting(const char * const usagestr[], const struct option *opts)
>> Â{
>> Â Â Â char *tmp, *tok, *str = strdup(sort_order);
>> diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
>> index 3f67ae3..effcae1 100644
>> --- a/tools/perf/util/sort.h
>> +++ b/tools/perf/util/sort.h
>> @@ -31,11 +31,14 @@ extern const char *parent_pattern;
>> Âextern const char default_sort_order[];
>> Âextern int sort__need_collapse;
>> Âextern int sort__has_parent;
>> +extern bool sort__branch_mode;
>> Âextern char *field_sep;
>> Âextern struct sort_entry sort_comm;
>> Âextern struct sort_entry sort_dso;
>> Âextern struct sort_entry sort_sym;
>> Âextern struct sort_entry sort_parent;
>> +extern struct sort_entry sort_lbr_dso;
>> +extern struct sort_entry sort_lbr_sym;
>> Âextern enum sort_type sort__first_dimension;
>>
>> Â/**
>> @@ -72,6 +75,7 @@ struct hist_entry {
>> Â Â Â Â Â Â Â struct hist_entry *pair;
>>        struct rb_root  Âsorted_chain;
>> Â Â Â };
>> +   struct branch_info   Â*branch_info;
>>    struct callchain_root  callchain[0];
>> Â};
>>
>> @@ -82,6 +86,7 @@ enum sort_type {
>> Â Â Â SORT_SYM,
>> Â Â Â SORT_PARENT,
>> Â Â Â SORT_CPU,
>> + Â Â SORT_MISPREDICTED,
>> Â};
>>
>> Â/*
>> diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
>> index 2a683d4..5866ce6 100644
>> --- a/tools/perf/util/symbol.h
>> +++ b/tools/perf/util/symbol.h
>> @@ -5,6 +5,7 @@
>> Â#include <stdbool.h>
>> Â#include <stdint.h>
>> Â#include "map.h"
>> +#include "../perf.h"
>> Â#include <linux/list.h>
>> Â#include <linux/rbtree.h>
>> Â#include <stdio.h>
>> @@ -120,6 +121,18 @@ struct map_symbol {
>>    bool     Âhas_children;
>> Â};
>>
>> +struct addr_map_symbol {
>> +   struct map  Â*map;
>> + Â Â struct symbol *sym;
>> + Â Â u64 Â Â Â Â Â addr;
>> +};
>> +
>> +struct branch_info {
>> + Â Â struct addr_map_symbol from;
>> + Â Â struct addr_map_symbol to;
>> + Â Â struct branch_flags flags;
>> +};
>> +
>> Âstruct addr_location {
>> Â Â Â struct thread *thread;
>>    struct map  Â*map;
>> --
>> 1.7.4.1
¢éì®&Þ~º&¶¬–+-±éÝ¥Šw®žË±Êâmébžìdz¹Þ)í…æèw*jg¬±¨¶‰šŽŠÝj/êäz¹ÞŠà2ŠÞ¨è­Ú&¢)ß«a¶Úþø®G«éh®æj:+v‰¨Šwè†Ù>Wš±êÞiÛaxPjØm¶Ÿÿà -»+ƒùdš_