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

Commit 4cc9cec6 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Arnaldo Carvalho de Melo
Browse files

perf probe: Introduce lines walker interface



Introduce die_walk_lines() for walking on the line list of given die, and use
it in line_range finder and probe point finder.

Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Franck Bui-Huu <fbuihuu@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20110113124558.22426.48170.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
[ committer note: s/%ld/%zd/ for a size_t nlines var that broke f14 x86 build]
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b0e8572f
Loading
Loading
Loading
Loading
+167 −154
Original line number Diff line number Diff line
@@ -458,6 +458,124 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
	return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
}

/* Walker on lines (Note: line number will not be sorted) */
typedef int (* line_walk_handler_t) (const char *fname, int lineno,
				     Dwarf_Addr addr, void *data);

struct __line_walk_param {
	line_walk_handler_t handler;
	void *data;
	int retval;
};

/* Walk on decl lines in given DIE */
static int __die_walk_funclines(Dwarf_Die *sp_die,
				line_walk_handler_t handler, void *data)
{
	const char *fname;
	Dwarf_Addr addr;
	int lineno, ret = 0;

	/* Handle function declaration line */
	fname = dwarf_decl_file(sp_die);
	if (fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
	    dwarf_entrypc(sp_die, &addr) == 0) {
		ret = handler(fname, lineno, addr, data);
	}

	return ret;
}

static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
{
	struct __line_walk_param *lw = data;

	lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data);
	if (lw->retval != 0)
		return DWARF_CB_ABORT;

	return DWARF_CB_OK;
}

/*
 * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on
 * the lines inside the subprogram, otherwise PDIE must be a CU DIE.
 */
static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler,
			  void *data)
{
	Dwarf_Lines *lines;
	Dwarf_Line *line;
	Dwarf_Addr addr;
	const char *fname;
	int lineno, ret = 0;
	Dwarf_Die die_mem, *cu_die;
	size_t nlines, i;

	/* Get the CU die */
	if (dwarf_tag(pdie) == DW_TAG_subprogram)
		cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL);
	else
		cu_die = pdie;
	if (!cu_die) {
		pr_debug2("Failed to get CU from subprogram\n");
		return -EINVAL;
	}

	/* Get lines list in the CU */
	if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
		pr_debug2("Failed to get source lines on this CU.\n");
		return -ENOENT;
	}
	pr_debug2("Get %zd lines from this CU\n", nlines);

	/* Walk on the lines on lines list */
	for (i = 0; i < nlines; i++) {
		line = dwarf_onesrcline(lines, i);
		if (line == NULL ||
		    dwarf_lineno(line, &lineno) != 0 ||
		    dwarf_lineaddr(line, &addr) != 0) {
			pr_debug2("Failed to get line info. "
				  "Possible error in debuginfo.\n");
			continue;
		}
		/* Filter lines based on address */
		if (pdie != cu_die)
			/*
			 * Address filtering
			 * The line is included in given function, and
			 * no inline block includes it.
			 */
			if (!dwarf_haspc(pdie, addr) ||
			    die_find_inlinefunc(pdie, addr, &die_mem))
				continue;
		/* Get source line */
		fname = dwarf_linesrc(line, NULL, NULL);

		ret = handler(fname, lineno, addr, data);
		if (ret != 0)
			return ret;
	}

	/*
	 * Dwarf lines doesn't include function declarations and inlined
	 * subroutines. We have to check functions list or given function.
	 */
	if (pdie != cu_die)
		ret = __die_walk_funclines(pdie, handler, data);
	else {
		struct __line_walk_param param = {
			.handler = handler,
			.data = data,
			.retval = 0,
		};
		dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
		ret = param.retval;
	}

	return ret;
}

struct __find_variable_param {
	const char *name;
	Dwarf_Addr addr;
@@ -1050,43 +1168,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
	return ret;
}

/* Find probe point from its line number */
static int find_probe_point_by_line(struct probe_finder *pf)
static int probe_point_line_walker(const char *fname, int lineno,
				   Dwarf_Addr addr, void *data)
{
	Dwarf_Lines *lines;
	Dwarf_Line *line;
	size_t nlines, i;
	Dwarf_Addr addr;
	int lineno;
	int ret = 0;

	if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
		pr_warning("No source lines found.\n");
		return -ENOENT;
	}

	for (i = 0; i < nlines && ret == 0; i++) {
		line = dwarf_onesrcline(lines, i);
		if (dwarf_lineno(line, &lineno) != 0 ||
		    lineno != pf->lno)
			continue;
	struct probe_finder *pf = data;
	int ret;

		/* TODO: Get fileno from line, but how? */
		if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
			continue;
	if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
		return 0;

		if (dwarf_lineaddr(line, &addr) != 0) {
			pr_warning("Failed to get the address of the line.\n");
			return -ENOENT;
		}
		pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n",
			 (int)i, lineno, (uintmax_t)addr);
	pf->addr = addr;

	ret = call_probe_finder(NULL, pf);
		/* Continuing, because target line might be inlined. */

	/* Continue if no error, because the line will be in inline function */
	return ret < 0 ?: 0;
}
	return ret;

/* Find probe point from its line number */
static int find_probe_point_by_line(struct probe_finder *pf)
{
	return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
}

/* Find lines which match lazy pattern */
@@ -1140,15 +1241,31 @@ out_close:
	return nlines;
}

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

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

	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);

	/*
	 * Continue if no error, because the lazy pattern will match
	 * to other lines
	 */
	return ret < 0 ?: 0;
}

/* Find probe points from lazy pattern  */
static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
{
	Dwarf_Lines *lines;
	Dwarf_Line *line;
	size_t nlines, i;
	Dwarf_Addr addr;
	Dwarf_Die die_mem;
	int lineno;
	int ret = 0;

	if (list_empty(&pf->lcache)) {
@@ -1162,45 +1279,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
			return ret;
	}

	if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) {
		pr_warning("No source lines found.\n");
		return -ENOENT;
	}

	for (i = 0; i < nlines && ret >= 0; i++) {
		line = dwarf_onesrcline(lines, i);

		if (dwarf_lineno(line, &lineno) != 0 ||
		    !line_list__has_line(&pf->lcache, lineno))
			continue;

		/* TODO: Get fileno from line, but how? */
		if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0)
			continue;

		if (dwarf_lineaddr(line, &addr) != 0) {
			pr_debug("Failed to get the address of line %d.\n",
				 lineno);
			continue;
		}
		if (sp_die) {
			/* Address filtering 1: does sp_die include addr? */
			if (!dwarf_haspc(sp_die, addr))
				continue;
			/* Address filtering 2: No child include addr? */
			if (die_find_inlinefunc(sp_die, addr, &die_mem))
				continue;
		}

		pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n",
			 (int)i, lineno, (unsigned long long)addr);
		pf->addr = addr;

		ret = call_probe_finder(sp_die, pf);
		/* Continuing, because target line might be inlined. */
	}
	/* TODO: deallocate lines, but how? */
	return ret;
	return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
}

/* Callback parameter with return value */
@@ -1644,91 +1723,28 @@ static int line_range_add_line(const char *src, unsigned int lineno,
	return line_list__add_line(&lr->line_list, lineno);
}

/* Search function declaration lines */
static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data)
static int line_range_walk_cb(const char *fname, int lineno,
			      Dwarf_Addr addr __used,
			      void *data)
{
	struct dwarf_callback_param *param = data;
	struct line_finder *lf = param->data;
	const char *src;
	int lineno;

	src = dwarf_decl_file(sp_die);
	if (src && strtailcmp(src, lf->fname) != 0)
		return DWARF_CB_OK;
	struct line_finder *lf = data;

	if (dwarf_decl_line(sp_die, &lineno) != 0 ||
	if ((strtailcmp(fname, lf->fname) != 0) ||
	    (lf->lno_s > lineno || lf->lno_e < lineno))
		return DWARF_CB_OK;
		return 0;

	param->retval = line_range_add_line(src, lineno, lf->lr);
	if (param->retval < 0)
		return DWARF_CB_ABORT;
	return DWARF_CB_OK;
}
	if (line_range_add_line(fname, lineno, lf->lr) < 0)
		return -EINVAL;

static int find_line_range_func_decl_lines(struct line_finder *lf)
{
	struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
	dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, &param, 0);
	return param.retval;
	return 0;
}

/* Find line range from its line number */
static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
{
	Dwarf_Lines *lines;
	Dwarf_Line *line;
	size_t nlines, i;
	Dwarf_Addr addr;
	int lineno, ret = 0;
	const char *src;
	Dwarf_Die die_mem;

	line_list__init(&lf->lr->line_list);
	if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) {
		pr_warning("No source lines found.\n");
		return -ENOENT;
	}

	/* Search probable lines on lines list */
	for (i = 0; i < nlines; i++) {
		line = dwarf_onesrcline(lines, i);
		if (dwarf_lineno(line, &lineno) != 0 ||
		    (lf->lno_s > lineno || lf->lno_e < lineno))
			continue;

		if (sp_die) {
			/* Address filtering 1: does sp_die include addr? */
			if (dwarf_lineaddr(line, &addr) != 0 ||
			    !dwarf_haspc(sp_die, addr))
				continue;

			/* Address filtering 2: No child include addr? */
			if (die_find_inlinefunc(sp_die, addr, &die_mem))
				continue;
		}

		/* TODO: Get fileno from line, but how? */
		src = dwarf_linesrc(line, NULL, NULL);
		if (strtailcmp(src, lf->fname) != 0)
			continue;

		ret = line_range_add_line(src, lineno, lf->lr);
		if (ret < 0)
			return ret;
	}
	int ret;

	/*
	 * Dwarf lines doesn't include function declarations. We have to
	 * check functions list or given function.
	 */
	if (sp_die) {
		src = dwarf_decl_file(sp_die);
		if (src && dwarf_decl_line(sp_die, &lineno) == 0 &&
		    (lf->lno_s <= lineno && lf->lno_e >= lineno))
			ret = line_range_add_line(src, lineno, lf->lr);
	} else
		ret = find_line_range_func_decl_lines(lf);
	ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);

	/* Update status */
	if (ret >= 0)
@@ -1758,9 +1774,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
	struct line_finder *lf = param->data;
	struct line_range *lr = lf->lr;

	pr_debug("find (%llx) %s\n",
		 (unsigned long long)dwarf_dieoffset(sp_die),
		 dwarf_diename(sp_die));
	if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
	    die_compare_name(sp_die, lr->function)) {
		lf->fname = dwarf_decl_file(sp_die);