[PATCH 5/5] perf, tools, report: Add srcline_from/to branch sort keys

Andi Kleen
2016-03-28T17:46:50Z

Add srcline_from and srcline_to branch sort keys that allow
to show the source lines of a branch. That makes it much easier
to track down where particular branches happen in the program,
for example to examine branch mispredictions, or to associate
it with cycle counts:

% perf record -b -e cycles:p ./tcall
% perf report --sort srcline_from,srcline_to,mispredict
...
15.10% tcall.c:18 tcall.c:10 N
14.83% tcall.c:11 tcall.c:5 N
14.12% tcall.c:7 tcall.c:12 N
14.04% tcall.c:12 tcall.c:5 N
12.42% tcall.c:17 tcall.c:18 N
12.39% tcall.c:7 tcall.c:13 N
12.27% tcall.c:13 tcall.c:17 N
...

% perf report --sort srcline_from,srcline_to,cycles
...
17.12% tcall.c:18 tcall.c:11 1
17.01% tcall.c:12 tcall.c:6 1
16.98% tcall.c:11 tcall.c:6 1
15.91% tcall.c:17 tcall.c:18 1
6.38% tcall.c:7 tcall.c:17 7
4.80% tcall.c:7 tcall.c:12 8
4.21% tcall.c:7 tcall.c:17 8
2.67% tcall.c:7 tcall.c:12 7
2.62% tcall.c:7 tcall.c:12 10
2.10% tcall.c:7 tcall.c:17 9
1.58% tcall.c:7 tcall.c:12 6
1.44% tcall.c:7 tcall.c:12 5
1.38% tcall.c:7 tcall.c:12 9
1.06% tcall.c:7 tcall.c:17 13
1.05% tcall.c:7 tcall.c:12 4
1.01% tcall.c:7 tcall.c:17 6

Open issues:
- Some kernel symbols get misresolved.

Signed-off-by: Andi Kleen <ak@linux.intel.com>
---
tools/perf/Documentation/perf-report.txt | 3 +-
tools/perf/util/hist.c | 9 ++++
tools/perf/util/hist.h | 2 +
tools/perf/util/sort.c | 84 ++++++++++++++++++++++++++++++++
tools/perf/util/sort.h | 2 +
tools/perf/util/symbol.h | 2 +
6 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 496d42c..9cbddc2 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -103,12 +103,13 @@ OPTIONS

If --branch-stack option is used, following sort keys are also
available:
- dso_from, dso_to, symbol_from, symbol_to, mispredict.

- dso_from: name of library or module branched from
- dso_to: name of library or module branched to
- symbol_from: name of function branched from
- symbol_to: name of function branched to
+ - srcline_from: source file and line branched from
+ - srcline_to: source file and line branched to
- mispredict: "N" for predicted branch, "Y" for mispredicted branch
- in_tx: branch in TSX transaction
- abort: TSX transaction abort.
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 3d34c57..58caa69 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -117,6 +117,13 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
}
+
+ if (h->branch_info->srcline_from)
+ hists__new_col_len(hists, HISTC_SRCLINE_FROM,
+ strlen(h->branch_info->srcline_from));
+ if (h->branch_info->srcline_to)
+ hists__new_col_len(hists, HISTC_SRCLINE_TO,
+ strlen(h->branch_info->srcline_to));
}

if (h->mem_info) {
@@ -1042,6 +1049,8 @@ void hist_entry__delete(struct hist_entry *he)
if (he->branch_info) {
map__zput(he->branch_info->from.map);
map__zput(he->branch_info->to.map);
+ free_srcline(he->branch_info->srcline_from);
+ free_srcline(he->branch_info->srcline_to);
zfree(&he->branch_info);
}

diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 5885965..4dc87aa 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -52,6 +52,8 @@ enum hist_column {
HISTC_MEM_IADDR_SYMBOL,
HISTC_TRANSACTION,
HISTC_CYCLES,
+ HISTC_SRCLINE_FROM,
+ HISTC_SRCLINE_TO,
HISTC_TRACE,
HISTC_NR_COLS, /* Last entry */
};
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 47966a1..04093c4 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -360,6 +360,88 @@ struct sort_entry sort_srcline = {
.se_width_idx = HISTC_SRCLINE,
};

+/* --sort srcline_from */
+
+static int64_t
+sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ if (!left->branch_info->srcline_from) {
+ struct map *map = left->branch_info->from.map;
+ if (!map)
+ left->branch_info->srcline_from = SRCLINE_UNKNOWN;
+ else
+ left->branch_info->srcline_from = get_srcline(map->dso,
+ map__rip_2objdump(map,
+ left->branch_info->from.al_addr),
+ left->branch_info->from.sym, true);
+ }
+ if (!right->branch_info->srcline_from) {
+ struct map *map = right->branch_info->from.map;
+ if (!map)
+ right->branch_info->srcline_from = SRCLINE_UNKNOWN;
+ else
+ right->branch_info->srcline_from = get_srcline(map->dso,
+ map__rip_2objdump(map,
+ right->branch_info->from.al_addr),
+ right->branch_info->from.sym, true);
+ }
+ return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
+}
+
+static int hist_entry__srcline_from_snprintf(struct hist_entry *he, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_from);
+}
+
+struct sort_entry sort_srcline_from = {
+ .se_header = "From Source:Line",
+ .se_cmp = sort__srcline_from_cmp,
+ .se_snprintf = hist_entry__srcline_from_snprintf,
+ .se_width_idx = HISTC_SRCLINE_FROM,
+};
+
+/* --sort srcline_to */
+
+static int64_t
+sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ if (!left->branch_info->srcline_to) {
+ struct map *map = left->branch_info->to.map;
+ if (!map)
+ left->branch_info->srcline_to = SRCLINE_UNKNOWN;
+ else
+ left->branch_info->srcline_to = get_srcline(map->dso,
+ map__rip_2objdump(map,
+ left->branch_info->to.al_addr),
+ left->branch_info->from.sym, true);
+ }
+ if (!right->branch_info->srcline_to) {
+ struct map *map = right->branch_info->to.map;
+ if (!map)
+ right->branch_info->srcline_to = SRCLINE_UNKNOWN;
+ else
+ right->branch_info->srcline_to = get_srcline(map->dso,
+ map__rip_2objdump(map,
+ right->branch_info->to.al_addr),
+ right->branch_info->to.sym, true);
+ }
+ return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
+}
+
+static int hist_entry__srcline_to_snprintf(struct hist_entry *he, char *bf,
+ size_t size, unsigned int width)
+{
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, he->branch_info->srcline_to);
+}
+
+struct sort_entry sort_srcline_to = {
+ .se_header = "To Source:Line",
+ .se_cmp = sort__srcline_to_cmp,
+ .se_snprintf = hist_entry__srcline_to_snprintf,
+ .se_width_idx = HISTC_SRCLINE_TO,
+};
+
/* --sort srcfile */

static char no_srcfile[1];
@@ -1354,6 +1436,8 @@ static struct sort_dimension bstack_sort_dimensions[] = {
DIM(SORT_IN_TX, "in_tx", sort_in_tx),
DIM(SORT_ABORT, "abort", sort_abort),
DIM(SORT_CYCLES, "cycles", sort_cycles),
+ DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
+ DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
};

#undef DIM
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 3f4e359..9e3d80f 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -222,6 +222,8 @@ enum sort_type {
SORT_ABORT,
SORT_IN_TX,
SORT_CYCLES,
+ SORT_SRCLINE_FROM,
+ SORT_SRCLINE_TO,

/* memory mode specific sort keys */
__SORT_MEMORY_MODE,
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index c8b7544..fecc30c 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -177,6 +177,8 @@ struct branch_info {
struct addr_map_symbol from;
struct addr_map_symbol to;
struct branch_flags flags;
+ char *srcline_from;
+ char *srcline_to;
};

struct mem_info {
--
2.5.5