perf probe: Use libdw callback routines

Use libdw callback functions aggressively, and remove
local tree-search API. This change simplifies the code.

Changes in v3:
 - Cast Dwarf_Addr to uintmax_t for printf-formats.

Changes in v2:
 - Cast Dwarf_Addr to unsigned long long for printf-formats.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Ulrich Drepper <drepper@redhat.com>
Cc: Roland McGrath <roland@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
LKML-Reference: <20100225133549.6725.81499.stgit@localhost6.localdomain6>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Masami Hiramatsu 2010-02-25 08:35:50 -05:00 committed by Ingo Molnar
parent 804b36068e
commit e92b85e1ff
2 changed files with 88 additions and 179 deletions

View File

@ -38,13 +38,6 @@
#include "probe-finder.h" #include "probe-finder.h"
/* Dwarf_Die Linkage to parent Die */
struct die_link {
struct die_link *parent; /* Parent die */
Dwarf_Die die; /* Current die */
};
/* /*
* Generic dwarf analysis helpers * Generic dwarf analysis helpers
*/ */
@ -177,26 +170,6 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
return strcmp(tname, name); return strcmp(tname, name);
} }
/* Check the address is in the subprogram(function). */
static bool die_within_subprogram(Dwarf_Die *sp_die, Dwarf_Addr addr,
size_t *offs)
{
Dwarf_Addr epc;
int ret;
ret = dwarf_haspc(sp_die, addr);
if (ret <= 0)
return false;
if (offs) {
ret = dwarf_entrypc(sp_die, &epc);
DIE_IF(ret == -1);
*offs = addr - epc;
}
return true;
}
/* Get entry pc(or low pc, 1st entry of ranges) of the die */ /* Get entry pc(or low pc, 1st entry of ranges) of the die */
static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
{ {
@ -208,70 +181,34 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
return epc; return epc;
} }
/* Check if the abstract origin's address or not */ /* Get a variable die */
static bool die_compare_abstract_origin(Dwarf_Die *in_die, void *origin_addr) static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
Dwarf_Die *die_mem)
{ {
Dwarf_Attribute attr; Dwarf_Die child_die;
Dwarf_Die origin; int tag;
if (!dwarf_attr(in_die, DW_AT_abstract_origin, &attr))
return false;
if (!dwarf_formref_die(&attr, &origin))
return false;
return origin.addr == origin_addr;
}
/*
* Search a Die from Die tree.
* Note: cur_link->die should be deallocated in this function.
*/
static int __search_die_tree(struct die_link *cur_link,
int (*die_cb)(struct die_link *, void *),
void *data)
{
struct die_link new_link;
int ret; int ret;
if (!die_cb) ret = dwarf_child(sp_die, die_mem);
return 0;
/* Check current die */
while (!(ret = die_cb(cur_link, data))) {
/* Check child die */
ret = dwarf_child(&cur_link->die, &new_link.die);
if (ret == 0) {
new_link.parent = cur_link;
ret = __search_die_tree(&new_link, die_cb, data);
if (ret)
break;
}
/* Move to next sibling */
ret = dwarf_siblingof(&cur_link->die, &cur_link->die);
if (ret != 0) if (ret != 0)
return 0; return NULL;
do {
tag = dwarf_tag(die_mem);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
(die_compare_name(die_mem, name) == 0))
return die_mem;
if (die_find_variable(die_mem, name, &child_die)) {
memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
return die_mem;
} }
return ret; } while (dwarf_siblingof(die_mem, die_mem) == 0);
return NULL;
} }
/* Search a die in its children's die tree */
static int search_die_from_children(Dwarf_Die *parent_die,
int (*die_cb)(struct die_link *, void *),
void *data)
{
struct die_link new_link;
int ret;
new_link.parent = NULL;
ret = dwarf_child(parent_die, &new_link.die);
if (ret == 0)
return __search_die_tree(&new_link, die_cb, data);
else
return 0;
}
/* /*
* Probe finder related functions * Probe finder related functions
*/ */
@ -347,28 +284,13 @@ static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
" Perhaps, it has been optimized out.", pf->var); " Perhaps, it has been optimized out.", pf->var);
} }
static int variable_search_cb(struct die_link *dlink, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
int tag;
tag = dwarf_tag(&dlink->die);
DIE_IF(tag < 0);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
(die_compare_name(&dlink->die, pf->var) == 0)) {
show_variable(&dlink->die, pf);
return 1;
}
/* TODO: Support struct members and arrays */
return 0;
}
/* Find a variable in a subprogram die */ /* Find a variable in a subprogram die */
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{ {
int ret; int ret;
Dwarf_Die vr_die;
/* TODO: Support struct members and arrays */
if (!is_c_varname(pf->var)) { if (!is_c_varname(pf->var)) {
/* Output raw parameters */ /* Output raw parameters */
ret = snprintf(pf->buf, pf->len, " %s", pf->var); ret = snprintf(pf->buf, pf->len, " %s", pf->var);
@ -379,31 +301,42 @@ static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
pr_debug("Searching '%s' variable in context.\n", pf->var); pr_debug("Searching '%s' variable in context.\n", pf->var);
/* Search child die for local variables and parameters. */ /* Search child die for local variables and parameters. */
ret = search_die_from_children(sp_die, variable_search_cb, pf); if (!die_find_variable(sp_die, pf->var, &vr_die))
if (!ret)
die("Failed to find '%s' in this function.", pf->var); die("Failed to find '%s' in this function.", pf->var);
show_variable(&vr_die, pf);
} }
/* Show a probe point to output buffer */ /* Show a probe point to output buffer */
static void show_probe_point(Dwarf_Die *sp_die, size_t offs, static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
struct probe_finder *pf)
{ {
struct probe_point *pp = pf->pp; struct probe_point *pp = pf->pp;
Dwarf_Addr eaddr;
Dwarf_Die die_mem;
const char *name; const char *name;
char tmp[MAX_PROBE_BUFFER]; char tmp[MAX_PROBE_BUFFER];
int ret, i, len; int ret, i, len;
Dwarf_Attribute fb_attr; Dwarf_Attribute fb_attr;
size_t nops; size_t nops;
/* If no real subprogram, find a real one */
if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
sp_die = die_get_real_subprogram(&pf->cu_die,
pf->addr, &die_mem);
if (!sp_die)
die("Probe point is not found in subprograms.");
}
/* Output name of probe point */ /* Output name of probe point */
name = dwarf_diename(sp_die); name = dwarf_diename(sp_die);
if (name) { if (name) {
ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, dwarf_entrypc(sp_die, &eaddr);
(unsigned int)offs); ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name,
(unsigned long)(pf->addr - eaddr));
/* Copy the function name if possible */ /* Copy the function name if possible */
if (!pp->function) { if (!pp->function) {
pp->function = strdup(name); pp->function = strdup(name);
pp->offset = offs; pp->offset = (size_t)(pf->addr - eaddr);
} }
} else { } else {
/* This function has no name. */ /* This function has no name. */
@ -450,10 +383,9 @@ static void find_probe_point_by_line(struct probe_finder *pf)
Dwarf_Lines *lines; Dwarf_Lines *lines;
Dwarf_Line *line; Dwarf_Line *line;
size_t nlines, i; size_t nlines, i;
Dwarf_Addr addr, epc; Dwarf_Addr addr;
int lineno; int lineno;
int ret; int ret;
Dwarf_Die *sp_die, die_mem;
ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
DIE_IF(ret != 0); DIE_IF(ret != 0);
@ -474,77 +406,57 @@ static void find_probe_point_by_line(struct probe_finder *pf)
(int)i, lineno, (uintmax_t)addr); (int)i, lineno, (uintmax_t)addr);
pf->addr = addr; pf->addr = addr;
sp_die = die_get_real_subprogram(&pf->cu_die, addr, &die_mem); show_probe_point(NULL, pf);
if (!sp_die)
die("Probe point is not found in subprograms.");
dwarf_entrypc(sp_die, &epc);
show_probe_point(sp_die, (size_t)(addr - epc), pf);
/* Continuing, because target line might be inlined. */ /* Continuing, because target line might be inlined. */
} }
} }
static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
/* Search function from function name */
static int probe_point_search_cb(struct die_link *dlink, void *data)
{ {
struct probe_finder *pf = (struct probe_finder *)data; struct probe_finder *pf = (struct probe_finder *)data;
struct probe_point *pp = pf->pp; struct probe_point *pp = pf->pp;
struct die_link *lk;
size_t offs;
int tag;
int ret;
tag = dwarf_tag(&dlink->die); /* Get probe address */
if (tag == DW_TAG_subprogram) { pf->addr = die_get_entrypc(in_die);
if (die_compare_name(&dlink->die, pp->function) == 0) { pf->addr += pp->offset;
pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr);
show_probe_point(in_die, pf);
return DWARF_CB_OK;
}
/* Search function from function name */
static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
{
struct probe_finder *pf = (struct probe_finder *)data;
struct probe_point *pp = pf->pp;
/* Check tag and diename */
if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
die_compare_name(sp_die, pp->function) != 0)
return 0;
if (pp->line) { /* Function relative line */ if (pp->line) { /* Function relative line */
pf->fname = dwarf_decl_file(&dlink->die); pf->fname = dwarf_decl_file(sp_die);
dwarf_decl_line(&dlink->die, &pf->lno); dwarf_decl_line(sp_die, &pf->lno);
pf->lno += pp->line; pf->lno += pp->line;
find_probe_point_by_line(pf); find_probe_point_by_line(pf);
return 1; } else if (!dwarf_func_inline(sp_die)) {
} /* Real function */
if (dwarf_func_inline(&dlink->die)) { pf->addr = die_get_entrypc(sp_die);
/* Inlined function, save it. */
pf->origin = dlink->die.addr;
return 0; /* Continue to search */
}
/* Get probe address */
pf->addr = die_get_entrypc(&dlink->die);
pf->addr += pp->offset; pf->addr += pp->offset;
/* TODO: Check the address in this function */ /* TODO: Check the address in this function */
show_probe_point(&dlink->die, pp->offset, pf); show_probe_point(sp_die, pf);
} else
/* Inlined function: search instances */
dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf);
return 1; /* Exit; no same symbol in this CU. */ return 1; /* Exit; no same symbol in this CU. */
}
} else if (tag == DW_TAG_inlined_subroutine && pf->origin) {
if (die_compare_abstract_origin(&dlink->die, pf->origin)) {
/* Get probe address */
pf->addr = die_get_entrypc(&dlink->die);
pf->addr += pp->offset;
pr_debug("found inline addr: 0x%jx\n",
(uintmax_t)pf->addr);
/* Inlined function. Get a real subprogram */
for (lk = dlink->parent; lk != NULL; lk = lk->parent) {
tag = dwarf_tag(&lk->die);
if (tag == DW_TAG_subprogram &&
!dwarf_func_inline(&lk->die))
goto found;
}
die("Failed to find real subprogram.");
found:
/* Get offset from subprogram */
ret = die_within_subprogram(&lk->die, pf->addr, &offs);
DIE_IF(!ret);
show_probe_point(&lk->die, offs, pf);
/* Continue to search */
}
}
return 0;
} }
static void find_probe_point_by_func(struct probe_finder *pf) static void find_probe_point_by_func(struct probe_finder *pf)
{ {
search_die_from_children(&pf->cu_die, probe_point_search_cb, pf); dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0);
} }
/* Find a probe point */ /* Find a probe point */
@ -669,27 +581,25 @@ static void find_line_range_by_line(struct line_finder *lf)
} }
/* Search function from function name */ /* Search function from function name */
static int line_range_search_cb(struct die_link *dlink, void *data) static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
{ {
struct line_finder *lf = (struct line_finder *)data; struct line_finder *lf = (struct line_finder *)data;
struct line_range *lr = lf->lr; struct line_range *lr = lf->lr;
int tag;
int ret; int ret;
tag = dwarf_tag(&dlink->die); if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
if (tag == DW_TAG_subprogram && die_compare_name(sp_die, lr->function) == 0) {
die_compare_name(&dlink->die, lr->function) == 0) {
/* Get the address range of this function */ /* Get the address range of this function */
ret = dwarf_highpc(&dlink->die, &lf->addr_e); ret = dwarf_highpc(sp_die, &lf->addr_e);
if (ret == 0) if (ret == 0)
ret = dwarf_lowpc(&dlink->die, &lf->addr_s); ret = dwarf_lowpc(sp_die, &lf->addr_s);
if (ret != 0) { if (ret != 0) {
lf->addr_s = 0; lf->addr_s = 0;
lf->addr_e = 0; lf->addr_e = 0;
} }
lf->fname = dwarf_decl_file(&dlink->die); lf->fname = dwarf_decl_file(sp_die);
dwarf_decl_line(&dlink->die, &lr->offset); dwarf_decl_line(sp_die, &lr->offset);
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
lf->lno_s = lr->offset + lr->start; lf->lno_s = lr->offset + lr->start;
if (!lr->end) if (!lr->end)
@ -706,7 +616,7 @@ static int line_range_search_cb(struct die_link *dlink, void *data)
static void find_line_range_by_func(struct line_finder *lf) static void find_line_range_by_func(struct line_finder *lf)
{ {
search_die_from_children(&lf->cu_die, line_range_search_cb, lf); dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0);
} }
int find_line_range(int fd, struct line_range *lr) int find_line_range(int fd, struct line_range *lr)

View File

@ -66,7 +66,6 @@ struct probe_finder {
Dwarf_Addr addr; /* Address */ Dwarf_Addr addr; /* Address */
const char *fname; /* File name */ const char *fname; /* File name */
int lno; /* Line number */ int lno; /* Line number */
void *origin; /* Inline origin addr */
Dwarf_Die cu_die; /* Current CU */ Dwarf_Die cu_die; /* Current CU */
/* For variable searching */ /* For variable searching */