perf/core inline improvements:

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: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEELb9bqkb7Te0zijNb1lAW81NSqkAFAlnwsBYACgkQ1lAW81NS
 qkBQrxAAkARBYgZS5y2WFSDB8zIvXHBPiMmTDNwpgAIUo66YMAI9+y18VoTGBwju
 kTbD1VhLQAg1DRODuGjwnT2lKLDkl2kYhP5hwzFZYk7L8/F0jBtrPNI3uM3kOgvm
 zfmMHpKMWCdRU1gcLqmhW3gUs5ySHj7iZuZWWTlyrmTJoBzpQFRnGi89gNds5NkC
 0nY0HU099HnTgBcAkidExg5RYKhFjevYnisuBW4Ob7g34WMYgL44cVepnBzuBZdn
 uKYFFUsnd+aL4Spq6jr+W31XVv61xUKttaSKT7PUrWiDGzRdpTQZLXwzd7cyJShF
 e0TGvTtqt1xSpTU9YCZvAqfs0K5gkp0e9OddYB/P1Wne1oj7tWAa4kKFdccYFMY1
 +rbPMK0A/ms37QMx3m+/zJ+07dxuu94W/MI6k+KW+Al10QnKQSaAzLe7XxZU4p8y
 TA7KOrrZI/KYOvTyIHpeeI1LFHnn4OZy+LnkoxxH71fmCphsUsGvNIhzID1ntffK
 I/RLhpe1+9425NhViZlCoD4+vIMf9CCpJYhGNQZ6ndK+ESacuWUa4DTRJYUnF5xZ
 ducZefpm7XCTwnvkkvNN3JwmeBI0H9ePaepvLjLJP11RzZyE9fgcRdQIcPBNC76+
 a2NEkq/ean+pEFiz6S5vYrz0DSw5FTfJbfCVrVXQkYQVZcCU7F8=
 =IEC2
 -----END PGP SIGNATURE-----

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: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2017-10-25 19:07:46 +02:00
commit 57646b6fda
18 changed files with 447 additions and 427 deletions

View File

@ -434,7 +434,8 @@ include::itrace.txt[]
--inline::
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[]

View File

@ -327,7 +327,8 @@ include::itrace.txt[]
--inline::
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
--------

View File

@ -154,57 +154,9 @@ static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
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)
{
int n = 0, inline_rows;
int n = 0;
struct rb_node *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)
list_for_each_entry(chain, &child->val, list) {
++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 */
folded_sign = callchain_list__folded(chain);
if (folded_sign == '+')
@ -272,7 +218,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
{
struct callchain_list *chain;
bool unfolded = false;
int n = 0, inline_rows;
int n = 0;
if (callchain_param.mode == CHAIN_FLAT)
return callchain_node__count_flat_rows(node);
@ -281,10 +227,6 @@ static int callchain_node__count_rows(struct callchain_node *node)
list_for_each_entry(chain, &node->val, list) {
++n;
if (symbol_conf.inline_name) {
inline_rows = callchain_list__inline_rows(chain);
n += inline_rows;
}
unfolded = chain->unfolded;
}
@ -432,19 +374,6 @@ static void hist_entry__init_have_children(struct hist_entry *he)
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)
{
struct hist_entry *he = browser->he_selection;
@ -476,12 +405,8 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser)
if (he->unfolded) {
if (he->leaf)
if (he->inline_node)
he->nr_rows = inline__count_rows(
he->inline_node);
else
he->nr_rows = callchain__count_rows(
&he->sorted_chain);
he->nr_rows = callchain__count_rows(
&he->sorted_chain);
else
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
#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,
struct callchain_node *node,
struct callchain_list *chain,
@ -917,7 +777,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
char bf[1024], *alloc_str;
char buf[64], *alloc_str2;
const char *str;
int inline_rows = 0, ret = 1;
int ret = 1;
if (arg->row_offset != 0) {
arg->row_offset--;
@ -954,12 +814,7 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser,
free(alloc_str);
free(alloc_str2);
if (symbol_conf.inline_name) {
inline_rows = show_inline_list(browser, chain->ms.map,
chain->ip, row + 1, offset);
}
return ret + inline_rows;
return ret;
}
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,
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) {
struct hpp_arg arg = {
.b = &browser->b,
@ -1420,8 +1269,7 @@ static int hist_browser__show_entry(struct hist_browser *browser,
}
if (first) {
if (symbol_conf.use_callchain ||
symbol_conf.inline_name) {
if (symbol_conf.use_callchain) {
ui_browser__printf(&browser->b, "%c ", folded_sign);
width -= 2;
}
@ -1463,15 +1311,11 @@ static int hist_browser__show_entry(struct hist_browser *browser,
.is_current_entry = current_entry,
};
if (entry->inline_node)
printed += hist_browser__show_inline(browser,
entry->inline_node, row, 0);
else
printed += hist_browser__show_callchain(browser,
entry, 1, row,
hist_browser__show_callchain_entry,
&arg,
hist_browser__check_output_full);
printed += hist_browser__show_callchain(browser,
entry, 1, row,
hist_browser__show_callchain_entry,
&arg,
hist_browser__check_output_full);
}
return printed;

View File

@ -21,64 +21,6 @@ static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
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,
int left_margin)
{
@ -137,9 +79,6 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node,
fputc('\n', fp);
free(alloc_str);
if (symbol_conf.inline_name)
ret += inline__fprintf(chain->ms.map, chain->ip,
left_margin, depth, depth_mask, fp);
return ret;
}
@ -314,13 +253,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
if (++entries_printed == callchain_param.print_limit)
break;
if (symbol_conf.inline_name)
ret += inline__fprintf(chain->ms.map,
chain->ip,
left_margin,
0, 0,
fp);
}
root = &cnode->rb_root;
}
@ -600,7 +532,6 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
{
int ret;
int callchain_ret = 0;
int inline_ret = 0;
struct perf_hpp hpp = {
.buf = bf,
.size = 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,
0, fp);
if (callchain_ret == 0 && symbol_conf.inline_name) {
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;
ret += callchain_ret;
return ret;
}

View File

@ -566,6 +566,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
call->ip = cursor_node->ip;
call->ms.sym = cursor_node->sym;
call->ms.map = map__get(cursor_node->map);
call->srcline = cursor_node->srcline;
if (cursor_node->branch) {
call->branch_count = 1;
@ -644,103 +645,120 @@ enum match_result {
MATCH_GT,
};
static enum match_result match_chain_srcline(struct callchain_cursor_node *node,
struct callchain_list *cnode)
static enum match_result match_chain_strings(const char *left,
const char *right)
{
char *left = NULL;
char *right = NULL;
enum match_result ret = MATCH_EQ;
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)
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;
return MATCH_ERROR;
if (cmp != 0)
ret = cmp < 0 ? MATCH_LT : MATCH_GT;
free_srcline(left);
free_srcline(right);
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,
struct callchain_list *cnode)
{
struct symbol *sym = node->sym;
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);
enum match_result match = MATCH_ERROR;
switch (callchain_param.key) {
case CCKEY_SRCLINE:
match = match_chain_strings(cnode->srcline, node->srcline);
if (match != MATCH_ERROR)
return match;
}
if (cnode->ms.sym && sym && callchain_param.key == CCKEY_FUNCTION) {
left = cnode->ms.sym->start;
right = sym->start;
left_dso = cnode->ms.map->dso;
right_dso = node->map->dso;
} else {
left = cnode->ip;
right = node->ip;
}
if (left == right && left_dso == right_dso) {
if (node->branch) {
cnode->branch_count++;
if (node->branch_from) {
/*
* It's "to" of a branch
*/
cnode->brtype_stat.branch_to = true;
if (node->branch_flags.predicted)
cnode->predicted_count++;
if (node->branch_flags.abort)
cnode->abort_count++;
branch_type_count(&cnode->brtype_stat,
&node->branch_flags,
node->branch_from,
node->ip);
break;
/* otherwise fall-back to symbol-based comparison below */
__fallthrough;
case CCKEY_FUNCTION:
if (node->sym && cnode->ms.sym) {
/*
* Compare inlined frames based on their symbol name
* because different inlined frames will have the same
* symbol start. Otherwise do a faster comparison based
* on the symbol start address.
*/
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 {
/*
* It's "from" of a branch
*/
cnode->brtype_stat.branch_to = false;
cnode->cycles_count +=
node->branch_flags.cycles;
cnode->iter_count += node->nr_loop_iter;
cnode->iter_cycles += node->iter_cycles;
match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
node->map, node->sym->start);
break;
}
}
return MATCH_EQ;
/* 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;
}
return left > right ? MATCH_GT : MATCH_LT;
if (match == MATCH_EQ && node->branch) {
cnode->branch_count++;
if (node->branch_from) {
/*
* It's "to" of a branch
*/
cnode->brtype_stat.branch_to = true;
if (node->branch_flags.predicted)
cnode->predicted_count++;
if (node->branch_flags.abort)
cnode->abort_count++;
branch_type_count(&cnode->brtype_stat,
&node->branch_flags,
node->branch_from,
node->ip);
} else {
/*
* It's "from" of a branch
*/
cnode->brtype_stat.branch_to = false;
cnode->cycles_count += node->branch_flags.cycles;
cnode->iter_count += node->nr_loop_iter;
cnode->iter_cycles += node->iter_cycles;
}
}
return match;
}
/*
@ -969,7 +987,7 @@ merge_chain_branch(struct callchain_cursor *cursor,
list_for_each_entry_safe(list, next_list, &src->val, list) {
callchain_cursor_append(cursor, list->ip,
list->ms.map, list->ms.sym,
false, NULL, 0, 0, 0);
false, NULL, 0, 0, 0, list->srcline);
list_del(&list->list);
map__zput(list->ms.map);
free(list);
@ -1009,7 +1027,8 @@ int callchain_merge(struct callchain_cursor *cursor,
int callchain_cursor_append(struct callchain_cursor *cursor,
u64 ip, struct map *map, struct symbol *sym,
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;
@ -1028,6 +1047,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
node->branch = branch;
node->nr_loop_iter = nr_loop_iter;
node->iter_cycles = iter_cycles;
node->srcline = srcline;
if (flags)
memcpy(&node->branch_flags, flags,
@ -1070,6 +1090,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
{
al->map = node->map;
al->sym = node->sym;
al->srcline = node->srcline;
if (node->map)
al->addr = node->map->map_ip(node->map, node->ip);
else
@ -1115,16 +1136,15 @@ char *callchain_list__sym_name(struct callchain_list *cl,
int printed;
if (cl->ms.sym) {
if (show_srcline && cl->ms.map && !cl->srcline)
cl->srcline = get_srcline(cl->ms.map->dso,
map__rip_2objdump(cl->ms.map,
cl->ip),
cl->ms.sym, false, show_addr);
if (cl->srcline)
printed = scnprintf(bf, bfsize, "%s %s",
cl->ms.sym->name, cl->srcline);
const char *inlined = cl->ms.sym->inlined ? " (inlined)" : "";
if (show_srcline && cl->srcline)
printed = scnprintf(bf, bfsize, "%s %s%s",
cl->ms.sym->name, cl->srcline,
inlined);
else
printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
printed = scnprintf(bf, bfsize, "%s%s",
cl->ms.sym->name, inlined);
} else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
@ -1532,7 +1552,7 @@ int callchain_cursor__copy(struct callchain_cursor *dst,
node->branch, &node->branch_flags,
node->nr_loop_iter,
node->iter_cycles,
node->branch_from);
node->branch_from, node->srcline);
if (rc)
break;

View File

@ -121,7 +121,7 @@ struct callchain_list {
u64 iter_count;
u64 iter_cycles;
struct branch_type_stat brtype_stat;
char *srcline;
const char *srcline;
struct list_head list;
};
@ -135,6 +135,7 @@ struct callchain_cursor_node {
u64 ip;
struct map *map;
struct symbol *sym;
const char *srcline;
bool branch;
struct branch_flags branch_flags;
u64 branch_from;
@ -201,7 +202,8 @@ static inline void callchain_cursor_reset(struct callchain_cursor *cursor)
int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip,
struct map *map, struct symbol *sym,
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 */
static inline void callchain_cursor_commit(struct callchain_cursor *cursor)

View File

@ -10,6 +10,7 @@
#include "compress.h"
#include "path.h"
#include "symbol.h"
#include "srcline.h"
#include "dso.h"
#include "machine.h"
#include "auxtrace.h"
@ -1201,6 +1202,8 @@ struct dso *dso__new(const char *name)
for (i = 0; i < MAP__NR_TYPES; ++i)
dso->symbols[i] = dso->symbol_names[i] = RB_ROOT;
dso->data.cache = RB_ROOT;
dso->inlined_nodes = RB_ROOT;
dso->srclines = RB_ROOT;
dso->data.fd = -1;
dso->data.status = DSO_DATA_STATUS_UNKNOWN;
dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
@ -1232,6 +1235,10 @@ void dso__delete(struct dso *dso)
if (!RB_EMPTY_NODE(&dso->rb_node))
pr_err("DSO %s is still in rbtree when being deleted!\n",
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)
symbols__delete(&dso->symbols[i]);

View File

@ -141,6 +141,8 @@ struct dso {
struct rb_root *root; /* root of rbtree that rb_node is in */
struct rb_root symbols[MAP__NR_TYPES];
struct rb_root symbol_names[MAP__NR_TYPES];
struct rb_root inlined_nodes;
struct rb_root srclines;
struct {
u64 addr;
struct symbol *symbol;

View File

@ -1604,6 +1604,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
al->sym = NULL;
al->cpu = sample->cpu;
al->socket = -1;
al->srcline = NULL;
if (al->cpu >= 0) {
struct perf_env *env = machine->env;

View File

@ -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 += map__fprintf_dsoname(node->map, fp);
printed += fprintf(fp, ")");
@ -166,41 +166,12 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
if (print_srcline)
printed += map__fprintf_srcline(node->map, addr, "\n ", fp);
if (node->sym && node->sym->inlined)
printed += fprintf(fp, " (inlined)");
if (!print_oneline)
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 &&
node->sym &&
strlist__has_entry(symbol_conf.bt_stop_list,

View File

@ -596,6 +596,7 @@ __hists__add_entry(struct hists *hists,
.map = al->map,
.sym = al->sym,
},
.srcline = al->srcline ? strdup(al->srcline) : NULL,
.socket = al->socket,
.cpu = al->cpu,
.cpumode = al->cpumode,
@ -950,6 +951,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
.map = al->map,
.sym = al->sym,
},
.srcline = al->srcline ? strdup(al->srcline) : NULL,
.parent = iter->parent,
.raw_data = sample->raw_data,
.raw_size = sample->raw_size,
@ -1141,11 +1143,6 @@ void hist_entry__delete(struct hist_entry *he)
zfree(&he->mem_info);
}
if (he->inline_node) {
inline_node__delete(he->inline_node);
he->inline_node = NULL;
}
zfree(&he->stat_acc);
free_srcline(he->srcline);
if (he->srcfile && he->srcfile[0])

View File

@ -1709,6 +1709,26 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
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 {
int nr_loop_iter;
u64 cycles;
@ -1728,6 +1748,7 @@ static int add_callchain_ip(struct thread *thread,
struct addr_location al;
int nr_loop_iter = 0;
u64 iter_cycles = 0;
const char *srcline = NULL;
al.filtered = 0;
al.sym = NULL;
@ -1783,9 +1804,10 @@ static int add_callchain_ip(struct thread *thread,
iter_cycles = iter->cycles;
}
srcline = callchain_srcline(al.map, al.sym, al.addr);
return callchain_cursor_append(cursor, al.addr, al.map, al.sym,
branch, flags, nr_loop_iter,
iter_cycles, branch_from);
iter_cycles, branch_from, srcline);
}
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
@ -2098,15 +2120,54 @@ check_calls:
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)
{
struct callchain_cursor *cursor = arg;
const char *srcline = NULL;
if (symbol_conf.hide_unresolved && entry->sym == NULL)
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,
entry->map, entry->sym,
false, NULL, 0, 0, 0);
false, NULL, 0, 0, 0, srcline);
}
static int thread__resolve_callchain_unwind(struct thread *thread,

View File

@ -225,6 +225,9 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
if (sym_l == sym_r)
return 0;
if (sym_l->inlined || sym_r->inlined)
return strcmp(sym_l->name, sym_r->name);
if (sym_l->start != sym_r->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,
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
sym->name);
if (sym->inlined)
ret += repsep_snprintf(bf + ret, size - ret,
" (inlined)");
}
} else {
size_t len = BITS_PER_LONG / 4;

View File

@ -129,7 +129,6 @@ struct hist_entry {
};
char *srcline;
char *srcfile;
struct inline_node *inline_node;
struct symbol *parent;
struct branch_info *branch_info;
struct hists *hists;

View File

@ -33,28 +33,17 @@ static const char *dso__name(struct dso *dso)
return dso_name;
}
static int inline_list__append(char *filename, char *funcname, int line_nr,
struct inline_node *node, struct dso *dso)
static int inline_list__append(struct symbol *symbol, char *srcline,
struct inline_node *node)
{
struct inline_list *ilist;
char *demangled;
ilist = zalloc(sizeof(*ilist));
if (ilist == NULL)
return -1;
ilist->filename = filename;
ilist->line_nr = line_nr;
if (dso != NULL) {
demangled = dso__demangle_sym(dso, 0, funcname);
if (demangled == NULL) {
ilist->funcname = funcname;
} else {
ilist->funcname = demangled;
free(funcname);
}
}
ilist->symbol = symbol;
ilist->srcline = srcline;
if (callchain_param.order == ORDER_CALLEE)
list_add_tail(&ilist->list, &node->val);
@ -64,6 +53,30 @@ static int inline_list__append(char *filename, char *funcname, int line_nr,
return 0;
}
/* basename version that takes a const input string */
static const char *gnu_basename(const char *path)
{
const char *base = strrchr(path, '/');
return base ? base + 1 : path;
}
static char *srcline_from_fileline(const char *file, unsigned int line)
{
char *srcline;
if (!file)
return NULL;
if (!srcline_full_filename)
file = gnu_basename(file);
if (asprintf(&srcline, "%s:%u", file, line) < 0)
return NULL;
return srcline;
}
#ifdef HAVE_LIBBFD_SUPPORT
/*
@ -206,19 +219,59 @@ static void addr2line_cleanup(struct a2l_data *a2l)
#define MAX_INLINE_NEST 1024
static struct symbol *new_inline_sym(struct dso *dso,
struct symbol *base_sym,
const char *funcname)
{
struct symbol *inline_sym;
char *demangled = NULL;
if (dso) {
demangled = dso__demangle_sym(dso, 0, funcname);
if (demangled)
funcname = demangled;
}
if (base_sym && strcmp(funcname, base_sym->name) == 0) {
/* reuse the real, existing symbol */
inline_sym = base_sym;
/* ensure that we don't alias an inlined symbol, which could
* lead to double frees in inline_node__delete
*/
assert(!base_sym->inlined);
} else {
/* create a fake symbol for the inline frame */
inline_sym = symbol__new(base_sym ? base_sym->start : 0,
base_sym ? base_sym->end : 0,
base_sym ? base_sym->binding : 0,
funcname);
if (inline_sym)
inline_sym->inlined = 1;
}
free(demangled);
return inline_sym;
}
static int inline_list__append_dso_a2l(struct dso *dso,
struct inline_node *node)
struct inline_node *node,
struct symbol *sym)
{
struct a2l_data *a2l = dso->a2l;
char *funcname = a2l->funcname ? strdup(a2l->funcname) : NULL;
char *filename = a2l->filename ? strdup(a2l->filename) : NULL;
struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname);
char *srcline = NULL;
return inline_list__append(filename, funcname, a2l->line, node, dso);
if (a2l->filename)
srcline = srcline_from_fileline(a2l->filename, a2l->line);
return inline_list__append(inline_sym, srcline, node);
}
static int addr2line(const char *dso_name, u64 addr,
char **file, unsigned int *line, struct dso *dso,
bool unwind_inlines, struct inline_node *node)
bool unwind_inlines, struct inline_node *node,
struct symbol *sym)
{
int ret = 0;
struct a2l_data *a2l = dso->a2l;
@ -244,7 +297,7 @@ static int addr2line(const char *dso_name, u64 addr,
if (unwind_inlines) {
int cnt = 0;
if (node && inline_list__append_dso_a2l(dso, node))
if (node && inline_list__append_dso_a2l(dso, node, sym))
return 0;
while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
@ -255,7 +308,7 @@ static int addr2line(const char *dso_name, u64 addr,
a2l->filename = NULL;
if (node != NULL) {
if (inline_list__append_dso_a2l(dso, node))
if (inline_list__append_dso_a2l(dso, node, sym))
return 0;
// found at least one inline frame
ret = 1;
@ -287,7 +340,7 @@ void dso__free_a2l(struct dso *dso)
}
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
struct dso *dso)
struct dso *dso, struct symbol *sym)
{
struct inline_node *node;
@ -300,17 +353,8 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
INIT_LIST_HEAD(&node->val);
node->addr = addr;
if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node))
goto out_free_inline_node;
if (list_empty(&node->val))
goto out_free_inline_node;
addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym);
return node;
out_free_inline_node:
inline_node__delete(node);
return NULL;
}
#else /* HAVE_LIBBFD_SUPPORT */
@ -340,7 +384,8 @@ static int addr2line(const char *dso_name, u64 addr,
char **file, unsigned int *line_nr,
struct dso *dso __maybe_unused,
bool unwind_inlines __maybe_unused,
struct inline_node *node __maybe_unused)
struct inline_node *node __maybe_unused,
struct symbol *sym __maybe_unused)
{
FILE *fp;
char cmd[PATH_MAX];
@ -380,7 +425,8 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
}
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
struct dso *dso __maybe_unused)
struct dso *dso __maybe_unused,
struct symbol *sym)
{
FILE *fp;
char cmd[PATH_MAX];
@ -408,13 +454,15 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
node->addr = addr;
while (getline(&filename, &len, fp) != -1) {
char *srcline;
if (filename_split(filename, &line_nr) != 1) {
free(filename);
goto out;
}
if (inline_list__append(filename, NULL, line_nr, node,
NULL) != 0)
srcline = srcline_from_fileline(filename, line_nr);
if (inline_list__append(sym, srcline, node) != 0)
goto out;
filename = NULL;
@ -423,11 +471,6 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
out:
pclose(fp);
if (list_empty(&node->val)) {
inline_node__delete(node);
return NULL;
}
return node;
}
@ -454,19 +497,18 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
if (dso_name == NULL)
goto out;
if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL))
if (!addr2line(dso_name, addr, &file, &line, dso,
unwind_inlines, NULL, sym))
goto out;
if (asprintf(&srcline, "%s:%u",
srcline_full_filename ? file : basename(file),
line) < 0) {
free(file);
srcline = srcline_from_fileline(file, line);
free(file);
if (!srcline)
goto out;
}
dso->a2l_fails = 0;
free(file);
return srcline;
out:
@ -500,7 +542,74 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
}
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
struct srcline_node {
u64 addr;
char *srcline;
struct rb_node rb_node;
};
void srcline__tree_insert(struct rb_root *tree, u64 addr, char *srcline)
{
struct rb_node **p = &tree->rb_node;
struct rb_node *parent = NULL;
struct srcline_node *i, *node;
node = zalloc(sizeof(struct srcline_node));
if (!node) {
perror("not enough memory for the srcline node");
return;
}
node->addr = addr;
node->srcline = srcline;
while (*p != NULL) {
parent = *p;
i = rb_entry(parent, struct srcline_node, rb_node);
if (addr < i->addr)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&node->rb_node, parent, p);
rb_insert_color(&node->rb_node, tree);
}
char *srcline__tree_find(struct rb_root *tree, u64 addr)
{
struct rb_node *n = tree->rb_node;
while (n) {
struct srcline_node *i = rb_entry(n, struct srcline_node,
rb_node);
if (addr < i->addr)
n = n->rb_left;
else if (addr > i->addr)
n = n->rb_right;
else
return i->srcline;
}
return NULL;
}
void srcline__tree_delete(struct rb_root *tree)
{
struct srcline_node *pos;
struct rb_node *next = rb_first(tree);
while (next) {
pos = rb_entry(next, struct srcline_node, rb_node);
next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, tree);
free_srcline(pos->srcline);
zfree(&pos);
}
}
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
struct symbol *sym)
{
const char *dso_name;
@ -508,7 +617,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
if (dso_name == NULL)
return NULL;
return addr2inlines(dso_name, addr, dso);
return addr2inlines(dso_name, addr, dso, sym);
}
void inline_node__delete(struct inline_node *node)
@ -517,10 +626,63 @@ void inline_node__delete(struct inline_node *node)
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
list_del_init(&ilist->list);
zfree(&ilist->filename);
zfree(&ilist->funcname);
free_srcline(ilist->srcline);
/* only the inlined symbols are owned by the list */
if (ilist->symbol && ilist->symbol->inlined)
symbol__delete(ilist->symbol);
free(ilist);
}
free(node);
}
void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines)
{
struct rb_node **p = &tree->rb_node;
struct rb_node *parent = NULL;
const u64 addr = inlines->addr;
struct inline_node *i;
while (*p != NULL) {
parent = *p;
i = rb_entry(parent, struct inline_node, rb_node);
if (addr < i->addr)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&inlines->rb_node, parent, p);
rb_insert_color(&inlines->rb_node, tree);
}
struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr)
{
struct rb_node *n = tree->rb_node;
while (n) {
struct inline_node *i = rb_entry(n, struct inline_node,
rb_node);
if (addr < i->addr)
n = n->rb_left;
else if (addr > i->addr)
n = n->rb_right;
else
return i;
}
return NULL;
}
void inlines__tree_delete(struct rb_root *tree)
{
struct inline_node *pos;
struct rb_node *next = rb_first(tree);
while (next) {
pos = rb_entry(next, struct inline_node, rb_node);
next = rb_next(&pos->rb_node);
rb_erase(&pos->rb_node, tree);
inline_node__delete(pos);
}
}

View File

@ -2,6 +2,7 @@
#define PERF_SRCLINE_H
#include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/types.h>
struct dso;
@ -14,21 +15,38 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool show_addr, bool unwind_inlines);
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")
struct inline_list {
char *filename;
char *funcname;
unsigned int line_nr;
struct symbol *symbol;
char *srcline;
struct list_head list;
};
struct inline_node {
u64 addr;
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);
/* 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 */

View File

@ -45,6 +45,7 @@ struct symbol_conf symbol_conf = {
.show_hist_headers = true,
.symfs = "",
.event_group = true,
.inline_name = true,
};
static enum dso_binary_type binary_type_symtab[] = {

View File

@ -59,6 +59,7 @@ struct symbol {
u8 binding;
u8 idle:1;
u8 ignore:1;
u8 inlined:1;
u8 arch_sym;
char name[0];
};
@ -208,6 +209,7 @@ struct addr_location {
struct thread *thread;
struct map *map;
struct symbol *sym;
const char *srcline;
u64 addr;
char level;
u8 filtered;