mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 00:52:01 +00:00
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:
commit
57646b6fda
@ -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[]
|
|||||||
|
|
||||||
--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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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
|
|||||||
|
|
||||||
#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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
.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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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)
|
|||||||
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 {
|
|||||||
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,
|
||||||
if (left == right && left_dso == right_dso) {
|
node->sym->name);
|
||||||
if (node->branch) {
|
if (match != MATCH_ERROR)
|
||||||
cnode->branch_count++;
|
break;
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
/*
|
match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start,
|
||||||
* It's "from" of a branch
|
node->map, node->sym->start);
|
||||||
*/
|
break;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* otherwise fall-back to IP-based comparison below */
|
||||||
return MATCH_EQ;
|
__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) {
|
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,
|
|||||||
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,
|
|||||||
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 *
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
#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)
|
|||||||
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)
|
|||||||
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 {
|
|||||||
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,
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
.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,
|
|||||||
.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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@ check_calls:
|
|||||||
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)
|
|||||||
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,
|
|||||||
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 {
|
|||||||
};
|
};
|
||||||
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;
|
||||||
|
@ -33,28 +33,17 @@ static const char *dso__name(struct dso *dso)
|
|||||||
return dso_name;
|
return dso_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inline_list__append(char *filename, char *funcname, int line_nr,
|
static int inline_list__append(struct symbol *symbol, char *srcline,
|
||||||
struct inline_node *node, struct dso *dso)
|
struct inline_node *node)
|
||||||
{
|
{
|
||||||
struct inline_list *ilist;
|
struct inline_list *ilist;
|
||||||
char *demangled;
|
|
||||||
|
|
||||||
ilist = zalloc(sizeof(*ilist));
|
ilist = zalloc(sizeof(*ilist));
|
||||||
if (ilist == NULL)
|
if (ilist == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ilist->filename = filename;
|
ilist->symbol = symbol;
|
||||||
ilist->line_nr = line_nr;
|
ilist->srcline = srcline;
|
||||||
|
|
||||||
if (dso != NULL) {
|
|
||||||
demangled = dso__demangle_sym(dso, 0, funcname);
|
|
||||||
if (demangled == NULL) {
|
|
||||||
ilist->funcname = funcname;
|
|
||||||
} else {
|
|
||||||
ilist->funcname = demangled;
|
|
||||||
free(funcname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callchain_param.order == ORDER_CALLEE)
|
if (callchain_param.order == ORDER_CALLEE)
|
||||||
list_add_tail(&ilist->list, &node->val);
|
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;
|
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
|
#ifdef HAVE_LIBBFD_SUPPORT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -206,19 +219,59 @@ static void addr2line_cleanup(struct a2l_data *a2l)
|
|||||||
|
|
||||||
#define MAX_INLINE_NEST 1024
|
#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,
|
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;
|
struct a2l_data *a2l = dso->a2l;
|
||||||
char *funcname = a2l->funcname ? strdup(a2l->funcname) : NULL;
|
struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname);
|
||||||
char *filename = a2l->filename ? strdup(a2l->filename) : NULL;
|
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,
|
static int addr2line(const char *dso_name, u64 addr,
|
||||||
char **file, unsigned int *line, struct dso *dso,
|
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;
|
int ret = 0;
|
||||||
struct a2l_data *a2l = dso->a2l;
|
struct a2l_data *a2l = dso->a2l;
|
||||||
@ -244,7 +297,7 @@ static int addr2line(const char *dso_name, u64 addr,
|
|||||||
if (unwind_inlines) {
|
if (unwind_inlines) {
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
if (node && inline_list__append_dso_a2l(dso, node))
|
if (node && inline_list__append_dso_a2l(dso, node, sym))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
|
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;
|
a2l->filename = NULL;
|
||||||
|
|
||||||
if (node != NULL) {
|
if (node != NULL) {
|
||||||
if (inline_list__append_dso_a2l(dso, node))
|
if (inline_list__append_dso_a2l(dso, node, sym))
|
||||||
return 0;
|
return 0;
|
||||||
// found at least one inline frame
|
// found at least one inline frame
|
||||||
ret = 1;
|
ret = 1;
|
||||||
@ -287,7 +340,7 @@ void dso__free_a2l(struct dso *dso)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
||||||
struct dso *dso)
|
struct dso *dso, struct symbol *sym)
|
||||||
{
|
{
|
||||||
struct inline_node *node;
|
struct inline_node *node;
|
||||||
|
|
||||||
@ -300,17 +353,8 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
|||||||
INIT_LIST_HEAD(&node->val);
|
INIT_LIST_HEAD(&node->val);
|
||||||
node->addr = addr;
|
node->addr = addr;
|
||||||
|
|
||||||
if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node))
|
addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym);
|
||||||
goto out_free_inline_node;
|
|
||||||
|
|
||||||
if (list_empty(&node->val))
|
|
||||||
goto out_free_inline_node;
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
out_free_inline_node:
|
|
||||||
inline_node__delete(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* HAVE_LIBBFD_SUPPORT */
|
#else /* HAVE_LIBBFD_SUPPORT */
|
||||||
@ -340,7 +384,8 @@ static int addr2line(const char *dso_name, u64 addr,
|
|||||||
char **file, unsigned int *line_nr,
|
char **file, unsigned int *line_nr,
|
||||||
struct dso *dso __maybe_unused,
|
struct dso *dso __maybe_unused,
|
||||||
bool unwind_inlines __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;
|
FILE *fp;
|
||||||
char cmd[PATH_MAX];
|
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,
|
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;
|
FILE *fp;
|
||||||
char cmd[PATH_MAX];
|
char cmd[PATH_MAX];
|
||||||
@ -408,13 +454,15 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
|||||||
node->addr = addr;
|
node->addr = addr;
|
||||||
|
|
||||||
while (getline(&filename, &len, fp) != -1) {
|
while (getline(&filename, &len, fp) != -1) {
|
||||||
|
char *srcline;
|
||||||
|
|
||||||
if (filename_split(filename, &line_nr) != 1) {
|
if (filename_split(filename, &line_nr) != 1) {
|
||||||
free(filename);
|
free(filename);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inline_list__append(filename, NULL, line_nr, node,
|
srcline = srcline_from_fileline(filename, line_nr);
|
||||||
NULL) != 0)
|
if (inline_list__append(sym, srcline, node) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
filename = NULL;
|
filename = NULL;
|
||||||
@ -423,11 +471,6 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
|||||||
out:
|
out:
|
||||||
pclose(fp);
|
pclose(fp);
|
||||||
|
|
||||||
if (list_empty(&node->val)) {
|
|
||||||
inline_node__delete(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,19 +497,18 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
|||||||
if (dso_name == NULL)
|
if (dso_name == NULL)
|
||||||
goto out;
|
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;
|
goto out;
|
||||||
|
|
||||||
if (asprintf(&srcline, "%s:%u",
|
srcline = srcline_from_fileline(file, line);
|
||||||
srcline_full_filename ? file : basename(file),
|
free(file);
|
||||||
line) < 0) {
|
|
||||||
free(file);
|
if (!srcline)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
|
||||||
|
|
||||||
dso->a2l_fails = 0;
|
dso->a2l_fails = 0;
|
||||||
|
|
||||||
free(file);
|
|
||||||
return srcline;
|
return srcline;
|
||||||
|
|
||||||
out:
|
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);
|
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;
|
const char *dso_name;
|
||||||
|
|
||||||
@ -508,7 +617,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
|
|||||||
if (dso_name == NULL)
|
if (dso_name == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return addr2inlines(dso_name, addr, dso);
|
return addr2inlines(dso_name, addr, dso, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inline_node__delete(struct inline_node *node)
|
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_for_each_entry_safe(ilist, tmp, &node->val, list) {
|
||||||
list_del_init(&ilist->list);
|
list_del_init(&ilist->list);
|
||||||
zfree(&ilist->filename);
|
free_srcline(ilist->srcline);
|
||||||
zfree(&ilist->funcname);
|
/* only the inlined symbols are owned by the list */
|
||||||
|
if (ilist->symbol && ilist->symbol->inlined)
|
||||||
|
symbol__delete(ilist->symbol);
|
||||||
free(ilist);
|
free(ilist);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(node);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
|||||||
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 = {
|
|||||||
.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 {
|
|||||||
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 {
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user