Example #1
0
static int __die_walk_instances_cb(Dwarf_Die *inst, void *data)
{
	struct __instance_walk_param *iwp = data;
	Dwarf_Attribute attr_mem;
	Dwarf_Die origin_mem;
	Dwarf_Attribute *attr;
	Dwarf_Die *origin;
	int tmp;

	attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem);
	if (attr == NULL)
		return DIE_FIND_CB_CONTINUE;

	origin = dwarf_formref_die(attr, &origin_mem);
	if (origin == NULL || origin->addr != iwp->addr)
		return DIE_FIND_CB_CONTINUE;

	/* Ignore redundant instances */
	if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) {
		dwarf_decl_line(origin, &tmp);
		if (die_get_call_lineno(inst) == tmp) {
			tmp = die_get_decl_fileno(origin);
			if (die_get_call_fileno(inst) == tmp)
				return DIE_FIND_CB_CONTINUE;
		}
	}

	iwp->retval = iwp->callback(inst, iwp->data);

	return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE;
}
Example #2
0
static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
{
	struct __line_walk_param *lw = data;
	Dwarf_Addr addr;
	int lineno;

	if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
		lineno = die_get_call_lineno(in_die);
		if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
			lw->retval = lw->handler(lw->fname, lineno, addr,
						 lw->data);
			if (lw->retval != 0)
				return DIE_FIND_CB_FOUND;
		}
	}
	return DIE_FIND_CB_SIBLING;
}
int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
				struct perf_probe_point *ppt)
{
	Dwarf_Die cudie, spdie, indie;
	Dwarf_Addr _addr, baseaddr;
	const char *fname = NULL, *func = NULL, *tmp;
	int baseline = 0, lineno = 0, ret = 0;

	
	addr += self->bias;

	
	if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) {
		pr_warning("Failed to find debug information for address %lx\n",
			   addr);
		ret = -EINVAL;
		goto end;
	}

	
	cu_find_lineinfo(&cudie, addr, &fname, &lineno);
	

	
	if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
		
		tmp = dwarf_diename(&spdie);
		if (!tmp ||
		    dwarf_entrypc(&spdie, &baseaddr) != 0 ||
		    dwarf_decl_line(&spdie, &baseline) != 0)
			goto post;
		func = tmp;

		if (addr == (unsigned long)baseaddr)
			
			lineno = baseline;
		else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
					     &indie)) {
			if (dwarf_entrypc(&indie, &_addr) == 0 &&
			    _addr == addr)
				lineno = die_get_call_lineno(&indie);
			else {
				tmp = dwarf_diename(&indie);
				if (tmp &&
				    dwarf_decl_line(&spdie, &baseline) == 0)
					func = tmp;
			}
		}
	}

post:
	
	if (lineno)
		ppt->line = lineno - baseline;
	else if (func)
		ppt->offset = addr - (unsigned long)baseaddr;

	
	if (func) {
		ppt->function = strdup(func);
		if (ppt->function == NULL) {
			ret = -ENOMEM;
			goto end;
		}
	}
	if (fname) {
		ppt->file = strdup(fname);
		if (ppt->file == NULL) {
			if (ppt->function) {
				free(ppt->function);
				ppt->function = NULL;
			}
			ret = -ENOMEM;
			goto end;
		}
	}
end:
	if (ret == 0 && (fname || func))
		ret = 1;	
	return ret;
}
Example #4
0
static int find_probe_point_by_func(struct probe_finder *pf)
{
	struct dwarf_callback_param _param = {.data = (void *)pf,
					      .retval = 0};
	dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
	return _param.retval;
}

struct pubname_callback_param {
	char *function;
	char *file;
	Dwarf_Die *cu_die;
	Dwarf_Die *sp_die;
	int found;
};

static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
{
	struct pubname_callback_param *param = data;

	if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) {
		if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
			return DWARF_CB_OK;

		if (die_compare_name(param->sp_die, param->function)) {
			if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
				return DWARF_CB_OK;

			if (param->file &&
			    strtailcmp(param->file, dwarf_decl_file(param->sp_die)))
				return DWARF_CB_OK;

			param->found = 1;
			return DWARF_CB_ABORT;
		}
	}

	return DWARF_CB_OK;
}

/* Find probe points from debuginfo */
static int debuginfo__find_probes(struct debuginfo *dbg,
				  struct probe_finder *pf)
{
	struct perf_probe_point *pp = &pf->pev->point;
	Dwarf_Off off, noff;
	size_t cuhl;
	Dwarf_Die *diep;
	int ret = 0;

#if _ELFUTILS_PREREQ(0, 142)
	Elf *elf;
	GElf_Ehdr ehdr;
	GElf_Shdr shdr;

	/* Get the call frame information from this dwarf */
	elf = dwarf_getelf(dbg->dbg);
	if (elf == NULL)
		return -EINVAL;

	if (gelf_getehdr(elf, &ehdr) == NULL)
		return -EINVAL;

	if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) &&
	    shdr.sh_type == SHT_PROGBITS) {
		pf->cfi = dwarf_getcfi_elf(elf);
	} else {
		pf->cfi = dwarf_getcfi(dbg->dbg);
	}
#endif

	off = 0;
	pf->lcache = intlist__new(NULL);
	if (!pf->lcache)
		return -ENOMEM;

	/* Fastpath: lookup by function name from .debug_pubnames section */
	if (pp->function) {
		struct pubname_callback_param pubname_param = {
			.function = pp->function,
			.file	  = pp->file,
			.cu_die	  = &pf->cu_die,
			.sp_die	  = &pf->sp_die,
			.found	  = 0,
		};
		struct dwarf_callback_param probe_param = {
			.data = pf,
		};

		dwarf_getpubnames(dbg->dbg, pubname_search_cb,
				  &pubname_param, 0);
		if (pubname_param.found) {
			ret = probe_point_search_cb(&pf->sp_die, &probe_param);
			if (ret)
				goto found;
		}
	}

	/* Loop on CUs (Compilation Unit) */
	while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
		/* Get the DIE(Debugging Information Entry) of this CU */
		diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die);
		if (!diep)
			continue;

		/* Check if target file is included. */
		if (pp->file)
			pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
		else
			pf->fname = NULL;

		if (!pp->file || pf->fname) {
			if (pp->function)
				ret = find_probe_point_by_func(pf);
			else if (pp->lazy_line)
				ret = find_probe_point_lazy(&pf->cu_die, pf);
			else {
				pf->lno = pp->line;
				ret = find_probe_point_by_line(pf);
			}
			if (ret < 0)
				break;
		}
		off = noff;
	}

found:
	intlist__delete(pf->lcache);
	pf->lcache = NULL;

	return ret;
}

struct local_vars_finder {
	struct probe_finder *pf;
	struct perf_probe_arg *args;
	int max_args;
	int nargs;
	int ret;
};

/* Collect available variables in this scope */
static int copy_variables_cb(Dwarf_Die *die_mem, void *data)
{
	struct local_vars_finder *vf = data;
	struct probe_finder *pf = vf->pf;
	int tag;

	tag = dwarf_tag(die_mem);
	if (tag == DW_TAG_formal_parameter ||
	    tag == DW_TAG_variable) {
		if (convert_variable_location(die_mem, vf->pf->addr,
					      vf->pf->fb_ops, &pf->sp_die,
					      NULL) == 0) {
			vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem);
			if (vf->args[vf->nargs].var == NULL) {
				vf->ret = -ENOMEM;
				return DIE_FIND_CB_END;
			}
			pr_debug(" %s", vf->args[vf->nargs].var);
			vf->nargs++;
		}
	}

	if (dwarf_haspc(die_mem, vf->pf->addr))
		return DIE_FIND_CB_CONTINUE;
	else
		return DIE_FIND_CB_SIBLING;
}

static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
			     struct perf_probe_arg *args)
{
	Dwarf_Die die_mem;
	int i;
	int n = 0;
	struct local_vars_finder vf = {.pf = pf, .args = args,
				.max_args = MAX_PROBE_ARGS, .ret = 0};

	for (i = 0; i < pf->pev->nargs; i++) {
		/* var never be NULL */
		if (strcmp(pf->pev->args[i].var, "$vars") == 0) {
			pr_debug("Expanding $vars into:");
			vf.nargs = n;
			/* Special local variables */
			die_find_child(sc_die, copy_variables_cb, (void *)&vf,
				       &die_mem);
			pr_debug(" (%d)\n", vf.nargs - n);
			if (vf.ret < 0)
				return vf.ret;
			n = vf.nargs;
		} else {
			/* Copy normal argument */
			args[n] = pf->pev->args[i];
			n++;
		}
	}
	return n;
}

/* Add a found probe point into trace event list */
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);
	struct probe_trace_event *tev;
	struct perf_probe_arg *args;
	int ret, i;

	/* Check number of tevs */
	if (tf->ntevs == tf->max_tevs) {
		pr_warning("Too many( > %d) probe point found.\n",
			   tf->max_tevs);
		return -ERANGE;
	}
	tev = &tf->tevs[tf->ntevs++];

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

	pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
		 tev->point.offset);

	/* Expand special probe argument if exist */
	args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS);
	if (args == NULL)
		return -ENOMEM;

	ret = expand_probe_args(sc_die, pf, args);
	if (ret < 0)
		goto end;

	tev->nargs = ret;
	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
	if (tev->args == NULL) {
		ret = -ENOMEM;
		goto end;
	}

	/* Find each argument */
	for (i = 0; i < tev->nargs; i++) {
		pf->pvar = &args[i];
		pf->tvar = &tev->args[i];
		/* Variable should be found from scope DIE */
		ret = find_variable(sc_die, pf);
		if (ret != 0)
			break;
	}

end:
	free(args);
	return ret;
}

/* Find probe_trace_events specified by perf_probe_event from debuginfo */
int debuginfo__find_trace_events(struct debuginfo *dbg,
				 struct perf_probe_event *pev,
				 struct probe_trace_event **tevs, int max_tevs)
{
	struct trace_event_finder tf = {
			.pf = {.pev = pev, .callback = add_probe_trace_event},
			.mod = dbg->mod, .max_tevs = max_tevs};
	int ret;

	/* Allocate result tevs array */
	*tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
	if (*tevs == NULL)
		return -ENOMEM;

	tf.tevs = *tevs;
	tf.ntevs = 0;

	ret = debuginfo__find_probes(dbg, &tf.pf);
	if (ret < 0) {
		zfree(tevs);
		return ret;
	}

	return (ret < 0) ? ret : tf.ntevs;
}

#define MAX_VAR_LEN 64

/* Collect available variables in this scope */
static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
{
	struct available_var_finder *af = data;
	struct variable_list *vl;
	char buf[MAX_VAR_LEN];
	int tag, ret;

	vl = &af->vls[af->nvls - 1];

	tag = dwarf_tag(die_mem);
	if (tag == DW_TAG_formal_parameter ||
	    tag == DW_TAG_variable) {
		ret = convert_variable_location(die_mem, af->pf.addr,
						af->pf.fb_ops, &af->pf.sp_die,
						NULL);
		if (ret == 0) {
			ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
			pr_debug2("Add new var: %s\n", buf);
			if (ret > 0)
				strlist__add(vl->vars, buf);
		}
	}

	if (af->child && dwarf_haspc(die_mem, af->pf.addr))
		return DIE_FIND_CB_CONTINUE;
	else
		return DIE_FIND_CB_SIBLING;
}

/* Add a found vars into available variables list */
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);
	struct variable_list *vl;
	Dwarf_Die die_mem;
	int ret;

	/* Check number of tevs */
	if (af->nvls == af->max_vls) {
		pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
		return -ERANGE;
	}
	vl = &af->vls[af->nvls++];

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

	pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
		 vl->point.offset);

	/* Find local variables */
	vl->vars = strlist__new(true, NULL);
	if (vl->vars == NULL)
		return -ENOMEM;
	af->child = true;
	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;
	die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem);

out:
	if (strlist__empty(vl->vars)) {
		strlist__delete(vl->vars);
		vl->vars = NULL;
	}

	return ret;
}

/*
 * Find available variables at given probe point
 * Return the number of found probe points. Return 0 if there is no
 * matched probe point. Return <0 if an error occurs.
 */
int debuginfo__find_available_vars_at(struct debuginfo *dbg,
				      struct perf_probe_event *pev,
				      struct variable_list **vls,
				      int max_vls, bool externs)
{
	struct available_var_finder af = {
			.pf = {.pev = pev, .callback = add_available_vars},
			.mod = dbg->mod,
			.max_vls = max_vls, .externs = externs};
	int ret;

	/* Allocate result vls array */
	*vls = zalloc(sizeof(struct variable_list) * max_vls);
	if (*vls == NULL)
		return -ENOMEM;

	af.vls = *vls;
	af.nvls = 0;

	ret = debuginfo__find_probes(dbg, &af.pf);
	if (ret < 0) {
		/* Free vlist for error */
		while (af.nvls--) {
			zfree(&af.vls[af.nvls].point.symbol);
			strlist__delete(af.vls[af.nvls].vars);
		}
		zfree(vls);
		return ret;
	}

	return (ret < 0) ? ret : af.nvls;
}

/* Reverse search */
int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
				struct perf_probe_point *ppt)
{
	Dwarf_Die cudie, spdie, indie;
	Dwarf_Addr _addr = 0, baseaddr = 0;
	const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp;
	int baseline = 0, lineno = 0, ret = 0;

	/* Find cu die */
	if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) {
		pr_warning("Failed to find debug information for address %lx\n",
			   addr);
		ret = -EINVAL;
		goto end;
	}

	/* Find a corresponding line (filename and lineno) */
	cu_find_lineinfo(&cudie, addr, &fname, &lineno);
	/* Don't care whether it failed or not */

	/* Find a corresponding function (name, baseline and baseaddr) */
	if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
		/* Get function entry information */
		func = basefunc = dwarf_diename(&spdie);
		if (!func ||
		    dwarf_entrypc(&spdie, &baseaddr) != 0 ||
		    dwarf_decl_line(&spdie, &baseline) != 0) {
			lineno = 0;
			goto post;
		}

		fname = dwarf_decl_file(&spdie);
		if (addr == (unsigned long)baseaddr) {
			/* Function entry - Relative line number is 0 */
			lineno = baseline;
			goto post;
		}

		/* Track down the inline functions step by step */
		while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr,
						&indie)) {
			/* There is an inline function */
			if (dwarf_entrypc(&indie, &_addr) == 0 &&
			    _addr == addr) {
				/*
				 * addr is at an inline function entry.
				 * In this case, lineno should be the call-site
				 * line number. (overwrite lineinfo)
				 */
				lineno = die_get_call_lineno(&indie);
				fname = die_get_call_file(&indie);
				break;
			} else {
				/*
				 * addr is in an inline function body.
				 * Since lineno points one of the lines
				 * of the inline function, baseline should
				 * be the entry line of the inline function.
				 */
				tmp = dwarf_diename(&indie);
				if (!tmp ||
				    dwarf_decl_line(&indie, &baseline) != 0)
					break;
				func = tmp;
				spdie = indie;
			}
		}
		/* Verify the lineno and baseline are in a same file */
		tmp = dwarf_decl_file(&spdie);
		if (!tmp || strcmp(tmp, fname) != 0)
			lineno = 0;
	}

post:
	/* Make a relative line number or an offset */
	if (lineno)
		ppt->line = lineno - baseline;
	else if (basefunc) {
		ppt->offset = addr - (unsigned long)baseaddr;
		func = basefunc;
	}

	/* Duplicate strings */
	if (func) {
		ppt->function = strdup(func);
		if (ppt->function == NULL) {
			ret = -ENOMEM;
			goto end;
		}
	}
	if (fname) {
		ppt->file = strdup(fname);
		if (ppt->file == NULL) {
			zfree(&ppt->function);
			ret = -ENOMEM;
			goto end;
		}
	}
end:
	if (ret == 0 && (fname || func))
		ret = 1;	/* Found a point */
	return ret;
}

/* Add a line and store the src path */
static int line_range_add_line(const char *src, unsigned int lineno,
			       struct line_range *lr)
{
	/* Copy source path */
	if (!lr->path) {
		lr->path = strdup(src);
		if (lr->path == NULL)
			return -ENOMEM;
	}
	return intlist__add(lr->line_list, lineno);
}

static int line_range_walk_cb(const char *fname, int lineno,
			      Dwarf_Addr addr __maybe_unused,
			      void *data)
{
	struct line_finder *lf = data;
	int err;

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

	err = line_range_add_line(fname, lineno, lf->lr);
	if (err < 0 && err != -EEXIST)
		return err;

	return 0;
}

/* Find line range from its line number */
static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
{
	int ret;

	ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);

	/* Update status */
	if (ret >= 0)
		if (!intlist__empty(lf->lr->line_list))
			ret = lf->found = 1;
		else
			ret = 0;	/* Lines are not found */
	else {
		zfree(&lf->lr->path);
	}
	return ret;
}
Example #5
0
/**
 * die_walk_instances - Walk on instances of given DIE
 * @or_die: an abstract original DIE
 * @callback: a callback function which is called with instance DIE
 * @data: user data
 *
 * Walk on the instances of give @in_die. @in_die must be an inlined function
 * declartion. This returns the return value of @callback if it returns
 * non-zero value, or -ENOENT if there is no instance.
 */
int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *),
		       void *data)
{
	Dwarf_Die cu_die;
	Dwarf_Die die_mem;
	struct __instance_walk_param iwp = {
		.addr = or_die->addr,
		.callback = callback,
		.data = data,
		.retval = -ENOENT,
	};

	if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL)
		return -ENOENT;

	die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem);

	return iwp.retval;
}

/* Line walker internal parameters */
struct __line_walk_param {
	bool recursive;
	line_walk_callback_t callback;
	void *data;
	int retval;
};

static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
{
	struct __line_walk_param *lw = data;
	Dwarf_Addr addr = 0;
	const char *fname;
	int lineno;

	if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
		fname = die_get_call_file(in_die);
		lineno = die_get_call_lineno(in_die);
		if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
			lw->retval = lw->callback(fname, lineno, addr, lw->data);
			if (lw->retval != 0)
				return DIE_FIND_CB_END;
		}
	}
	if (!lw->recursive)
		/* Don't need to search recursively */
		return DIE_FIND_CB_SIBLING;

	if (addr) {
		fname = dwarf_decl_file(in_die);
		if (fname && dwarf_decl_line(in_die, &lineno) == 0) {
			lw->retval = lw->callback(fname, lineno, addr, lw->data);
			if (lw->retval != 0)
				return DIE_FIND_CB_END;
		}
	}

	/* Continue to search nested inlined function call-sites */
	return DIE_FIND_CB_CONTINUE;
}

/* Walk on lines of blocks included in given DIE */
static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive,
				line_walk_callback_t callback, void *data)
{
	struct __line_walk_param lw = {
		.recursive = recursive,
		.callback = callback,
		.data = data,
		.retval = 0,
	};
	Dwarf_Die die_mem;
	Dwarf_Addr addr;
	const char *fname;
	int lineno;

	/* 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) {
		lw.retval = callback(fname, lineno, addr, data);
		if (lw.retval != 0)
			goto done;
	}
	die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
done:
	return lw.retval;
}

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, true, lw->callback, lw->data);
	if (lw->retval != 0)
		return DWARF_CB_ABORT;

	return DWARF_CB_OK;
}

/**
 * die_walk_lines - Walk on lines inside given DIE
 * @rt_die: a root DIE (CU, subprogram or inlined_subroutine)
 * @callback: callback routine
 * @data: user data
 *
 * Walk on all lines inside given @rt_die and call @callback on each line.
 * If the @rt_die is a function, walk only on the lines inside the function,
 * otherwise @rt_die must be a CU DIE.
 * Note that this walks not only dwarf line list, but also function entries
 * and inline call-site.
 */
int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, 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(rt_die) != DW_TAG_compile_unit)
		cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL);
	else
		cu_die = rt_die;
	if (!cu_die) {
		pr_debug2("Failed to get CU from given DIE.\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 (rt_die != cu_die)
			/*
			 * Address filtering
			 * The line is included in given function, and
			 * no inline block includes it.
			 */
			if (!dwarf_haspc(rt_die, addr) ||
			    die_find_inlinefunc(rt_die, addr, &die_mem))
				continue;
		/* Get source line */
		fname = dwarf_linesrc(line, NULL, NULL);

		ret = callback(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 (rt_die != cu_die)
		/*
		 * Don't need walk functions recursively, because nested
		 * inlined functions don't have lines of the specified DIE.
		 */
		ret = __die_walk_funclines(rt_die, false, callback, data);
	else {
		struct __line_walk_param param = {
			.callback = callback,
			.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;
};

static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
{
	struct __find_variable_param *fvp = data;
	Dwarf_Attribute attr;
	int tag;

	tag = dwarf_tag(die_mem);
	if ((tag == DW_TAG_formal_parameter ||
	     tag == DW_TAG_variable) &&
	    die_compare_name(die_mem, fvp->name) &&
	/* Does the DIE have location information or external instance? */
	    (dwarf_attr(die_mem, DW_AT_external, &attr) ||
	     dwarf_attr(die_mem, DW_AT_location, &attr)))
		return DIE_FIND_CB_END;
	if (dwarf_haspc(die_mem, fvp->addr))
		return DIE_FIND_CB_CONTINUE;
	else
		return DIE_FIND_CB_SIBLING;
}

/**
 * die_find_variable_at - Find a given name variable at given address
 * @sp_die: a function DIE
 * @name: variable name
 * @addr: address
 * @die_mem: a buffer for result DIE
 *
 * Find a variable DIE called @name at @addr in @sp_die.
 */
Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
				Dwarf_Addr addr, Dwarf_Die *die_mem)
{
	struct __find_variable_param fvp = { .name = name, .addr = addr};

	return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
			      die_mem);
}

static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
{
	const char *name = data;

	if (dwarf_tag(die_mem) == DW_TAG_member) {
		if (die_compare_name(die_mem, name))
			return DIE_FIND_CB_END;
		else if (!dwarf_diename(die_mem)) {	/* Unnamed structure */
			Dwarf_Die type_die, tmp_die;
			if (die_get_type(die_mem, &type_die) &&
			    die_find_member(&type_die, name, &tmp_die))
				return DIE_FIND_CB_END;
		}
	}
	return DIE_FIND_CB_SIBLING;
}

/**
 * die_find_member - Find a given name member in a data structure
 * @st_die: a data structure type DIE
 * @name: member name
 * @die_mem: a buffer for result DIE
 *
 * Find a member DIE called @name in @st_die.
 */
Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
			   Dwarf_Die *die_mem)
{
	return die_find_child(st_die, __die_find_member_cb, (void *)name,
			      die_mem);
}

/**
 * die_get_typename - Get the name of given variable DIE
 * @vr_die: a variable DIE
 * @buf: a buffer for result type name
 * @len: a max-length of @buf
 *
 * Get the name of @vr_die and stores it to @buf. Return the actual length
 * of type name if succeeded. Return -E2BIG if @len is not enough long, and
 * Return -ENOENT if failed to find type name.
 * Note that the result will stores typedef name if possible, and stores
 * "*(function_type)" if the type is a function pointer.
 */
int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
{
	Dwarf_Die type;
	int tag, ret, ret2;
	const char *tmp = "";

	if (__die_get_real_type(vr_die, &type) == NULL)
		return -ENOENT;

	tag = dwarf_tag(&type);
	if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
		tmp = "*";
	else if (tag == DW_TAG_subroutine_type) {
		/* Function pointer */
		ret = snprintf(buf, len, "(function_type)");
		return (ret >= len) ? -E2BIG : ret;
	} else {
		if (!dwarf_diename(&type))
			return -ENOENT;
		if (tag == DW_TAG_union_type)
			tmp = "union ";
		else if (tag == DW_TAG_structure_type)
			tmp = "struct ";
		else if (tag == DW_TAG_enumeration_type)
			tmp = "enum ";
		/* Write a base name */
		ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
		return (ret >= len) ? -E2BIG : ret;
	}
	ret = die_get_typename(&type, buf, len);
	if (ret > 0) {
		ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
		ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
	}
	return ret;
}

/**
 * die_get_varname - Get the name and type of given variable DIE
 * @vr_die: a variable DIE
 * @buf: a buffer for type and variable name
 * @len: the max-length of @buf
 *
 * Get the name and type of @vr_die and stores it in @buf as "type\tname".
 */
int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
{
	int ret, ret2;

	ret = die_get_typename(vr_die, buf, len);
	if (ret < 0) {
		pr_debug("Failed to get type, make it unknown.\n");
		ret = snprintf(buf, len, "(unknown_type)");
	}
	if (ret > 0) {
		ret2 = snprintf(buf + ret, len - ret, "\t%s",
				dwarf_diename(vr_die));
		ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
	}
	return ret;
}
Example #6
0
/* Reverse search */
int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
				struct perf_probe_point *ppt)
{
	Dwarf_Die cudie, spdie, indie;
	Dwarf_Addr _addr, baseaddr;
	const char *fname = NULL, *func = NULL, *tmp;
	int baseline = 0, lineno = 0, ret = 0;

	/* Adjust address with bias */
	addr += self->bias;

	/* Find cu die */
	if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) {
		pr_warning("Failed to find debug information for address %lx\n",
			   addr);
		ret = -EINVAL;
		goto end;
	}

	/* Find a corresponding line (filename and lineno) */
	cu_find_lineinfo(&cudie, addr, &fname, &lineno);
	/* Don't care whether it failed or not */

	/* Find a corresponding function (name, baseline and baseaddr) */
	if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
		/* Get function entry information */
		tmp = dwarf_diename(&spdie);
		if (!tmp ||
		    dwarf_entrypc(&spdie, &baseaddr) != 0 ||
		    dwarf_decl_line(&spdie, &baseline) != 0)
			goto post;
		func = tmp;

		if (addr == (unsigned long)baseaddr)
			/* Function entry - Relative line number is 0 */
			lineno = baseline;
		else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr,
					     &indie)) {
			if (dwarf_entrypc(&indie, &_addr) == 0 &&
			    _addr == addr)
				/*
				 * addr is at an inline function entry.
				 * In this case, lineno should be the call-site
				 * line number.
				 */
				lineno = die_get_call_lineno(&indie);
			else {
				/*
				 * addr is in an inline function body.
				 * Since lineno points one of the lines
				 * of the inline function, baseline should
				 * be the entry line of the inline function.
				 */
				tmp = dwarf_diename(&indie);
				if (tmp &&
				    dwarf_decl_line(&spdie, &baseline) == 0)
					func = tmp;
			}
		}
	}

post:
	/* Make a relative line number or an offset */
	if (lineno)
		ppt->line = lineno - baseline;
	else if (func)
		ppt->offset = addr - (unsigned long)baseaddr;

	/* Duplicate strings */
	if (func) {
		ppt->function = strdup(func);
		if (ppt->function == NULL) {
			ret = -ENOMEM;
			goto end;
		}
	}
	if (fname) {
		ppt->file = strdup(fname);
		if (ppt->file == NULL) {
			if (ppt->function) {
				free(ppt->function);
				ppt->function = NULL;
			}
			ret = -ENOMEM;
			goto end;
		}
	}
end:
	if (ret == 0 && (fname || func))
		ret = 1;	/* Found a point */
	return ret;
}