Commit 57646b6f authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-4.15-20171025' of...

Merge tag 'perf-core-for-mingo-4.15-20171025' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core inline improvements from Arnaldo Carvalho de Melo:

From Milian's cover letter: (Milian Wolff)

"This series of patches completely reworks the way inline frames are
 handled.  Instead of querying for the inline nodes on-demand in the
 individual tools, we now create proper callchain nodes for inlined
 frames. The advantages this approach brings are numerous:

 - Less duplicated code in the individual browser

 - Aggregated cost for inlined frames for the --children top-down list

 - Various bug fixes that arose from querying for a srcline/symbol based on
   the IP of a sample, which will always point to the last inlined frame
   instead of the corresponding non-inlined frame

 - Overall much better support for visualizing cost for heavily-inlined C++
   code, which simply was confusing and unreliably before

 - srcline honors the global setting as to whether full paths or basenames
   should be shown

 - Caches for inlined frames and srcline information, which allow us to
   enable inline frame handling by default"
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 9b7c8547 d8a88dd2
...@@ -434,7 +434,8 @@ include::itrace.txt[] ...@@ -434,7 +434,8 @@ include::itrace.txt[]
--inline:: --inline::
If a callgraph address belongs to an inlined function, the inline stack If a callgraph address belongs to an inlined function, the inline stack
will be printed. Each entry is function name or file/line. will be printed. Each entry is function name or file/line. Enabled by
default, disable with --no-inline.
include::callchain-overhead-calculation.txt[] include::callchain-overhead-calculation.txt[]
......
...@@ -327,7 +327,8 @@ include::itrace.txt[] ...@@ -327,7 +327,8 @@ include::itrace.txt[]
--inline:: --inline::
If a callgraph address belongs to an inlined function, the inline stack If a callgraph address belongs to an inlined function, the inline stack
will be printed. Each entry has function name and file/line. will be printed. Each entry has function name and file/line. Enabled by
default, disable with --no-inline.
SEE ALSO SEE ALSO
-------- --------
......
...@@ -154,57 +154,9 @@ static void callchain_list__set_folding(struct callchain_list *cl, bool unfold) ...@@ -154,57 +154,9 @@ static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
cl->unfolded = unfold ? cl->has_children : false; cl->unfolded = unfold ? cl->has_children : false;
} }
static struct inline_node *inline_node__create(struct map *map, u64 ip)
{
struct dso *dso;
struct inline_node *node;
if (map == NULL)
return NULL;
dso = map->dso;
if (dso == NULL)
return NULL;
node = dso__parse_addr_inlines(dso,
map__rip_2objdump(map, ip));
return node;
}
static int inline__count_rows(struct inline_node *node)
{
struct inline_list *ilist;
int i = 0;
if (node == NULL)
return 0;
list_for_each_entry(ilist, &node->val, list) {
if ((ilist->filename != NULL) || (ilist->funcname != NULL))
i++;
}
return i;
}
static int callchain_list__inline_rows(struct callchain_list *chain)
{
struct inline_node *node;
int rows;
node = inline_node__create(chain->ms.map, chain->ip);
if (node == NULL)
return 0;
rows = inline__count_rows(node);
inline_node__delete(node);
return rows;
}
static int callchain_node__count_rows_rb_tree(struct callchain_node *node) static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
{ {
int n = 0, inline_rows; int n = 0;
struct rb_node *nd; struct rb_node *nd;
for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) { for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
...@@ -215,12 +167,6 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node) ...@@ -215,12 +167,6 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
list_for_each_entry(chain, &child->val, list) { list_for_each_entry(chain, &child->val, list) {
++n; ++n;
if (symbol_conf.inline_name) {
inline_rows =
callchain_list__inline_rows(chain);
n += inline_rows;
}
/* We need this because we may not have children */ /* We need this because we may not have children */
folded_sign = callchain_list__folded(chain); folded_sign = callchain_list__folded(chain);
if (folded_sign == '+') if (folded_sign == '+')
...@@ -272,7 +218,7 @@ static int callchain_node__count_rows(struct callchain_node *node) ...@@ -272,7 +218,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
{ {
struct callchain_list *chain; struct callchain_list *chain;
bool unfolded = false; bool unfolded = false;
int n = 0, inline_rows; int n = 0;
if (callchain_param.mode == CHAIN_FLAT) if (callchain_param.mode == CHAIN_FLAT)
return callchain_node__count_flat_rows(node); return callchain_node__count_flat_rows(node);
...@@ -281,10 +227,6 @@ static int callchain_node__count_rows(struct callchain_node *node) ...@@ -281,10 +227,6 @@ static int callchain_node__count_rows(struct callchain_node *node)
list_for_each_entry(chain, &node->val, list) { list_for_each_entry(chain, &node->val, list) {
++n; ++n;
if (symbol_conf.inline_name) {
inline_rows = callchain_list__inline_rows(chain);
n += inline_rows;
}
unfolded = chain->unfolded; unfolded = chain->unfolded;
} }
...@@ -432,19 +374,6 @@ static void hist_entry__init_have_children(struct hist_entry *he) ...@@ -432,19 +374,6 @@ static void hist_entry__init_have_children(struct hist_entry *he)
he->init_have_children = true; he->init_have_children = true;
} }
static void hist_entry_init_inline_node(struct hist_entry *he)
{
if (he->inline_node)
return;
he->inline_node = inline_node__create(he->ms.map, he->ip);
if (he->inline_node == NULL)
return;
he->has_children = true;
}
static bool hist_browser__toggle_fold(struct hist_browser *browser) static bool hist_browser__toggle_fold(struct hist_browser *browser)
{ {
struct hist_entry *he = browser->he_selection; struct hist_entry *he = browser->he_selection;
...@@ -476,12 +405,8 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser) ...@@ -476,12 +405,8 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
if (he->unfolded) { if (he->unfolded) {
if (he->leaf) if (he->leaf)
if (he->inline_node) he->nr_rows = callchain__count_rows(
he->nr_rows = inline__count_rows( &he->sorted_chain);
he->inline_node);
else
he->nr_rows = callchain__count_rows(
&he->sorted_chain);
else else
he->nr_rows = hierarchy_count_rows(browser, he, false); he->nr_rows = hierarchy_count_rows(browser, he, false);
...@@ -841,71 +766,6 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u ...@@ -841,71 +766,6 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u
#define LEVEL_OFFSET_STEP 3 #define LEVEL_OFFSET_STEP 3
static int hist_browser__show_inline(struct hist_browser *browser,
struct inline_node *node,
unsigned short row,
int offset)
{
struct inline_list *ilist;
char buf[1024];
int color, width, first_row;
first_row = row;
width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
list_for_each_entry(ilist, &node->val, list) {
if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
color = HE_COLORSET_NORMAL;
if (ui_browser__is_current_entry(&browser->b, row))
color = HE_COLORSET_SELECTED;
if (callchain_param.key == CCKEY_ADDRESS ||
callchain_param.key == CCKEY_SRCLINE) {
if (ilist->filename != NULL)
scnprintf(buf, sizeof(buf),
"%s:%d (inline)",
ilist->filename,
ilist->line_nr);
else
scnprintf(buf, sizeof(buf), "??");
} else if (ilist->funcname != NULL)
scnprintf(buf, sizeof(buf), "%s (inline)",
ilist->funcname);
else if (ilist->filename != NULL)
scnprintf(buf, sizeof(buf),
"%s:%d (inline)",
ilist->filename,
ilist->line_nr);
else
scnprintf(buf, sizeof(buf), "??");
ui_browser__set_color(&browser->b, color);
hist_browser__gotorc(browser, row, 0);
ui_browser__write_nstring(&browser->b, " ",
LEVEL_OFFSET_STEP + offset);
ui_browser__write_nstring(&browser->b, buf, width);
row++;
}
}
return row - first_row;
}
static size_t show_inline_list(struct hist_browser *browser, struct map *map,
u64 ip, int row, int offset)
{
struct inline_node *node;
int ret;
node = inline_node__create(map, ip);
if (node == NULL)
return 0;
ret = hist_browser__show_inline(browser, node, row, offset);
inline_node__delete(node);
return ret;
}
static int hist_browser__show_callchain_list(struct hist_browser *browser, static int hist_browser__show_callchain_list(struct hist_browser *browser,
struct callchain_node *node, struct callchain_node *node,
struct callchain_list *chain, struct callchain_list *chain,
...@@ -917,7 +777,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser, ...@@ -917,7 +777,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
char bf[1024], *alloc_str; char bf[1024], *alloc_str;
char buf[64], *alloc_str2; char buf[64], *alloc_str2;
const char *str; const char *str;
int inline_rows = 0, ret = 1; int ret = 1;
if (arg->row_offset != 0) { if (arg->row_offset != 0) {
arg->row_offset--; arg->row_offset--;
...@@ -954,12 +814,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser, ...@@ -954,12 +814,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
free(alloc_str); free(alloc_str);
free(alloc_str2); free(alloc_str2);
if (symbol_conf.inline_name) { return ret;
inline_rows = show_inline_list(browser, chain->ms.map,
chain->ip, row + 1, offset);
}
return ret + inline_rows;
} }
static bool check_percent_display(struct rb_node *node, u64 parent_total) static bool check_percent_display(struct rb_node *node, u64 parent_total)
...@@ -1383,12 +1238,6 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -1383,12 +1238,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
folded_sign = hist_entry__folded(entry); folded_sign = hist_entry__folded(entry);
} }
if (symbol_conf.inline_name &&
(!entry->has_children)) {
hist_entry_init_inline_node(entry);
folded_sign = hist_entry__folded(entry);
}
if (row_offset == 0) { if (row_offset == 0) {
struct hpp_arg arg = { struct hpp_arg arg = {
.b = &browser->b, .b = &browser->b,
...@@ -1420,8 +1269,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -1420,8 +1269,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
} }
if (first) { if (first) {
if (symbol_conf.use_callchain || if (symbol_conf.use_callchain) {
symbol_conf.inline_name) {
ui_browser__printf(&browser->b, "%c ", folded_sign); ui_browser__printf(&browser->b, "%c ", folded_sign);
width -= 2; width -= 2;
} }
...@@ -1463,15 +1311,11 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -1463,15 +1311,11 @@ static int hist_browser__show_entry(struct hist_browser *browser,
.is_current_entry = current_entry, .is_current_entry = current_entry,
}; };
if (entry->inline_node) printed += hist_browser__show_callchain(browser,
printed += hist_browser__show_inline(browser, entry, 1, row,
entry->inline_node, row, 0); hist_browser__show_callchain_entry,
else &arg,
printed += hist_browser__show_callchain(browser, hist_browser__check_output_full);
entry, 1, row,
hist_browser__show_callchain_entry,
&arg,
hist_browser__check_output_full);
} }
return printed; return printed;
......
...@@ -21,64 +21,6 @@ static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) ...@@ -21,64 +21,6 @@ static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
return ret; return ret;
} }
static size_t inline__fprintf(struct map *map, u64 ip, int left_margin,
int depth, int depth_mask, FILE *fp)
{
struct dso *dso;
struct inline_node *node;
struct inline_list *ilist;
int ret = 0, i;
if (map == NULL)
return 0;
dso = map->dso;
if (dso == NULL)
return 0;
node = dso__parse_addr_inlines(dso,
map__rip_2objdump(map, ip));
if (node == NULL)
return 0;
list_for_each_entry(ilist, &node->val, list) {
if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
ret += callchain__fprintf_left_margin(fp, left_margin);
for (i = 0; i < depth; i++) {
if (depth_mask & (1 << i))
ret += fprintf(fp, "|");
else
ret += fprintf(fp, " ");
ret += fprintf(fp, " ");
}
if (callchain_param.key == CCKEY_ADDRESS ||
callchain_param.key == CCKEY_SRCLINE) {
if (ilist->filename != NULL)
ret += fprintf(fp, "%s:%d (inline)",
ilist->filename,
ilist->line_nr);
else
ret += fprintf(fp, "??");
} else if (ilist->funcname != NULL)
ret += fprintf(fp, "%s (inline)",
ilist->funcname);
else if (ilist->filename != NULL)
ret += fprintf(fp, "%s:%d (inline)",
ilist->filename,
ilist->line_nr);
else
ret += fprintf(fp, "??");
ret += fprintf(fp, "\n");
}
}
inline_node__delete(node);
return ret;
}
static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
int left_margin) int left_margin)
{ {
...@@ -137,9 +79,6 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node, ...@@ -137,9 +79,6 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
fputc('\n', fp); fputc('\n', fp);
free(alloc_str); free(alloc_str);
if (symbol_conf.inline_name)
ret += inline__fprintf(chain->ms.map, chain->ip,
left_margin, depth, depth_mask, fp);
return ret; return ret;
} }
...@@ -314,13 +253,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, ...@@ -314,13 +253,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
if (++entries_printed == callchain_param.print_limit) if (++entries_printed == callchain_param.print_limit)
break; break;
if (symbol_conf.inline_name)
ret += inline__fprintf(chain->ms.map,
chain->ip,
left_margin,
0, 0,
fp);
} }
root = &cnode->rb_root; root = &cnode->rb_root;
} }
...@@ -600,7 +532,6 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, ...@@ -600,7 +532,6 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
{ {
int ret; int ret;
int callchain_ret = 0; int callchain_ret = 0;
int inline_ret = 0;
struct perf_hpp hpp = { struct perf_hpp hpp = {
.buf = bf, .buf = bf,
.size = size, .size = size,
...@@ -622,13 +553,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, ...@@ -622,13 +553,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
callchain_ret = hist_entry_callchain__fprintf(he, total_period, callchain_ret = hist_entry_callchain__fprintf(he, total_period,
0, fp); 0, fp);
if (callchain_ret == 0 && symbol_conf.inline_name) { ret += callchain_ret;
inline_ret = inline__fprintf(he->ms.map, he->ip, 0, 0, 0, fp);
ret += inline_ret;
if (inline_ret > 0)
ret += fprintf(fp, "\n");
} else
ret += callchain_ret;
return ret; return ret;
} }
......
...@@ -566,6 +566,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) ...@@ -566,6 +566,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
call->ip = cursor_node->ip; call->ip = cursor_node->ip;
call->ms.sym = cursor_node->sym; call->ms.sym = cursor_node->sym;
call->ms.map = map__get(cursor_node->map); call->ms.map = map__get(cursor_node->map);
call->srcline = cursor_node->srcline;
if (cursor_node->branch) { if (cursor_node->branch) {
call->branch_count = 1; call->branch_count = 1;
...@@ -644,103 +645,120 @@ enum match_result { ...@@ -644,103 +645,120 @@ enum match_result {
MATCH_GT, MATCH_GT,
}; };
static enum match_result match_chain_srcline(struct callchain_cursor_node *node, static enum match_result match_chain_strings(const char *left,
struct callchain_list *cnode) const char *right)
{ {
char *left = NULL;
char *right = NULL;
enum match_result ret = MATCH_EQ; enum match_result ret = MATCH_EQ;
int cmp; int cmp;
if (cnode->ms.map)
left = get_srcline(cnode->ms.map->dso,
map__rip_2objdump(cnode->ms.map, cnode->ip),
cnode->ms.sym, true, false);
if (node->map)
right = get_srcline(node->map->dso,
map__rip_2objdump(node->map, node->ip),
node->sym, true, false);
if (left && right) if (left && right)
cmp = strcmp(left, right); cmp = strcmp(left, right);
else if (!left && right) else if (!left && right)
cmp = 1; cmp = 1;
else if (left && !right) else if (left && !right)
cmp = -1; cmp = -1;
else if (cnode->ip == node->ip)
cmp = 0;
else else
cmp = (cnode->ip < node->ip) ? -1 : 1; return MATCH_ERROR;
if (cmp != 0) if (cmp != 0)
ret = cmp < 0 ? MATCH_LT : MATCH_GT; ret = cmp < 0 ? MATCH_LT : MATCH_GT;
free_srcline(left);
free_srcline(right);
return ret; return ret;
} }
/*
* We need to always use relative addresses because we're aggregating
* callchains from multiple threads, i.e. different address spaces, so
* comparing absolute addresses make no sense as a symbol in a DSO may end up
* in a different address when used in a different binary or even the same
* binary but with some sort of address randomization technique, thus we need
* to compare just relative addresses. -acme
*/
static enum match_result match_chain_dso_addresses(struct map *left_map, u64 left_ip,
struct map *right_map, u64 right_ip)
{
struct dso *left_dso = left_map ? left_map->dso : NULL;
struct dso *right_dso = right_map ? right_map->dso : NULL;
if (left_dso != right_dso)
return left_dso < right_dso ? MATCH_LT : MATCH_GT;
if (left_ip != right_ip)
return left_ip < right_ip ? MATCH_LT : MATCH_GT;
return MATCH_EQ;
}
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; enum match_result match = MATCH_ERROR;
u64 left, right;
struct dso *left_dso = NULL;
struct dso *right_dso = NULL;
if (callchain_param.key == CCKEY_SRCLINE) {
enum match_result match = match_chain_srcline(node, cnode);
switch (callchain_param.key) {
case CCKEY_SRCLINE:
match = match_chain_strings(cnode->srcline, node->srcline);
if (match != MATCH_ERROR) if (match != MATCH_ERROR)
return match; break;
} /* otherwise fall-back to symbol-based comparison below */
__fallthrough;
if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) { case CCKEY_FUNCTION:
left = cnode->ms.sym->start; if (node->sym && cnode->ms.sym) {
right = sym->start; /*
left_dso = cnode->ms.map->dso; * Compare inlined frames based on their symbol name
right_dso = node->map->dso; * because different inlined frames will have the same
} else { * symbol start. Otherwise do a faster comparison based
left = cnode->ip; * on the symbol start address.
right = node->ip; */
if (cnode->ms.sym->inlined || node->sym->inlined) {
match = match_chain_strings(cnode->ms.sym->name,
node->sym->name);
if (match != MATCH_ERROR)
break;
} else {
match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
node->map, node->sym->start);
break;
}
}
/* otherwise fall-back to IP-based comparison below */
__fallthrough;
case CCKEY_ADDRESS:
default:
match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->map, node->ip);
break;
} }
if (left == right && left_dso == right_dso) { if (match == MATCH_EQ && node->branch) {
if (node->branch) { cnode->branch_count++;
cnode->branch_count++;
if (node->branch_from) { if (node->branch_from) {
/* /*
* It's "to" of a branch * It's "to" of a branch
*/ */
cnode->brtype_stat.branch_to = true; cnode->brtype_stat.branch_to = true;
if (node->branch_flags.predicted) if (node->branch_flags.predicted)
cnode->predicted_count++; cnode->predicted_count++;
if (node->branch_flags.abort) if (node->branch_flags.abort)
cnode->abort_count++; cnode->abort_count++;
branch_type_count(&cnode->brtype_stat, branch_type_count(&cnode->brtype_stat,
&node->branch_flags, &node->branch_flags,
node->branch_from, node->branch_from,
node->ip); node->ip);
} else { } else {
/* /*
* It's "from" of a branch * It's "from" of a branch
*/ */
cnode->brtype_stat.branch_to = false; cnode->brtype_stat.branch_to = false;
cnode->cycles_count += cnode->cycles_count += node->branch_flags.cycles;
node->branch_flags.cycles; cnode->iter_count += node->nr_loop_iter;
cnode->iter_count += node->nr_loop_iter; cnode->iter_cycles += node->iter_cycles;
cnode->iter_cycles += node->iter_cycles;
}
} }
return MATCH_EQ;
} }
return left > right ? MATCH_GT : MATCH_LT; return match;
} }
/* /*
...@@ -969,7 +987,7 @@ merge_chain_branch(struct callchain_cursor *cursor, ...@@ -969,7 +987,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
list_for_each_entry_safe(list, next_list, &src->val, list) { list_for_each_entry_safe(list, next_list, &src->val, list) {
callchain_cursor_append(cursor, list->ip, callchain_cursor_append(cursor, list->ip,
list->ms.map, list->ms.sym, list->ms.map, list->ms.sym,
false, NULL, 0, 0, 0); false, NULL, 0, 0, 0, list->srcline);
list_del(&list->list); list_del(&list->list);
map__zput(list->ms.map); map__zput(list->ms.map);
free(list); free(list);
...@@ -1009,7 +1027,8 @@ int callchain_merge(struct callchain_cursor *cursor, ...@@ -1009,7 +1027,8 @@ int callchain_merge(struct callchain_cursor *cursor,
int callchain_cursor_append(struct callchain_cursor *cursor, int callchain_cursor_append(struct callchain_cursor *cursor,
u64 ip, struct map *map, struct symbol *sym, u64 ip, struct map *map, struct symbol *sym,
bool branch, struct branch_flags *flags, bool branch, struct branch_flags *flags,
int nr_loop_iter, u64 iter_cycles, u64 branch_from) int nr_loop_iter, u64 iter_cycles, u64 branch_from,
const char *srcline)
{ {
struct callchain_cursor_node *node = *cursor->last; struct callchain_cursor_node *node = *cursor->last;
...@@ -1028,6 +1047,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor, ...@@ -1028,6 +1047,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
node->branch = branch; node->branch = branch;
node->nr_loop_iter = nr_loop_iter; node->nr_loop_iter = nr_loop_iter;
node->iter_cycles = iter_cycles; node->iter_cycles = iter_cycles;
node->srcline = srcline;
if (flags) if (flags)
memcpy(&node->branch_flags, flags, memcpy(&node->branch_flags, flags,
...@@ -1070,6 +1090,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * ...@@ -1070,6 +1090,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
{ {
al->map = node->map; al->map = node->map;
al->sym = node->sym; al->sym = node->sym;
al->srcline = node->srcline;
if (node->map) if (node->map)
al->addr = node->map->map_ip(node->map, node->ip); al->addr = node->map->map_ip(node->map, node->ip);
else else
...@@ -1115,16 +1136,15 @@ char *callchain_list__sym_name(struct callchain_list *cl, ...@@ -1115,16 +1136,15 @@ char *callchain_list__sym_name(struct callchain_list *cl,
int printed; int printed;
if (cl->ms.sym) { if (cl->ms.sym) {
if (show_srcline && cl->ms.map && !cl->srcline) const char *inlined = cl->ms.sym->inlined ? " (inlined)" : "";
cl->srcline = get_srcline(cl->ms.map->dso,
map__rip_2objdump(cl->ms.map, if (show_srcline && cl->srcline)
cl->ip), printed = scnprintf(bf, bfsize, "%s %s%s",
cl->ms.sym, false, show_addr); cl->ms.sym->name, cl->srcline,
if (cl->srcline) inlined);
printed = scnprintf(bf, bfsize, "%s %s",
cl->ms.sym->name, cl->srcline);
else else
printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name); printed = scnprintf(bf, bfsize, "%s%s",
cl->ms.sym->name, inlined);
} else } else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
...@@ -1532,7 +1552,7 @@ int callchain_cursor__copy(struct callchain_cursor *dst, ...@@ -1532,7 +1552,7 @@ int callchain_cursor__copy(struct callchain_cursor *dst,
node->branch, &node->branch_flags, node->branch, &node->branch_flags,
node->nr_loop_iter, node->nr_loop_iter,
node->iter_cycles, node->iter_cycles,
node->branch_from); node->branch_from, node->srcline);
if (rc) if (rc)
break; break;
......
...@@ -121,7 +121,7 @@ struct callchain_list { ...@@ -121,7 +121,7 @@ struct callchain_list {
u64 iter_count; u64 iter_count;
u64 iter_cycles; u64 iter_cycles;
struct branch_type_stat brtype_stat; struct branch_type_stat brtype_stat;
char *srcline; const char *srcline;
struct list_head list; struct list_head list;
}; };
...@@ -135,6 +135,7 @@ struct callchain_cursor_node { ...@@ -135,6 +135,7 @@ struct callchain_cursor_node {
u64 ip; u64 ip;
struct map *map; struct map *map;
struct symbol *sym; struct symbol *sym;
const char *srcline;
bool branch; bool branch;
struct branch_flags branch_flags; struct branch_flags branch_flags;
u64 branch_from; u64 branch_from;
...@@ -201,7 +202,8 @@ static inline void callchain_cursor_reset(struct callchain_cursor *cursor) ...@@ -201,7 +202,8 @@ static inline void callchain_cursor_reset(struct callchain_cursor *cursor)
int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
struct map *map, struct symbol *sym, struct map *map, struct symbol *sym,
bool branch, struct branch_flags *flags, bool branch, struct branch_flags *flags,
int nr_loop_iter, u64 iter_cycles, u64 branch_from); int nr_loop_iter, u64 iter_cycles, u64 branch_from,
const char *srcline);
/* Close a cursor writing session. Initialize for the reader */ /* Close a cursor writing session. Initialize for the reader */
static inline void callchain_cursor_commit(struct callchain_cursor *cursor) static inline void callchain_cursor_commit(struct callchain_cursor *cursor)
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "compress.h" #include "compress.h"
#include "path.h" #include "path.h"
#include "symbol.h" #include "symbol.h"
#include "srcline.h"
#include "dso.h" #include "dso.h"
#include "machine.h" #include "machine.h"
#include "auxtrace.h" #include "auxtrace.h"
...@@ -1201,6 +1202,8 @@ struct dso *dso__new(const char *name) ...@@ -1201,6 +1202,8 @@ struct dso *dso__new(const char *name)
for (i = 0; i < MAP__NR_TYPES; ++i) for (i = 0; i < MAP__NR_TYPES; ++i)
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
dso->data.cache = RB_ROOT; dso->data.cache = RB_ROOT;
dso->inlined_nodes = RB_ROOT;
dso->srclines = RB_ROOT;
dso->data.fd = -1; dso->data.fd = -1;
dso->data.status = DSO_DATA_STATUS_UNKNOWN; dso->data.status = DSO_DATA_STATUS_UNKNOWN;
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
...@@ -1232,6 +1235,10 @@ void dso__delete(struct dso *dso) ...@@ -1232,6 +1235,10 @@ void dso__delete(struct dso *dso)
if (!RB_EMPTY_NODE(&dso->rb_node)) if (!RB_EMPTY_NODE(&dso->rb_node))
pr_err("DSO %s is still in rbtree when being deleted!\n", pr_err("DSO %s is still in rbtree when being deleted!\n",
dso->long_name); dso->long_name);
/* free inlines first, as they reference symbols */
inlines__tree_delete(&dso->inlined_nodes);
srcline__tree_delete(&dso->srclines);
for (i = 0; i < MAP__NR_TYPES; ++i) for (i = 0; i < MAP__NR_TYPES; ++i)
symbols__delete(&dso->symbols[i]); symbols__delete(&dso->symbols[i]);
......
...@@ -141,6 +141,8 @@ struct dso { ...@@ -141,6 +141,8 @@ struct dso {
struct rb_root *root; /* root of rbtree that rb_node is in */ struct rb_root *root; /* root of rbtree that rb_node is in */
struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbols[MAP__NR_TYPES];
struct rb_root symbol_names[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES];
struct rb_root inlined_nodes;
struct rb_root srclines;
struct { struct {
u64 addr; u64 addr;
struct symbol *symbol; struct symbol *symbol;
......
...@@ -1604,6 +1604,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al, ...@@ -1604,6 +1604,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
al->sym = NULL; al->sym = NULL;
al->cpu = sample->cpu; al->cpu = sample->cpu;
al->socket = -1; al->socket = -1;
al->srcline = NULL;
if (al->cpu >= 0) { if (al->cpu >= 0) {
struct perf_env *env = machine->env; struct perf_env *env = machine->env;
......
...@@ -157,7 +157,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, ...@@ -157,7 +157,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
} }
} }
if (print_dso) { if (print_dso && (!node->sym || !node->sym->inlined)) {
printed += fprintf(fp, " ("); printed += fprintf(fp, " (");
printed += map__fprintf_dsoname(node->map, fp); printed += map__fprintf_dsoname(node->map, fp);
printed += fprintf(fp, ")"); printed += fprintf(fp, ")");
...@@ -166,41 +166,12 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, ...@@ -166,41 +166,12 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
if (print_srcline) if (print_srcline)
printed += map__fprintf_srcline(node->map, addr, "\n ", fp); printed += map__fprintf_srcline(node->map, addr, "\n ", fp);
if (node->sym && node->sym->inlined)
printed += fprintf(fp, " (inlined)");
if (!print_oneline) if (!print_oneline)
printed += fprintf(fp, "\n"); printed += fprintf(fp, "\n");
if (symbol_conf.inline_name && node->map) {
struct inline_node *inode;
addr = map__rip_2objdump(node->map, node->ip),
inode = dso__parse_addr_inlines(node->map->dso, addr);
if (inode) {
struct inline_list *ilist;
list_for_each_entry(ilist, &inode->val, list) {
if (print_arrow)
printed += fprintf(fp, " <-");
/* IP is same, just skip it */
if (print_ip)
printed += fprintf(fp, "%c%16s",
s, "");
if (print_sym)
printed += fprintf(fp, " %s",
ilist->funcname);
if (print_srcline)
printed += fprintf(fp, "\n %s:%d",
ilist->filename,
ilist->line_nr);
if (!print_oneline)
printed += fprintf(fp, "\n");
}
inline_node__delete(inode);
}
}
if (symbol_conf.bt_stop_list && if (symbol_conf.bt_stop_list &&
node->sym && node->sym &&
strlist__has_entry(symbol_conf.bt_stop_list, strlist__has_entry(symbol_conf.bt_stop_list,
......
...@@ -596,6 +596,7 @@ __hists__add_entry(struct hists *hists, ...@@ -596,6 +596,7 @@ __hists__add_entry(struct hists *hists,
.map = al->map, .map = al->map,
.sym = al->sym, .sym = al->sym,
}, },
.srcline = al->srcline ? strdup(al->srcline) : NULL,
.socket = al->socket, .socket = al->socket,
.cpu = al->cpu, .cpu = al->cpu,
.cpumode = al->cpumode, .cpumode = al->cpumode,
...@@ -950,6 +951,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, ...@@ -950,6 +951,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
.map = al->map, .map = al->map,
.sym = al->sym, .sym = al->sym,
}, },
.srcline = al->srcline ? strdup(al->srcline) : NULL,
.parent = iter->parent, .parent = iter->parent,
.raw_data = sample->raw_data, .raw_data = sample->raw_data,
.raw_size = sample->raw_size, .raw_size = sample->raw_size,
...@@ -1141,11 +1143,6 @@ void hist_entry__delete(struct hist_entry *he) ...@@ -1141,11 +1143,6 @@ void hist_entry__delete(struct hist_entry *he)
zfree(&he->mem_info); zfree(&he->mem_info);
} }
if (he->inline_node) {
inline_node__delete(he->inline_node);
he->inline_node = NULL;
}
zfree(&he->stat_acc); zfree(&he->stat_acc);
free_srcline(he->srcline); free_srcline(he->srcline);
if (he->srcfile && he->srcfile[0]) if (he->srcfile && he->srcfile[0])
......
...@@ -1709,6 +1709,26 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, ...@@ -1709,6 +1709,26 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
return mi; return mi;
} }
static char *callchain_srcline(struct map *map, struct symbol *sym, u64 ip)
{
char *srcline = NULL;
if (!map || callchain_param.key == CCKEY_FUNCTION)
return srcline;
srcline = srcline__tree_find(&map->dso->srclines, ip);
if (!srcline) {
bool show_sym = false;
bool show_addr = callchain_param.key == CCKEY_ADDRESS;
srcline = get_srcline(map->dso, map__rip_2objdump(map, ip),
sym, show_sym, show_addr);
srcline__tree_insert(&map->dso->srclines, ip, srcline);
}
return srcline;
}
struct iterations { struct iterations {
int nr_loop_iter; int nr_loop_iter;
u64 cycles; u64 cycles;
...@@ -1728,6 +1748,7 @@ static int add_callchain_ip(struct thread *thread, ...@@ -1728,6 +1748,7 @@ static int add_callchain_ip(struct thread *thread,
struct addr_location al; struct addr_location al;
int nr_loop_iter = 0; int nr_loop_iter = 0;
u64 iter_cycles = 0; u64 iter_cycles = 0;
const char *srcline = NULL;
al.filtered = 0; al.filtered = 0;
al.sym = NULL; al.sym = NULL;
...@@ -1783,9 +1804,10 @@ static int add_callchain_ip(struct thread *thread, ...@@ -1783,9 +1804,10 @@ static int add_callchain_ip(struct thread *thread,
iter_cycles = iter->cycles; iter_cycles = iter->cycles;
} }
srcline = callchain_srcline(al.map, al.sym, al.addr);
return callchain_cursor_append(cursor, al.addr, al.map, al.sym, return callchain_cursor_append(cursor, al.addr, al.map, al.sym,
branch, flags, nr_loop_iter, branch, flags, nr_loop_iter,
iter_cycles, branch_from); iter_cycles, branch_from, srcline);
} }
struct branch_info *sample__resolve_bstack(struct perf_sample *sample, struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
...@@ -2098,15 +2120,54 @@ static int thread__resolve_callchain_sample(struct thread *thread, ...@@ -2098,15 +2120,54 @@ static int thread__resolve_callchain_sample(struct thread *thread,
return 0; return 0;
} }
static int append_inlines(struct callchain_cursor *cursor,
struct map *map, struct symbol *sym, u64 ip)
{
struct inline_node *inline_node;
struct inline_list *ilist;
u64 addr;
int ret = 1;
if (!symbol_conf.inline_name || !map || !sym)
return ret;
addr = map__rip_2objdump(map, ip);
inline_node = inlines__tree_find(&map->dso->inlined_nodes, addr);
if (!inline_node) {
inline_node = dso__parse_addr_inlines(map->dso, addr, sym);
if (!inline_node)
return ret;
inlines__tree_insert(&map->dso->inlined_nodes, inline_node);
}
list_for_each_entry(ilist, &inline_node->val, list) {
ret = callchain_cursor_append(cursor, ip, map,
ilist->symbol, false,
NULL, 0, 0, 0, ilist->srcline);
if (ret != 0)
return ret;
}
return ret;
}
static int unwind_entry(struct unwind_entry *entry, void *arg) static int unwind_entry(struct unwind_entry *entry, void *arg)
{ {
struct callchain_cursor *cursor = arg; struct callchain_cursor *cursor = arg;
const char *srcline = NULL;
if (symbol_conf.hide_unresolved && entry->sym == NULL) if (symbol_conf.hide_unresolved && entry->sym == NULL)
return 0; return 0;
if (append_inlines(cursor, entry->map, entry->sym, entry->ip) == 0)
return 0;
srcline = callchain_srcline(entry->map, entry->sym, entry->ip);
return callchain_cursor_append(cursor, entry->ip, return callchain_cursor_append(cursor, entry->ip,
entry->map, entry->sym, entry->map, entry->sym,
false, NULL, 0, 0, 0); false, NULL, 0, 0, 0, srcline);
} }
static int thread__resolve_callchain_unwind(struct thread *thread, static int thread__resolve_callchain_unwind(struct thread *thread,
......
...@@ -225,6 +225,9 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) ...@@ -225,6 +225,9 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
if (sym_l == sym_r) if (sym_l == sym_r)
return 0; return 0;
if (sym_l->inlined || sym_r->inlined)
return strcmp(sym_l->name, sym_r->name);
if (sym_l->start != sym_r->start) if (sym_l->start != sym_r->start)
return (int64_t)(sym_r->start - sym_l->start); return (int64_t)(sym_r->start - sym_l->start);
...@@ -283,6 +286,9 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, ...@@ -283,6 +286,9 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
ret += repsep_snprintf(bf + ret, size - ret, "%.*s", ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret, width - ret,
sym->name); sym->name);
if (sym->inlined)
ret += repsep_snprintf(bf + ret, size - ret,
" (inlined)");
} }
} else { } else {
size_t len = BITS_PER_LONG / 4; size_t len = BITS_PER_LONG / 4;
......
...@@ -129,7 +129,6 @@ struct hist_entry { ...@@ -129,7 +129,6 @@ struct hist_entry {
}; };
char *srcline; char *srcline;
char *srcfile; char *srcfile;
struct inline_node *inline_node;
struct symbol *parent; struct symbol *parent;
struct branch_info *branch_info; struct branch_info *branch_info;
struct hists *hists; struct hists *hists;
......
This diff is collapsed.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define PERF_SRCLINE_H #define PERF_SRCLINE_H
#include <linux/list.h> #include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/types.h> #include <linux/types.h>
struct dso; struct dso;
...@@ -14,21 +15,38 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, ...@@ -14,21 +15,38 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool show_addr, bool unwind_inlines); bool show_sym, bool show_addr, bool unwind_inlines);
void free_srcline(char *srcline); void free_srcline(char *srcline);
/* insert the srcline into the DSO, which will take ownership */
void srcline__tree_insert(struct rb_root *tree, u64 addr, char *srcline);
/* find previously inserted srcline */
char *srcline__tree_find(struct rb_root *tree, u64 addr);
/* delete all srclines within the tree */
void srcline__tree_delete(struct rb_root *tree);
#define SRCLINE_UNKNOWN ((char *) "??:0") #define SRCLINE_UNKNOWN ((char *) "??:0")
struct inline_list { struct inline_list {
char *filename; struct symbol *symbol;
char *funcname; char *srcline;
unsigned int line_nr;
struct list_head list; struct list_head list;
}; };
struct inline_node { struct inline_node {
u64 addr; u64 addr;
struct list_head val; struct list_head val;
struct rb_node rb_node;
}; };
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr); /* parse inlined frames for the given address */
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
struct symbol *sym);
/* free resources associated to the inline node list */
void inline_node__delete(struct inline_node *node); void inline_node__delete(struct inline_node *node);
/* insert the inline node list into the DSO, which will take ownership */
void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines);
/* find previously inserted inline node list */
struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr);
/* delete all nodes within the tree of inline_node s */
void inlines__tree_delete(struct rb_root *tree);
#endif /* PERF_SRCLINE_H */ #endif /* PERF_SRCLINE_H */
...@@ -45,6 +45,7 @@ struct symbol_conf symbol_conf = { ...@@ -45,6 +45,7 @@ struct symbol_conf symbol_conf = {
.show_hist_headers = true, .show_hist_headers = true,
.symfs = "", .symfs = "",
.event_group = true, .event_group = true,
.inline_name = true,
}; };
static enum dso_binary_type binary_type_symtab[] = { static enum dso_binary_type binary_type_symtab[] = {
......
...@@ -59,6 +59,7 @@ struct symbol { ...@@ -59,6 +59,7 @@ struct symbol {
u8 binding; u8 binding;
u8 idle:1; u8 idle:1;
u8 ignore:1; u8 ignore:1;
u8 inlined:1;
u8 arch_sym; u8 arch_sym;
char name[0]; char name[0];
}; };
...@@ -208,6 +209,7 @@ struct addr_location { ...@@ -208,6 +209,7 @@ struct addr_location {
struct thread *thread; struct thread *thread;
struct map *map; struct map *map;
struct symbol *sym; struct symbol *sym;
const char *srcline;
u64 addr; u64 addr;
char level; char level;
u8 filtered; u8 filtered;
......
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