perf report: Add "addr_from" and "addr_to" sort dimensions
authorStephane Eranian <eranian@google.com>
Tue, 8 Feb 2022 21:16:37 +0000 (13:16 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 16 Feb 2022 14:21:22 +0000 (11:21 -0300)
With the existing symbol_from/symbol_to, branches captured in the same
function would be collapsed into a single function if the latencies
associated with the each branch (cycles) were all the same.  That is the
case on Intel Broadwell, for instance. Since Intel Skylake, the latency
is captured by hardware and therefore is used to disambiguate branches.

Add addr_from/addr_to sort dimensions to sort branches based on their
addresses and not the function there are in. The output is still the
function name but the offset within the function is provided to uniquely
identify each branch.  These new sort dimensions also help with annotate
because they create different entries in the histogram which, in turn,
generates proper branch annotations.

Here is an example using AMD's branch sampling:

  $ perf record -a -b -c 1000037 -e cpu/branch-brs/ test_prg

  $ perf report
  Samples: 6M of event 'cpu/branch-brs/', Event count (approx.): 6901276
  Overhead  Command          Source Shared Object  Source Symbol                                   Target Symbol                                   Basic Block Cycle
    99.65%  test_prg    test_prg              [.] test_thread                                 [.] test_thread                                 -
     0.02%  test_prg         [kernel.vmlinux]      [k] asm_sysvec_apic_timer_interrupt             [k] error_entry                                 -

  $ perf report -F overhead,comm,dso,addr_from,addr_to
  Samples: 6M of event 'cpu/branch-brs/', Event count (approx.): 6901276
  Overhead  Command          Shared Object     Source Address          Target Address
     4.22%  test_prg         test_prg          [.] test_thread+0x3c    [.] test_thread+0x4
     4.13%  test_prg         test_prg          [.] test_thread+0x4     [.] test_thread+0x3a
     4.09%  test_prg         test_prg          [.] test_thread+0x3a    [.] test_thread+0x6
     4.08%  test_prg         test_prg          [.] test_thread+0x2     [.] test_thread+0x3c
     4.06%  test_prg         test_prg          [.] test_thread+0x3e    [.] test_thread+0x2
     3.87%  test_prg         test_prg          [.] test_thread+0x6     [.] test_thread+0x38
     3.84%  test_prg         test_prg          [.] test_thread         [.] test_thread+0x3e
     3.76%  test_prg         test_prg          [.] test_thread+0x1e    [.] test_thread
     3.76%  test_prg         test_prg          [.] test_thread+0x38    [.] test_thread+0x8
     3.56%  test_prg         test_prg          [.] test_thread+0x22    [.] test_thread+0x1e
     3.54%  test_prg         test_prg          [.] test_thread+0x8     [.] test_thread+0x36
     3.47%  test_prg         test_prg          [.] test_thread+0x1c    [.] test_thread+0x22
     3.45%  test_prg         test_prg          [.] test_thread+0x36    [.] test_thread+0xa
     3.28%  test_prg         test_prg          [.] test_thread+0x24    [.] test_thread+0x1c
     3.25%  test_prg         test_prg          [.] test_thread+0xa     [.] test_thread+0x34
     3.24%  test_prg         test_prg          [.] test_thread+0x1a    [.] test_thread+0x24
     3.20%  test_prg         test_prg          [.] test_thread+0x34    [.] test_thread+0xc
     3.04%  test_prg         test_prg          [.] test_thread+0x26    [.] test_thread+0x1a
     3.01%  test_prg         test_prg          [.] test_thread+0xc     [.] test_thread+0x32
     2.98%  test_prg         test_prg          [.] test_thread+0x18    [.] test_thread+0x26
     2.94%  test_prg         test_prg          [.] test_thread+0x32    [.] test_thread+0xe
     2.76%  test_prg         test_prg          [.] test_thread+0x28    [.] test_thread+0x18
     2.73%  test_prg         test_prg          [.] test_thread+0xe     [.] test_thread+0x30
     2.67%  test_prg         test_prg          [.] test_thread+0x30    [.] test_thread+0x10
     2.67%  test_prg         test_prg          [.] test_thread+0x16    [.] test_thread+0x28
     2.46%  test_prg         test_prg          [.] test_thread+0x10    [.] test_thread+0x2e
     2.44%  test_prg         test_prg          [.] test_thread+0x2a    [.] test_thread+0x16
     2.38%  test_prg         test_prg          [.] test_thread+0x14    [.] test_thread+0x2a
     2.32%  test_prg         test_prg          [.] test_thread+0x2e    [.] test_thread+0x12
     2.28%  test_prg         test_prg          [.] test_thread+0x12    [.] test_thread+0x2c
     2.16%  test_prg         test_prg          [.] test_thread+0x2c    [.] test_thread+0x14
     0.02%  test_prg         [kernel.vmlinux]  [k] asm_sysvec_apic_ti+0x5  [k] error_entry

Signed-off-by: Stephane Eranian <eranian@google.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kim Phillips <kim.phillips@amd.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <songliubraving@fb.com>
Link: http://lore.kernel.org/lkml/20220208211637.2221872-13-eranian@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/sort.c
tools/perf/util/sort.h

index 0a8033b..1c085ab 100644 (file)
@@ -124,6 +124,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
                } else {
                        symlen = unresolved_col_width + 4 + 2;
                        hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
+                       hists__new_col_len(hists, HISTC_ADDR_FROM, symlen);
                        hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
                }
 
@@ -138,6 +139,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
                } else {
                        symlen = unresolved_col_width + 4 + 2;
                        hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
+                       hists__new_col_len(hists, HISTC_ADDR_TO, symlen);
                        hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
                }
 
index 2a15e22..7ed4648 100644 (file)
@@ -77,6 +77,8 @@ enum hist_column {
        HISTC_GLOBAL_INS_LAT,
        HISTC_LOCAL_P_STAGE_CYC,
        HISTC_GLOBAL_P_STAGE_CYC,
+       HISTC_ADDR_FROM,
+       HISTC_ADDR_TO,
        HISTC_NR_COLS, /* Last entry */
 };
 
index 2da081e..6d5588e 100644 (file)
@@ -990,6 +990,128 @@ struct sort_entry sort_sym_to = {
        .se_width_idx   = HISTC_SYMBOL_TO,
 };
 
+static int _hist_entry__addr_snprintf(struct map_symbol *ms,
+                                    u64 ip, char level, char *bf, size_t size,
+                                    unsigned int width)
+{
+       struct symbol *sym = ms->sym;
+       struct map *map = ms->map;
+       size_t ret = 0, offs;
+
+       ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
+       if (sym && map) {
+               if (sym->type == STT_OBJECT) {
+                       ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
+                       ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
+                                       ip - map->unmap_ip(map, sym->start));
+               } else {
+                       ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
+                                              width - ret,
+                                              sym->name);
+                       offs = ip - sym->start;
+                       if (offs)
+                               ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
+               }
+       } else {
+               size_t len = BITS_PER_LONG / 4;
+               ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
+                                      len, ip);
+       }
+
+       return ret;
+}
+
+static int hist_entry__addr_from_snprintf(struct hist_entry *he, char *bf,
+                                        size_t size, unsigned int width)
+{
+       if (he->branch_info) {
+               struct addr_map_symbol *from = &he->branch_info->from;
+
+               return _hist_entry__addr_snprintf(&from->ms, from->al_addr,
+                                                he->level, bf, size, width);
+       }
+
+       return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
+}
+
+static int hist_entry__addr_to_snprintf(struct hist_entry *he, char *bf,
+                                      size_t size, unsigned int width)
+{
+       if (he->branch_info) {
+               struct addr_map_symbol *to = &he->branch_info->to;
+
+               return _hist_entry__addr_snprintf(&to->ms, to->al_addr,
+                                                he->level, bf, size, width);
+       }
+
+       return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
+}
+
+static int64_t
+sort__addr_from_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       struct addr_map_symbol *from_l;
+       struct addr_map_symbol *from_r;
+       int64_t ret;
+
+       if (!left->branch_info || !right->branch_info)
+               return cmp_null(left->branch_info, right->branch_info);
+
+       from_l = &left->branch_info->from;
+       from_r = &right->branch_info->from;
+
+       /*
+        * comparing symbol address alone is not enough since it's a
+        * relative address within a dso.
+        */
+       ret = _sort__dso_cmp(from_l->ms.map, from_r->ms.map);
+       if (ret != 0)
+               return ret;
+
+       return _sort__addr_cmp(from_l->addr, from_r->addr);
+}
+
+static int64_t
+sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+       struct addr_map_symbol *to_l;
+       struct addr_map_symbol *to_r;
+       int64_t ret;
+
+       if (!left->branch_info || !right->branch_info)
+               return cmp_null(left->branch_info, right->branch_info);
+
+       to_l = &left->branch_info->to;
+       to_r = &right->branch_info->to;
+
+       /*
+        * comparing symbol address alone is not enough since it's a
+        * relative address within a dso.
+        */
+       ret = _sort__dso_cmp(to_l->ms.map, to_r->ms.map);
+       if (ret != 0)
+               return ret;
+
+       return _sort__addr_cmp(to_l->addr, to_r->addr);
+}
+
+struct sort_entry sort_addr_from = {
+       .se_header      = "Source Address",
+       .se_cmp         = sort__addr_from_cmp,
+       .se_snprintf    = hist_entry__addr_from_snprintf,
+       .se_filter      = hist_entry__sym_from_filter, /* shared with sym_from */
+       .se_width_idx   = HISTC_ADDR_FROM,
+};
+
+struct sort_entry sort_addr_to = {
+       .se_header      = "Target Address",
+       .se_cmp         = sort__addr_to_cmp,
+       .se_snprintf    = hist_entry__addr_to_snprintf,
+       .se_filter      = hist_entry__sym_to_filter, /* shared with sym_to */
+       .se_width_idx   = HISTC_ADDR_TO,
+};
+
+
 static int64_t
 sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
 {
@@ -1893,6 +2015,8 @@ static struct sort_dimension bstack_sort_dimensions[] = {
        DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
        DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
        DIM(SORT_SYM_IPC, "ipc_lbr", sort_sym_ipc),
+       DIM(SORT_ADDR_FROM, "addr_from", sort_addr_from),
+       DIM(SORT_ADDR_TO, "addr_to", sort_addr_to),
 };
 
 #undef DIM
@@ -3126,6 +3250,10 @@ static bool get_elide(int idx, FILE *output)
                return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
        case HISTC_DSO_TO:
                return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
+       case HISTC_ADDR_FROM:
+               return __get_elide(symbol_conf.sym_from_list, "addr_from", output);
+       case HISTC_ADDR_TO:
+               return __get_elide(symbol_conf.sym_to_list, "addr_to", output);
        default:
                break;
        }
index f994261..2ddc00d 100644 (file)
@@ -251,6 +251,8 @@ enum sort_type {
        SORT_SRCLINE_FROM,
        SORT_SRCLINE_TO,
        SORT_SYM_IPC,
+       SORT_ADDR_FROM,
+       SORT_ADDR_TO,
 
        /* memory mode specific sort keys */
        __SORT_MEMORY_MODE,