Commit a64489c5 authored by Jin Yao's avatar Jin Yao Committed by Arnaldo Carvalho de Melo

perf report: Find the inline stack for a given address

It would be useful for perf to support a mode to query the inline stack
for a given callgraph address. This would simplify finding the right
code in code that does a lot of inlining.

The srcline.c has contained the code which supports to translate the
address to filename:line_nr. This patch just extends the function to let
it support getting the inline stacks.

It introduces the inline_list which will store the inline function
result (filename:line_nr and funcname).

If BFD lib is not supported, the result is only filename:line_nr.
Signed-off-by: default avatarYao Jin <yao.jin@linux.intel.com>
Tested-by: default avatarMilian Wolff <milian.wolff@kdab.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Link: http://lkml.kernel.org/r/1490474069-15823-3-git-send-email-yao.jin@linux.intel.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 5580338d
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "util/dso.h" #include "util/dso.h"
#include "util/util.h" #include "util/util.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/callchain.h"
#include "symbol.h" #include "symbol.h"
...@@ -30,6 +31,34 @@ static const char *dso__name(struct dso *dso) ...@@ -30,6 +31,34 @@ 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,
struct inline_node *node, struct dso *dso)
{
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);
}
}
list_add_tail(&ilist->list, &node->val);
return 0;
}
#ifdef HAVE_LIBBFD_SUPPORT #ifdef HAVE_LIBBFD_SUPPORT
/* /*
...@@ -169,9 +198,17 @@ static void addr2line_cleanup(struct a2l_data *a2l) ...@@ -169,9 +198,17 @@ static void addr2line_cleanup(struct a2l_data *a2l)
#define MAX_INLINE_NEST 1024 #define MAX_INLINE_NEST 1024
static void inline_list__reverse(struct inline_node *node)
{
struct inline_list *ilist, *n;
list_for_each_entry_safe_reverse(ilist, n, &node->val, list)
list_move_tail(&ilist->list, &node->val);
}
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) bool unwind_inlines, struct inline_node *node)
{ {
int ret = 0; int ret = 0;
struct a2l_data *a2l = dso->a2l; struct a2l_data *a2l = dso->a2l;
...@@ -196,8 +233,21 @@ static int addr2line(const char *dso_name, u64 addr, ...@@ -196,8 +233,21 @@ static int addr2line(const char *dso_name, u64 addr,
while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
&a2l->funcname, &a2l->line) && &a2l->funcname, &a2l->line) &&
cnt++ < MAX_INLINE_NEST) cnt++ < MAX_INLINE_NEST) {
;
if (node != NULL) {
if (inline_list__append(strdup(a2l->filename),
strdup(a2l->funcname),
a2l->line, node,
dso) != 0)
return 0;
}
}
if ((node != NULL) &&
(callchain_param.order != ORDER_CALLEE)) {
inline_list__reverse(node);
}
} }
if (a2l->found && a2l->filename) { if (a2l->found && a2l->filename) {
...@@ -223,6 +273,35 @@ void dso__free_a2l(struct dso *dso) ...@@ -223,6 +273,35 @@ void dso__free_a2l(struct dso *dso)
dso->a2l = NULL; dso->a2l = NULL;
} }
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
struct dso *dso)
{
char *file = NULL;
unsigned int line = 0;
struct inline_node *node;
node = zalloc(sizeof(*node));
if (node == NULL) {
perror("not enough memory for the inline node");
return NULL;
}
INIT_LIST_HEAD(&node->val);
node->addr = addr;
if (!addr2line(dso_name, addr, &file, &line, dso, TRUE, node))
goto out_free_inline_node;
if (list_empty(&node->val))
goto out_free_inline_node;
return node;
out_free_inline_node:
inline_node__delete(node);
return NULL;
}
#else /* HAVE_LIBBFD_SUPPORT */ #else /* HAVE_LIBBFD_SUPPORT */
static int filename_split(char *filename, unsigned int *line_nr) static int filename_split(char *filename, unsigned int *line_nr)
...@@ -249,7 +328,8 @@ static int filename_split(char *filename, unsigned int *line_nr) ...@@ -249,7 +328,8 @@ static int filename_split(char *filename, unsigned int *line_nr)
static int addr2line(const char *dso_name, u64 addr, 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)
{ {
FILE *fp; FILE *fp;
char cmd[PATH_MAX]; char cmd[PATH_MAX];
...@@ -288,6 +368,58 @@ void dso__free_a2l(struct dso *dso __maybe_unused) ...@@ -288,6 +368,58 @@ 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)
{
FILE *fp;
char cmd[PATH_MAX];
struct inline_node *node;
char *filename = NULL;
size_t len;
unsigned int line_nr = 0;
scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i %016"PRIx64,
dso_name, addr);
fp = popen(cmd, "r");
if (fp == NULL) {
pr_err("popen failed for %s\n", dso_name);
return NULL;
}
node = zalloc(sizeof(*node));
if (node == NULL) {
perror("not enough memory for the inline node");
goto out;
}
INIT_LIST_HEAD(&node->val);
node->addr = addr;
while (getline(&filename, &len, fp) != -1) {
if (filename_split(filename, &line_nr) != 1) {
free(filename);
goto out;
}
if (inline_list__append(filename, NULL, line_nr, node,
NULL) != 0)
goto out;
filename = NULL;
}
out:
pclose(fp);
if (list_empty(&node->val)) {
inline_node__delete(node);
return NULL;
}
return node;
}
#endif /* HAVE_LIBBFD_SUPPORT */ #endif /* HAVE_LIBBFD_SUPPORT */
/* /*
...@@ -311,7 +443,7 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, ...@@ -311,7 +443,7 @@ 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)) if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL))
goto out; goto out;
if (asprintf(&srcline, "%s:%u", if (asprintf(&srcline, "%s:%u",
...@@ -351,3 +483,28 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, ...@@ -351,3 +483,28 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
{ {
return __get_srcline(dso, addr, sym, show_sym, false); return __get_srcline(dso, addr, sym, show_sym, false);
} }
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
{
const char *dso_name;
dso_name = dso__name(dso);
if (dso_name == NULL)
return NULL;
return addr2inlines(dso_name, addr, dso);
}
void inline_node__delete(struct inline_node *node)
{
struct inline_list *ilist, *tmp;
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
list_del_init(&ilist->list);
zfree(&ilist->filename);
zfree(&ilist->funcname);
free(ilist);
}
free(node);
}
...@@ -390,6 +390,11 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map * ...@@ -390,6 +390,11 @@ int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map *
return 0; return 0;
} }
char *dso__demangle_sym(struct dso *dso, int kmodule, char *elf_name)
{
return demangle_sym(dso, kmodule, elf_name);
}
/* /*
* Align offset to 4 bytes as needed for note name and descriptor data. * Align offset to 4 bytes as needed for note name and descriptor data.
*/ */
......
...@@ -373,3 +373,10 @@ int kcore_copy(const char *from_dir __maybe_unused, ...@@ -373,3 +373,10 @@ int kcore_copy(const char *from_dir __maybe_unused,
void symbol__elf_init(void) void symbol__elf_init(void)
{ {
} }
char *dso__demangle_sym(struct dso *dso __maybe_unused,
int kmodule __maybe_unused,
char *elf_name __maybe_unused)
{
return NULL;
}
...@@ -305,6 +305,8 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, ...@@ -305,6 +305,8 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss,
struct map *map); struct map *map);
char *dso__demangle_sym(struct dso *dso, int kmodule, char *elf_name);
void __symbols__insert(struct rb_root *symbols, struct symbol *sym, bool kernel); void __symbols__insert(struct rb_root *symbols, struct symbol *sym, bool kernel);
void symbols__insert(struct rb_root *symbols, struct symbol *sym); void symbols__insert(struct rb_root *symbols, struct symbol *sym);
void symbols__fixup_duplicate(struct rb_root *symbols); void symbols__fixup_duplicate(struct rb_root *symbols);
......
...@@ -364,4 +364,20 @@ int is_printable_array(char *p, unsigned int len); ...@@ -364,4 +364,20 @@ int is_printable_array(char *p, unsigned int len);
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz);
int unit_number__scnprintf(char *buf, size_t size, u64 n); int unit_number__scnprintf(char *buf, size_t size, u64 n);
struct inline_list {
char *filename;
char *funcname;
unsigned int line_nr;
struct list_head list;
};
struct inline_node {
u64 addr;
struct list_head val;
};
struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr);
void inline_node__delete(struct inline_node *node);
#endif /* GIT_COMPAT_UTIL_H */ #endif /* GIT_COMPAT_UTIL_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment