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

Commit e92b85e1 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Ingo Molnar
Browse files

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: default avatarMasami 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: default avatarIngo Molnar <mingo@elte.hu>
parent 804b3606
Loading
Loading
Loading
Loading
+86 −176
Original line number Diff line number Diff line
@@ -38,13 +38,6 @@
#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
 */
@@ -177,26 +170,6 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
	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 */
static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
{
@@ -208,69 +181,33 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
	return epc;
}

/* Check if the abstract origin's address or not */
static bool die_compare_abstract_origin(Dwarf_Die *in_die, void *origin_addr)
{
	Dwarf_Attribute attr;
	Dwarf_Die origin;

	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)
/* Get a variable die */
static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
				    Dwarf_Die *die_mem)
{
	struct die_link new_link;
	Dwarf_Die child_die;
	int tag;
	int ret;

	if (!die_cb)
		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);
	ret = dwarf_child(sp_die, die_mem);
	if (ret != 0)
			return 0;
	}
	return ret;
}
		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;
	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;

	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;
		if (die_find_variable(die_mem, name, &child_die)) {
			memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
			return die_mem;
		}
	} while (dwarf_siblingof(die_mem, die_mem) == 0);

	return NULL;
}

/*
 * 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);
}

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 */
static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
{
	int ret;
	Dwarf_Die vr_die;

	/* TODO: Support struct members and arrays */
	if (!is_c_varname(pf->var)) {
		/* Output raw parameters */
		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);
	/* Search child die for local variables and parameters. */
	ret = search_die_from_children(sp_die, variable_search_cb, pf);
	if (!ret)
	if (!die_find_variable(sp_die, pf->var, &vr_die))
		die("Failed to find '%s' in this function.", pf->var);

	show_variable(&vr_die, pf);
}

/* Show a probe point to output buffer */
static void show_probe_point(Dwarf_Die *sp_die, size_t offs,
			     struct probe_finder *pf)
static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
{
	struct probe_point *pp = pf->pp;
	Dwarf_Addr eaddr;
	Dwarf_Die die_mem;
	const char *name;
	char tmp[MAX_PROBE_BUFFER];
	int ret, i, len;
	Dwarf_Attribute fb_attr;
	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 */
	name = dwarf_diename(sp_die);
	if (name) {
		ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
				(unsigned int)offs);
		dwarf_entrypc(sp_die, &eaddr);
		ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name,
				(unsigned long)(pf->addr - eaddr));
		/* Copy the function name if possible */
		if (!pp->function) {
			pp->function = strdup(name);
			pp->offset = offs;
			pp->offset = (size_t)(pf->addr - eaddr);
		}
	} else {
		/* This function has no name. */
@@ -450,10 +383,9 @@ static void find_probe_point_by_line(struct probe_finder *pf)
	Dwarf_Lines *lines;
	Dwarf_Line *line;
	size_t nlines, i;
	Dwarf_Addr addr, epc;
	Dwarf_Addr addr;
	int lineno;
	int ret;
	Dwarf_Die *sp_die, die_mem;

	ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines);
	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);
		pf->addr = addr;

		sp_die = die_get_real_subprogram(&pf->cu_die, addr, &die_mem);
		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);
		show_probe_point(NULL, pf);
		/* Continuing, because target line might be inlined. */
	}
}

static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
{
	struct probe_finder *pf = (struct probe_finder *)data;
	struct probe_point *pp = pf->pp;

	/* Get probe address */
	pf->addr = die_get_entrypc(in_die);
	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(struct die_link *dlink, void *data)
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;
	struct die_link *lk;
	size_t offs;
	int tag;
	int ret;

	tag = dwarf_tag(&dlink->die);
	if (tag == DW_TAG_subprogram) {
		if (die_compare_name(&dlink->die, pp->function) == 0) {
	/* 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 */
				pf->fname = dwarf_decl_file(&dlink->die);
				dwarf_decl_line(&dlink->die, &pf->lno);
		pf->fname = dwarf_decl_file(sp_die);
		dwarf_decl_line(sp_die, &pf->lno);
		pf->lno += pp->line;
		find_probe_point_by_line(pf);
				return 1;
			}
			if (dwarf_func_inline(&dlink->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);
	} else if (!dwarf_func_inline(sp_die)) {
		/* Real function */
		pf->addr = die_get_entrypc(sp_die);
		pf->addr += pp->offset;
		/* 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. */
}
	} 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)
{
	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 */
@@ -669,27 +581,25 @@ static void find_line_range_by_line(struct line_finder *lf)
}

/* 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_range *lr = lf->lr;
	int tag;
	int ret;

	tag = dwarf_tag(&dlink->die);
	if (tag == DW_TAG_subprogram &&
	    die_compare_name(&dlink->die, lr->function) == 0) {
	if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
	    die_compare_name(sp_die, lr->function) == 0) {
		/* 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)
			ret = dwarf_lowpc(&dlink->die, &lf->addr_s);
			ret = dwarf_lowpc(sp_die, &lf->addr_s);
		if (ret != 0) {
			lf->addr_s = 0;
			lf->addr_e = 0;
		}

		lf->fname = dwarf_decl_file(&dlink->die);
		dwarf_decl_line(&dlink->die, &lr->offset);
		lf->fname = dwarf_decl_file(sp_die);
		dwarf_decl_line(sp_die, &lr->offset);
		pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
		lf->lno_s = lr->offset + lr->start;
		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)
{
	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)
+0 −1
Original line number Diff line number Diff line
@@ -66,7 +66,6 @@ struct probe_finder {
	Dwarf_Addr		addr;		/* Address */
	const char		*fname;		/* File name */
	int			lno;		/* Line number */
	void			*origin;	/* Inline origin addr */
	Dwarf_Die		cu_die;		/* Current CU */

	/* For variable searching */