summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/probe-finder.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r--tools/perf/util/probe-finder.c159
1 files changed, 105 insertions, 54 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 194f9e2a3285..b7c85ce466a1 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -273,6 +273,25 @@ static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
return dwarf_formstring(&attr);
}
+/* Get a line number and file name for given address */
+static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
+ const char **fname, int *lineno)
+{
+ Dwarf_Line *line;
+ Dwarf_Addr laddr;
+
+ line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr);
+ if (line && dwarf_lineaddr(line, &laddr) == 0 &&
+ addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) {
+ *fname = dwarf_linesrc(line, NULL, NULL);
+ if (!*fname)
+ /* line number is useless without filename */
+ *lineno = 0;
+ }
+
+ return *lineno ?: -ENOENT;
+}
+
/* Compare diename and tname */
static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
@@ -497,7 +516,20 @@ static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
Dwarf_Die *die_mem)
{
- return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
+ Dwarf_Die tmp_die;
+
+ sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die);
+ if (!sp_die)
+ return NULL;
+
+ /* Inlined function could be recursive. Trace it until fail */
+ while (sp_die) {
+ memcpy(die_mem, sp_die, sizeof(Dwarf_Die));
+ sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr,
+ &tmp_die);
+ }
+
+ return die_mem;
}
/* Walker on lines (Note: line number will not be sorted) */
@@ -1395,6 +1427,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
!die_compare_name(sp_die, pp->function))
return DWARF_CB_OK;
+ /* Check declared file */
+ if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
+ return DWARF_CB_OK;
+
pf->fname = dwarf_decl_file(sp_die);
if (pp->line) { /* Function relative line */
dwarf_decl_line(sp_die, &pf->lno);
@@ -1451,6 +1487,7 @@ static int find_probes(int fd, struct probe_finder *pf)
if (!dbg) {
pr_warning("No debug information found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ close(fd); /* Without dwfl_end(), fd isn't closed. */
return -EBADF;
}
@@ -1686,11 +1723,9 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
Dwarf_Die cudie, spdie, indie;
Dwarf *dbg = NULL;
Dwfl *dwfl = NULL;
- Dwarf_Line *line;
- Dwarf_Addr laddr, eaddr, bias = 0;
- const char *tmp;
- int lineno, ret = 0;
- bool found = false;
+ Dwarf_Addr _addr, baseaddr, bias = 0;
+ const char *fname = NULL, *func = NULL, *tmp;
+ int baseline = 0, lineno = 0, ret = 0;
/* Open the live linux kernel */
dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
@@ -1711,68 +1746,79 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
goto end;
}
- /* Find a corresponding line */
- line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr);
- if (line) {
- if (dwarf_lineaddr(line, &laddr) == 0 &&
- (Dwarf_Addr)addr == laddr &&
- dwarf_lineno(line, &lineno) == 0) {
- tmp = dwarf_linesrc(line, NULL, NULL);
- if (tmp) {
- ppt->line = lineno;
- ppt->file = strdup(tmp);
- if (ppt->file == NULL) {
- ret = -ENOMEM;
- goto end;
- }
- found = true;
- }
- }
- }
+ /* Find a corresponding line (filename and lineno) */
+ cu_find_lineinfo(&cudie, addr, &fname, &lineno);
+ /* Don't care whether it failed or not */
- /* Find a corresponding function */
+ /* Find a corresponding function (name, baseline and baseaddr) */
if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) {
+ /* Get function entry information */
tmp = dwarf_diename(&spdie);
- if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0)
- goto end;
-
- if (ppt->line) {
- if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
- &indie)) {
- /* addr in an inline function */
+ if (!tmp ||
+ dwarf_entrypc(&spdie, &baseaddr) != 0 ||
+ dwarf_decl_line(&spdie, &baseline) != 0)
+ goto post;
+ func = tmp;
+
+ if (addr == (unsigned long)baseaddr)
+ /* Function entry - Relative line number is 0 */
+ lineno = baseline;
+ else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
+ &indie)) {
+ if (dwarf_entrypc(&indie, &_addr) == 0 &&
+ _addr == addr)
+ /*
+ * addr is at an inline function entry.
+ * In this case, lineno should be the call-site
+ * line number.
+ */
+ lineno = die_get_call_lineno(&indie);
+ else {
+ /*
+ * addr is in an inline function body.
+ * Since lineno points one of the lines
+ * of the inline function, baseline should
+ * be the entry line of the inline function.
+ */
tmp = dwarf_diename(&indie);
- if (!tmp)
- goto end;
- ret = dwarf_decl_line(&indie, &lineno);
- } else {
- if (eaddr == addr) { /* Function entry */
- lineno = ppt->line;
- ret = 0;
- } else
- ret = dwarf_decl_line(&spdie, &lineno);
- }
- if (ret == 0) {
- /* Make a relative line number */
- ppt->line -= lineno;
- goto found;
+ if (tmp &&
+ dwarf_decl_line(&spdie, &baseline) == 0)
+ func = tmp;
}
}
- /* We don't have a line number, let's use offset */
- ppt->offset = addr - (unsigned long)eaddr;
-found:
- ppt->function = strdup(tmp);
+ }
+
+post:
+ /* Make a relative line number or an offset */
+ if (lineno)
+ ppt->line = lineno - baseline;
+ else if (func)
+ ppt->offset = addr - (unsigned long)baseaddr;
+
+ /* Duplicate strings */
+ if (func) {
+ ppt->function = strdup(func);
if (ppt->function == NULL) {
ret = -ENOMEM;
goto end;
}
- found = true;
}
-
+ if (fname) {
+ ppt->file = strdup(fname);
+ if (ppt->file == NULL) {
+ if (ppt->function) {
+ free(ppt->function);
+ ppt->function = NULL;
+ }
+ ret = -ENOMEM;
+ goto end;
+ }
+ }
end:
if (dwfl)
dwfl_end(dwfl);
- if (ret >= 0)
- ret = found ? 1 : 0;
+ if (ret == 0 && (fname || func))
+ ret = 1; /* Found a point */
return ret;
}
@@ -1840,6 +1886,10 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
struct line_finder *lf = param->data;
struct line_range *lr = lf->lr;
+ /* Check declared file */
+ if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die)))
+ return DWARF_CB_OK;
+
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
die_compare_name(sp_die, lr->function)) {
lf->fname = dwarf_decl_file(sp_die);
@@ -1892,6 +1942,7 @@ int find_line_range(int fd, struct line_range *lr)
if (!dbg) {
pr_warning("No debug information found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ close(fd); /* Without dwfl_end(), fd isn't closed. */
return -EBADF;
}