Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 221d0611 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Arnaldo Carvalho de Melo
Browse files

perf probe: Fix to search local variables in appropriate scope

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: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 13e27d76
Loading
Loading
Loading
Loading
+33 −0
Original line number Original line Diff line number Diff line
@@ -96,6 +96,39 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr,
	return *lineno ?: -ENOENT;
	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
 * die_compare_name - Compare diename and tname
 * @dw_die: a DIE
 * @dw_die: a DIE
+4 −0
Original line number Original line Diff line number Diff line
@@ -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,
extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
			    const char **fname, int *lineno);
			    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 */
/* Compare diename and tname */
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);


+106 −26
Original line number Original line Diff line number Diff line
@@ -612,8 +612,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
	return ret;
	return ret;
}
}


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


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


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

	/* 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 "
			pr_warning("Failed to find probe point in any "
				   "functions.\n");
				   "functions.\n");
			return -ENOENT;
			return -ENOENT;
		}
		}
	}
	} else
		memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));


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


	/* Call finder's callback handler */
	/* 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 will be cached in libdw. Don't free it. */
	pf->fb_ops = NULL;
	pf->fb_ops = NULL;
@@ -762,17 +766,82 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
	return ret;
	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,
static int probe_point_line_walker(const char *fname, int lineno,
				   Dwarf_Addr addr, void *data)
				   Dwarf_Addr addr, void *data)
{
{
	struct probe_finder *pf = data;
	struct probe_finder *pf = data;
	Dwarf_Die *sc_die, die_mem;
	int ret;
	int ret;


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


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


	if (!line_list__has_line(&pf->lcache, lineno) ||
	if (!line_list__has_line(&pf->lcache, lineno) ||
@@ -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",
	pr_debug("Probe line found: line:%d addr:0x%llx\n",
		 lineno, (unsigned long long)addr);
		 lineno, (unsigned long long)addr);
	pf->addr = 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
	 * Continue if no error, because the lazy pattern will match
@@ -1059,7 +1136,7 @@ static int debuginfo__find_probes(struct debuginfo *self,
}
}


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


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


@@ -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++) {
	for (i = 0; i < pf->pev->nargs; i++) {
		pf->pvar = &pf->pev->args[i];
		pf->pvar = &pf->pev->args[i];
		pf->tvar = &tev->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)
		if (ret != 0)
			return ret;
			return ret;
	}
	}
@@ -1158,7 +1237,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
}
}


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


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


@@ -1186,14 +1266,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
	if (vl->vars == NULL)
	if (vl->vars == NULL)
		return -ENOMEM;
		return -ENOMEM;
	af->child = true;
	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 */
	/* Find external variables */
	if (!af->externs)
	if (!af->externs)
		goto out;
		goto out;
	/* Don't need to search child DIE for externs. */
	/* Don't need to search child DIE for externs. */
	af->child = false;
	af->child = false;
	nscopes = dwarf_getscopes_die(sp_die, &scopes);
	nscopes = dwarf_getscopes_die(sc_die, &scopes);
	while (nscopes-- > 1)
	while (nscopes-- > 1)
		die_find_child(&scopes[nscopes], collect_variables_cb,
		die_find_child(&scopes[nscopes], collect_variables_cb,
			       (void *)af, &die_mem);
			       (void *)af, &die_mem);
+1 −1
Original line number Original line Diff line number Diff line
@@ -57,7 +57,7 @@ struct probe_finder {
	struct perf_probe_event	*pev;		/* Target probe event */
	struct perf_probe_event	*pev;		/* Target probe event */


	/* Callback when a probe point is found */
	/* 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 */
	/* For function searching */
	int			lno;		/* Line number */
	int			lno;		/* Line number */