Skip to content

Commit

Permalink
perf probe: Fix to search local variables in appropriate scope
Browse files Browse the repository at this point in the history
Fix perf probe to search local variables in appropriate local inlined
function scope. For example, pre_schedule() has only 2 local variables,
as below;

$ perf probe -L pre_schedule
<pre_schedule@/home/mhiramat/ksrc/linux-2.6/kernel/sched.c:0>
      0  static inline void pre_schedule(struct rq *rq, struct task_struct *prev)
         {
      2         if (prev->sched_class->pre_schedule)
      3                 prev->sched_class->pre_schedule(rq, prev);
         }

However, current perf probe shows 4 local variables on pre_schedule(),
because it searches variables in the caller(schedule()) scope.

$ perf probe -V pre_schedule
Available variables at pre_schedule
        @<schedule+445>
                int     cpu
                long unsigned int*      switch_count
                struct rq*      rq
                struct task_struct*     prev

This patch fixes this issue by searching variables in the local scope of
the instance of inlined function. Here is the result.

$ perf probe -V pre_schedule
Available variables at pre_schedule
        @<schedule+445>
                struct rq*      rq
                struct task_struct*     prev

Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110259.19900.85664.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
Masami Hiramatsu authored and Arnaldo Carvalho de Melo committed Aug 12, 2011
1 parent 13e27d7 commit 221d061
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 27 deletions.
33 changes: 33 additions & 0 deletions tools/perf/util/dwarf-aux.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,39 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr,
return *lineno ?: -ENOENT;
}

static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data);

/**
* cu_walk_functions_at - Walk on function DIEs at given address
* @cu_die: A CU DIE
* @addr: An address
* @callback: A callback which called with found DIEs
* @data: A user data
*
* Walk on function DIEs at given @addr in @cu_die. Passed DIEs
* should be subprogram or inlined-subroutines.
*/
int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
int (*callback)(Dwarf_Die *, void *), void *data)
{
Dwarf_Die die_mem;
Dwarf_Die *sc_die;
int ret = -ENOENT;

/* Inlined function could be recursive. Trace it until fail */
for (sc_die = die_find_realfunc(cu_die, addr, &die_mem);
sc_die != NULL;
sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr,
&die_mem)) {
ret = callback(sc_die, data);
if (ret)
break;
}

return ret;

}

/**
* die_compare_name - Compare diename and tname
* @dw_die: a DIE
Expand Down
4 changes: 4 additions & 0 deletions tools/perf/util/dwarf-aux.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ extern const char *cu_get_comp_dir(Dwarf_Die *cu_die);
extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
const char **fname, int *lineno);

/* Walk on funcitons at given address */
extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
int (*callback)(Dwarf_Die *, void *), void *data);

/* Compare diename and tname */
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);

Expand Down
132 changes: 106 additions & 26 deletions tools/perf/util/probe-finder.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,8 +612,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
return ret;
}

/* Find a variable in a subprogram die */
static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Find a variable in a scope DIE */
static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
{
Dwarf_Die vr_die, *scopes;
char buf[32], *ptr;
Expand Down Expand Up @@ -655,11 +655,11 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
pr_debug("Searching '%s' variable in context.\n",
pf->pvar->var);
/* Search child die for local variables and parameters. */
if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
if (die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die))
ret = convert_variable(&vr_die, pf);
else {
/* Search upper class */
nscopes = dwarf_getscopes_die(sp_die, &scopes);
nscopes = dwarf_getscopes_die(sc_die, &scopes);
ret = -ENOENT;
while (nscopes-- > 1) {
pr_debug("Searching variables in %s\n",
Expand Down Expand Up @@ -717,26 +717,30 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
return 0;
}

/* Call probe_finder callback with real subprogram DIE */
static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
/* Call probe_finder callback with scope DIE */
static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
{
Dwarf_Die die_mem;
Dwarf_Attribute fb_attr;
size_t nops;
int ret;

/* If no real subprogram, find a real one */
if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem);
if (!sp_die) {
if (!sc_die) {
pr_err("Caller must pass a scope DIE. Program error.\n");
return -EINVAL;
}

/* If not a real subprogram, find a real one */
if (dwarf_tag(sc_die) != DW_TAG_subprogram) {
if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
pr_warning("Failed to find probe point in any "
"functions.\n");
return -ENOENT;
}
}
} else
memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));

/* Get the frame base attribute/ops */
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
/* Get the frame base attribute/ops from subprogram */
dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr);
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
if (ret <= 0 || nops == 0) {
pf->fb_ops = NULL;
Expand All @@ -754,25 +758,90 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
}

/* Call finder's callback handler */
ret = pf->callback(sp_die, pf);
ret = pf->callback(sc_die, pf);

/* *pf->fb_ops will be cached in libdw. Don't free it. */
pf->fb_ops = NULL;

return ret;
}

struct find_scope_param {
const char *function;
const char *file;
int line;
int diff;
Dwarf_Die *die_mem;
bool found;
};

static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
{
struct find_scope_param *fsp = data;
const char *file;
int lno;

/* Skip if declared file name does not match */
if (fsp->file) {
file = dwarf_decl_file(fn_die);
if (!file || strcmp(fsp->file, file) != 0)
return 0;
}
/* If the function name is given, that's what user expects */
if (fsp->function) {
if (die_compare_name(fn_die, fsp->function)) {
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
fsp->found = true;
return 1;
}
} else {
/* With the line number, find the nearest declared DIE */
dwarf_decl_line(fn_die, &lno);
if (lno < fsp->line && fsp->diff > fsp->line - lno) {
/* Keep a candidate and continue */
fsp->diff = fsp->line - lno;
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
fsp->found = true;
}
}
return 0;
}

/* Find an appropriate scope fits to given conditions */
static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
{
struct find_scope_param fsp = {
.function = pf->pev->point.function,
.file = pf->fname,
.line = pf->lno,
.diff = INT_MAX,
.die_mem = die_mem,
.found = false,
};

cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp);

return fsp.found ? die_mem : NULL;
}

static int probe_point_line_walker(const char *fname, int lineno,
Dwarf_Addr addr, void *data)
{
struct probe_finder *pf = data;
Dwarf_Die *sc_die, die_mem;
int ret;

if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
return 0;

pf->addr = addr;
ret = call_probe_finder(NULL, pf);
sc_die = find_best_scope(pf, &die_mem);
if (!sc_die) {
pr_warning("Failed to find scope of probe point.\n");
return -ENOENT;
}

ret = call_probe_finder(sc_die, pf);

/* Continue if no error, because the line will be in inline function */
return ret < 0 ? ret : 0;
Expand Down Expand Up @@ -826,6 +895,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno,
Dwarf_Addr addr, void *data)
{
struct probe_finder *pf = data;
Dwarf_Die *sc_die, die_mem;
int ret;

if (!line_list__has_line(&pf->lcache, lineno) ||
Expand All @@ -835,7 +905,14 @@ static int probe_point_lazy_walker(const char *fname, int lineno,
pr_debug("Probe line found: line:%d addr:0x%llx\n",
lineno, (unsigned long long)addr);
pf->addr = addr;
ret = call_probe_finder(NULL, pf);
pf->lno = lineno;
sc_die = find_best_scope(pf, &die_mem);
if (!sc_die) {
pr_warning("Failed to find scope of probe point.\n");
return -ENOENT;
}

ret = call_probe_finder(sc_die, pf);

/*
* Continue if no error, because the lazy pattern will match
Expand Down Expand Up @@ -1059,7 +1136,7 @@ static int debuginfo__find_probes(struct debuginfo *self,
}

/* Add a found probe point into trace event list */
static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
{
struct trace_event_finder *tf =
container_of(pf, struct trace_event_finder, pf);
Expand All @@ -1074,8 +1151,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
}
tev = &tf->tevs[tf->ntevs++];

ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
&tev->point);
/* Trace point should be converted from subprogram DIE */
ret = convert_to_trace_point(&pf->sp_die, pf->addr,
pf->pev->point.retprobe, &tev->point);
if (ret < 0)
return ret;

Expand All @@ -1090,7 +1168,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
for (i = 0; i < pf->pev->nargs; i++) {
pf->pvar = &pf->pev->args[i];
pf->tvar = &tev->args[i];
ret = find_variable(sp_die, pf);
/* Variable should be found from scope DIE */
ret = find_variable(sc_die, pf);
if (ret != 0)
return ret;
}
Expand Down Expand Up @@ -1158,7 +1237,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
}

/* Add a found vars into available variables list */
static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
{
struct available_var_finder *af =
container_of(pf, struct available_var_finder, pf);
Expand All @@ -1173,8 +1252,9 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
}
vl = &af->vls[af->nvls++];

ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
&vl->point);
/* Trace point should be converted from subprogram DIE */
ret = convert_to_trace_point(&pf->sp_die, pf->addr,
pf->pev->point.retprobe, &vl->point);
if (ret < 0)
return ret;

Expand All @@ -1186,14 +1266,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
if (vl->vars == NULL)
return -ENOMEM;
af->child = true;
die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem);

/* Find external variables */
if (!af->externs)
goto out;
/* Don't need to search child DIE for externs. */
af->child = false;
nscopes = dwarf_getscopes_die(sp_die, &scopes);
nscopes = dwarf_getscopes_die(sc_die, &scopes);
while (nscopes-- > 1)
die_find_child(&scopes[nscopes], collect_variables_cb,
(void *)af, &die_mem);
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/util/probe-finder.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct probe_finder {
struct perf_probe_event *pev; /* Target probe event */

/* Callback when a probe point is found */
int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf);

/* For function searching */
int lno; /* Line number */
Expand Down

0 comments on commit 221d061

Please sign in to comment.