perf tools: Add code to support PERF_SAMPLE_BRANCH_STACK
authorRoberto Agostino Vitillo <ravitillo@lbl.gov>
Thu, 9 Feb 2012 22:21:01 +0000 (23:21 +0100)
committerIngo Molnar <mingo@elte.hu>
Fri, 9 Mar 2012 07:26:04 +0000 (08:26 +0100)
This patch adds:

 - ability to parse samples with PERF_SAMPLE_BRANCH_STACK
 - sort on branches (dso_from, symbol_from, dso_to, symbol_to, mispredict)
 - build histograms on branches

Signed-off-by: Roberto Agostino Vitillo <ravitillo@lbl.gov>
Signed-off-by: Stephane Eranian <eranian@google.com>
Cc: peterz@infradead.org
Cc: acme@redhat.com
Cc: robert.richter@amd.com
Cc: ming.m.lin@intel.com
Cc: andi@firstfloor.org
Cc: asharma@fb.com
Cc: vweaver1@eecs.utk.edu
Cc: khandual@linux.vnet.ibm.com
Cc: dsahern@gmail.com
Link: http://lkml.kernel.org/r/1328826068-11713-12-git-send-email-eranian@google.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
tools/perf/perf.h
tools/perf/util/event.h
tools/perf/util/evsel.c
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/symbol.h

index f0227e93665d07889bb5a03f6a720c22ffe4dd10..358f40135c4dc1b63ec38eedb01129371c1a4f61 100644 (file)
@@ -179,6 +179,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[];
 
index cbdeaad9c5e5c3f01b749d7c515eb16a99320612..1b197280c621fea866e3bfd90f71f3213971ee4a 100644 (file)
@@ -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
index 302d49a9f9855e86751dca4f60fa35498ed9cbe7..a1fd1cd2a5af25fdd894e81d6055974aae8a580e 100644 (file)
@@ -576,6 +576,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(u64);
+               array += sz;
+       }
        return 0;
 }
 
index 6f505d1abac72e493043e87f029ea9c0d9d4cea5..8380c3db1c920d9c2f0a6b72c777127e1323c852 100644 (file)
@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists)
                hists__set_col_len(hists, col, 0);
 }
 
+static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
+{
+       const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
+
+       if (hists__col_len(hists, dso) < unresolved_col_width &&
+           !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
+           !symbol_conf.dso_list)
+               hists__set_col_len(hists, dso, unresolved_col_width);
+}
+
 static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
 {
+       const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
        u16 len;
 
        if (h->ms.sym)
-               hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
-       else {
-               const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
-
-               if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
-                   !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
-                   !symbol_conf.dso_list)
-                       hists__set_col_len(hists, HISTC_DSO,
-                                          unresolved_col_width);
-       }
+               hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
+       else
+               hists__set_unres_dso_col_len(hists, HISTC_DSO);
 
        len = thread__comm_len(h->thread);
        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)
                len = dso__name_len(h->ms.map->dso);
                hists__new_col_len(hists, HISTC_DSO, len);
        }
+
+       if (h->branch_info) {
+               int symlen;
+               /*
+                * +4 accounts for '[x] ' priv level info
+                * +2 account of 0x prefix on raw addresses
+                */
+               if (h->branch_info->from.sym) {
+                       symlen = (int)h->branch_info->from.sym->namelen + 4;
+                       hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+
+                       symlen = dso__name_len(h->branch_info->from.map->dso);
+                       hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
+               } else {
+                       symlen = unresolved_col_width + 4 + 2;
+                       hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+                       hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
+               }
+
+               if (h->branch_info->to.sym) {
+                       symlen = (int)h->branch_info->to.sym->namelen + 4;
+                       hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+
+                       symlen = dso__name_len(h->branch_info->to.map->dso);
+                       hists__new_col_len(hists, HISTC_DSO_TO, symlen);
+               } else {
+                       symlen = unresolved_col_width + 4 + 2;
+                       hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+                       hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
+               }
+       }
 }
 
 static void hist_entry__add_cpumode_period(struct hist_entry *he,
@@ -195,26 +230,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 +248,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 +262,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 +275,51 @@ out_unlock:
        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,
+       };
+
+       return add_hist_entry(self, &entry, al, period);
+}
+
+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)
 {
index 48e5acd1e862f70c0f74a6a4dc53a2dc1246fd06..9413f3e31fea8b5c1c305f1d914b7de2d3e12009 100644 (file)
@@ -42,6 +42,11 @@ enum hist_column {
        HISTC_COMM,
        HISTC_PARENT,
        HISTC_CPU,
+       HISTC_MISPREDICT,
+       HISTC_SYMBOL_FROM,
+       HISTC_SYMBOL_TO,
+       HISTC_DSO_FROM,
+       HISTC_DSO_TO,
        HISTC_NR_COLS, /* Last entry */
 };
 
@@ -74,6 +79,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);
index 9f833cf9c6a9b9db2cf3874b33e7721110bb6591..bec8a328b1b8d8228406a94ee83ab50890a4ec41 100644 (file)
@@ -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
+                * 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
+                * 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 *machine__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 *
index c8d90178e7dea687cedfc684a0f76754beab3d5d..7a5434c005653a4736cabdc2921ad907f74f2c4c 100644 (file)
@@ -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 *machine__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);
index 16da30d8d765a30b3fa6d328ab43c4d150dafa9e..2739ed10d5e60b5d816c13ac6aebde3315dcce48 100644 (file)
@@ -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,
+                             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)
 {
@@ -163,31 +228,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
        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,19 +287,155 @@ 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_FROM,
+};
+
+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_TO,
+};
+
+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_FROM,
+};
+
+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_TO,
+};
+
+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;
        int                     taken;
 };
 
+#define DIM(d, n, func) [d] = { .name = n, .entry = &(func) }
+
 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,     },
+       DIM(SORT_PID, "pid", sort_thread),
+       DIM(SORT_COMM, "comm", sort_comm),
+       DIM(SORT_DSO, "dso", sort_dso),
+       DIM(SORT_DSO_FROM, "dso_from", sort_dso_from),
+       DIM(SORT_DSO_TO, "dso_to", sort_dso_to),
+       DIM(SORT_SYM, "symbol", sort_sym),
+       DIM(SORT_SYM_TO, "symbol_from", sort_sym_from),
+       DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to),
+       DIM(SORT_PARENT, "parent", sort_parent),
+       DIM(SORT_CPU, "cpu", sort_cpu),
+       DIM(SORT_MISPREDICT, "mispredict", sort_mispredict),
 };
 
 int sort_dimension__add(const char *tok)
@@ -270,7 +447,6 @@ 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) {
@@ -302,6 +478,16 @@ int sort_dimension__add(const char *tok)
                                sort__first_dimension = SORT_PARENT;
                        else if (!strcmp(sd->name, "cpu"))
                                sort__first_dimension = SORT_CPU;
+                       else if (!strcmp(sd->name, "symbol_from"))
+                               sort__first_dimension = SORT_SYM_FROM;
+                       else if (!strcmp(sd->name, "symbol_to"))
+                               sort__first_dimension = SORT_SYM_TO;
+                       else if (!strcmp(sd->name, "dso_from"))
+                               sort__first_dimension = SORT_DSO_FROM;
+                       else if (!strcmp(sd->name, "dso_to"))
+                               sort__first_dimension = SORT_DSO_TO;
+                       else if (!strcmp(sd->name, "mispredict"))
+                               sort__first_dimension = SORT_MISPREDICT;
                }
 
                list_add_tail(&sd->entry->list, &hist_entry__sort_list);
@@ -309,7 +495,6 @@ int sort_dimension__add(const char *tok)
 
                return 0;
        }
-
        return -ESRCH;
 }
 
index 3f67ae3957521aabed2bb1e2bc5931aa57f4930b..7aa72a00bc8e91eccfaa29e3b2b0875fb43bce24 100644 (file)
@@ -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,11 @@ enum sort_type {
        SORT_SYM,
        SORT_PARENT,
        SORT_CPU,
+       SORT_DSO_FROM,
+       SORT_DSO_TO,
+       SORT_SYM_FROM,
+       SORT_SYM_TO,
+       SORT_MISPREDICT,
 };
 
 /*
index 2a683d4fc9181fa4e05d34887b79d8f867411445..5866ce6b9c02b374fd18dfd5665229660e4f2858 100644 (file)
@@ -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;