Commit 5dfa210e authored by Milian Wolff's avatar Milian Wolff Committed by Arnaldo Carvalho de Melo

perf report: Enable sorting by srcline as key

Often it is interesting to know how costly a given source line is in
total. Previously, one had to build these sums manually based on all
addresses that pointed to the same source line. This patch introduces
srcline as a sort key, which will do the aggregation for us.

Paired with the recent addition of showing inline frames, this makes
perf report much more useful for many C++ work loads.

The following shows the new feature in action. First, let's show the
status quo output when we sort by address. The result contains many hist
entries that generate the same output:

  ~~~~~~~~~~~~~~~~
  $ perf report --stdio --inline -g address
  # Children      Self  Command       Shared Object        Symbol
  # ........  ........  ............  ...................  .........................................
  #
      99.89%    35.34%  cpp-inlining  cpp-inlining         [.] main
            |
            |--64.55%--main complex:655
            |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
            |          /usr/include/c++/6.3.1/complex:664 (inline)
            |          |
            |          |--60.31%--hypot +20
            |          |          |
            |          |          |--8.52%--__hypot_finite +273
            |          |          |
            |          |          |--7.32%--__hypot_finite +411
...
             --35.34%--_start +4194346
                       __libc_start_main +241
                       |
                       |--6.65%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
                       |
                       |--2.70%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
                       |
                       |--1.69%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
  ...
  ~~~~~~~~~~~~~~~~

With this patch and `-g srcline` we instead get the following output:

  ~~~~~~~~~~~~~~~~
  $ perf report --stdio --inline -g srcline
  # Children      Self  Command       Shared Object        Symbol
  # ........  ........  ............  ...................  .........................................
  #
      99.89%    35.34%  cpp-inlining  cpp-inlining         [.] main
            |
            |--64.55%--main complex:655
            |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
            |          /usr/include/c++/6.3.1/complex:664 (inline)
            |          |
            |          |--64.02%--hypot
            |          |          |
            |          |           --59.81%--__hypot_finite
            |          |
            |           --0.53%--cabs
            |
             --35.34%--_start
                       __libc_start_main
                       |
                       |--12.48%--main random.tcc:3326
                       |          /home/milian/projects/kdab/rnd/hotspot/tests/test-clients/cpp-inlining/main.cpp:39 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
                       |          /usr/include/c++/6.3.1/bits/random.h:185 (inline)
  ...
  ~~~~~~~~~~~~~~~~
Signed-off-by: default avatarMilian Wolff <milian.wolff@kdab.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yao Jin <yao.jin@linux.intel.com>
Link: http://lkml.kernel.org/r/20170318214928.9047-1-milian.wolff@kdab.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 0d3eb0b7
...@@ -235,6 +235,7 @@ OPTIONS ...@@ -235,6 +235,7 @@ OPTIONS
sort_key can be: sort_key can be:
- function: compare on functions (default) - function: compare on functions (default)
- address: compare on individual code addresses - address: compare on individual code addresses
- srcline: compare on source filename and line number
branch can be: branch can be:
- branch: include last branch information in callgraph when available. - branch: include last branch information in callgraph when available.
......
...@@ -851,7 +851,8 @@ static int hist_browser__show_inline(struct hist_browser *browser, ...@@ -851,7 +851,8 @@ static int hist_browser__show_inline(struct hist_browser *browser,
if (ui_browser__is_current_entry(&browser->b, row)) if (ui_browser__is_current_entry(&browser->b, row))
color = HE_COLORSET_SELECTED; color = HE_COLORSET_SELECTED;
if (callchain_param.key == CCKEY_ADDRESS) { if (callchain_param.key == CCKEY_ADDRESS ||
callchain_param.key == CCKEY_SRCLINE) {
if (ilist->filename != NULL) if (ilist->filename != NULL)
scnprintf(buf, sizeof(buf), scnprintf(buf, sizeof(buf),
"%s:%d (inline)", "%s:%d (inline)",
......
...@@ -52,7 +52,8 @@ static size_t inline__fprintf(struct map *map, u64 ip, int left_margin, ...@@ -52,7 +52,8 @@ static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
ret += fprintf(fp, " "); ret += fprintf(fp, " ");
} }
if (callchain_param.key == CCKEY_ADDRESS) { if (callchain_param.key == CCKEY_ADDRESS ||
callchain_param.key == CCKEY_SRCLINE) {
if (ilist->filename != NULL) if (ilist->filename != NULL)
ret += fprintf(fp, "%s:%d (inline)", ret += fprintf(fp, "%s:%d (inline)",
ilist->filename, ilist->filename,
......
...@@ -1674,7 +1674,8 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, ...@@ -1674,7 +1674,8 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
goto next; goto next;
offset = start + i; offset = start + i;
src_line->path = get_srcline(map->dso, offset, NULL, false); src_line->path = get_srcline(map->dso, offset, NULL,
false, true);
insert_source_line(&tmp_root, src_line); insert_source_line(&tmp_root, src_line);
next: next:
......
...@@ -80,6 +80,10 @@ static int parse_callchain_sort_key(const char *value) ...@@ -80,6 +80,10 @@ static int parse_callchain_sort_key(const char *value)
callchain_param.key = CCKEY_ADDRESS; callchain_param.key = CCKEY_ADDRESS;
return 0; return 0;
} }
if (!strncmp(value, "srcline", strlen(value))) {
callchain_param.key = CCKEY_SRCLINE;
return 0;
}
if (!strncmp(value, "branch", strlen(value))) { if (!strncmp(value, "branch", strlen(value))) {
callchain_param.branch_callstack = 1; callchain_param.branch_callstack = 1;
return 0; return 0;
...@@ -510,14 +514,51 @@ enum match_result { ...@@ -510,14 +514,51 @@ enum match_result {
MATCH_GT, MATCH_GT,
}; };
static enum match_result match_chain_srcline(struct callchain_cursor_node *node,
struct callchain_list *cnode)
{
char *left = get_srcline(cnode->ms.map->dso,
map__rip_2objdump(cnode->ms.map, cnode->ip),
cnode->ms.sym, true, false);
char *right = get_srcline(node->map->dso,
map__rip_2objdump(node->map, node->ip),
node->sym, true, false);
enum match_result ret = MATCH_EQ;
int cmp;
if (left && right)
cmp = strcmp(left, right);
else if (!left && right)
cmp = 1;
else if (left && !right)
cmp = -1;
else if (cnode->ip == node->ip)
cmp = 0;
else
cmp = (cnode->ip < node->ip) ? -1 : 1;
if (cmp != 0)
ret = cmp < 0 ? MATCH_LT : MATCH_GT;
free_srcline(left);
free_srcline(right);
return ret;
}
static enum match_result match_chain(struct callchain_cursor_node *node, static enum match_result match_chain(struct callchain_cursor_node *node,
struct callchain_list *cnode) struct callchain_list *cnode)
{ {
struct symbol *sym = node->sym; struct symbol *sym = node->sym;
u64 left, right; u64 left, right;
if (cnode->ms.sym && sym && if (callchain_param.key == CCKEY_SRCLINE) {
callchain_param.key == CCKEY_FUNCTION) { enum match_result match = match_chain_srcline(node, cnode);
if (match != MATCH_ERROR)
return match;
}
if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) {
left = cnode->ms.sym->start; left = cnode->ms.sym->start;
right = sym->start; right = sym->start;
} else { } else {
...@@ -911,15 +952,16 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * ...@@ -911,15 +952,16 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
char *callchain_list__sym_name(struct callchain_list *cl, char *callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize, bool show_dso) char *bf, size_t bfsize, bool show_dso)
{ {
bool show_addr = callchain_param.key == CCKEY_ADDRESS;
bool show_srcline = show_addr || callchain_param.key == CCKEY_SRCLINE;
int printed; int printed;
if (cl->ms.sym) { if (cl->ms.sym) {
if (callchain_param.key == CCKEY_ADDRESS && if (show_srcline && cl->ms.map && !cl->srcline)
cl->ms.map && !cl->srcline)
cl->srcline = get_srcline(cl->ms.map->dso, cl->srcline = get_srcline(cl->ms.map->dso,
map__rip_2objdump(cl->ms.map, map__rip_2objdump(cl->ms.map,
cl->ip), cl->ip),
cl->ms.sym, false); cl->ms.sym, false, show_addr);
if (cl->srcline) if (cl->srcline)
printed = scnprintf(bf, bfsize, "%s %s", printed = scnprintf(bf, bfsize, "%s %s",
cl->ms.sym->name, cl->srcline); cl->ms.sym->name, cl->srcline);
......
...@@ -77,7 +77,8 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *, ...@@ -77,7 +77,8 @@ typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
enum chain_key { enum chain_key {
CCKEY_FUNCTION, CCKEY_FUNCTION,
CCKEY_ADDRESS CCKEY_ADDRESS,
CCKEY_SRCLINE
}; };
enum chain_value { enum chain_value {
......
...@@ -405,7 +405,8 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, ...@@ -405,7 +405,8 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
if (map && map->dso) { if (map && map->dso) {
srcline = get_srcline(map->dso, srcline = get_srcline(map->dso,
map__rip_2objdump(map, addr), NULL, true); map__rip_2objdump(map, addr), NULL,
true, true);
if (srcline != SRCLINE_UNKNOWN) if (srcline != SRCLINE_UNKNOWN)
ret = fprintf(fp, "%s%s", prefix, srcline); ret = fprintf(fp, "%s%s", prefix, srcline);
free_srcline(srcline); free_srcline(srcline);
......
...@@ -323,7 +323,7 @@ char *hist_entry__get_srcline(struct hist_entry *he) ...@@ -323,7 +323,7 @@ char *hist_entry__get_srcline(struct hist_entry *he)
return SRCLINE_UNKNOWN; return SRCLINE_UNKNOWN;
return get_srcline(map->dso, map__rip_2objdump(map, he->ip), return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
he->ms.sym, true); he->ms.sym, true, true);
} }
static int64_t static int64_t
...@@ -366,7 +366,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -366,7 +366,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
left->branch_info->srcline_from = get_srcline(map->dso, left->branch_info->srcline_from = get_srcline(map->dso,
map__rip_2objdump(map, map__rip_2objdump(map,
left->branch_info->from.al_addr), left->branch_info->from.al_addr),
left->branch_info->from.sym, true); left->branch_info->from.sym,
true, true);
} }
if (!right->branch_info->srcline_from) { if (!right->branch_info->srcline_from) {
struct map *map = right->branch_info->from.map; struct map *map = right->branch_info->from.map;
...@@ -376,7 +377,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -376,7 +377,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
right->branch_info->srcline_from = get_srcline(map->dso, right->branch_info->srcline_from = get_srcline(map->dso,
map__rip_2objdump(map, map__rip_2objdump(map,
right->branch_info->from.al_addr), right->branch_info->from.al_addr),
right->branch_info->from.sym, true); right->branch_info->from.sym,
true, true);
} }
return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from); return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
} }
...@@ -407,7 +409,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -407,7 +409,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
left->branch_info->srcline_to = get_srcline(map->dso, left->branch_info->srcline_to = get_srcline(map->dso,
map__rip_2objdump(map, map__rip_2objdump(map,
left->branch_info->to.al_addr), left->branch_info->to.al_addr),
left->branch_info->from.sym, true); left->branch_info->from.sym,
true, true);
} }
if (!right->branch_info->srcline_to) { if (!right->branch_info->srcline_to) {
struct map *map = right->branch_info->to.map; struct map *map = right->branch_info->to.map;
...@@ -417,7 +420,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -417,7 +420,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
right->branch_info->srcline_to = get_srcline(map->dso, right->branch_info->srcline_to = get_srcline(map->dso,
map__rip_2objdump(map, map__rip_2objdump(map,
right->branch_info->to.al_addr), right->branch_info->to.al_addr),
right->branch_info->to.sym, true); right->branch_info->to.sym,
true, true);
} }
return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to); return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
} }
...@@ -448,7 +452,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e) ...@@ -448,7 +452,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e)
return no_srcfile; return no_srcfile;
sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip), sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
e->ms.sym, false, true); e->ms.sym, false, true, true);
if (!strcmp(sf, SRCLINE_UNKNOWN)) if (!strcmp(sf, SRCLINE_UNKNOWN))
return no_srcfile; return no_srcfile;
p = strchr(sf, ':'); p = strchr(sf, ':');
......
...@@ -429,7 +429,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr, ...@@ -429,7 +429,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
#define A2L_FAIL_LIMIT 123 #define A2L_FAIL_LIMIT 123
char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool unwind_inlines) bool show_sym, bool show_addr, bool unwind_inlines)
{ {
char *file = NULL; char *file = NULL;
unsigned line = 0; unsigned line = 0;
...@@ -463,6 +463,11 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, ...@@ -463,6 +463,11 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
dso->has_srcline = 0; dso->has_srcline = 0;
dso__free_a2l(dso); dso__free_a2l(dso);
} }
if (!show_addr)
return (show_sym && sym) ?
strndup(sym->name, sym->namelen) : NULL;
if (sym) { if (sym) {
if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "", if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
addr - sym->start) < 0) addr - sym->start) < 0)
...@@ -479,9 +484,9 @@ void free_srcline(char *srcline) ...@@ -479,9 +484,9 @@ void free_srcline(char *srcline)
} }
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym) bool show_sym, bool show_addr)
{ {
return __get_srcline(dso, addr, sym, show_sym, false); return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
} }
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr) struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
......
...@@ -287,9 +287,9 @@ struct symbol; ...@@ -287,9 +287,9 @@ struct symbol;
extern bool srcline_full_filename; extern bool srcline_full_filename;
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym); bool show_sym, bool show_addr);
char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool unwind_inlines); bool show_sym, bool show_addr, bool unwind_inlines);
void free_srcline(char *srcline); void free_srcline(char *srcline);
int perf_event_paranoid(void); int perf_event_paranoid(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment