diff options
Diffstat (limited to 'tools/perf/util/probe-finder.c')
| -rw-r--r-- | tools/perf/util/probe-finder.c | 645 | 
1 files changed, 511 insertions, 134 deletions
| diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 32b81f707ff5..3991d73d1cff 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)  	}  } +/* Dwarf FL wrappers */ + +static int __linux_kernel_find_elf(Dwfl_Module *mod, +				   void **userdata, +				   const char *module_name, +				   Dwarf_Addr base, +				   char **file_name, Elf **elfp) +{ +	int fd; +	const char *path = kernel_get_module_path(module_name); + +	if (path) { +		fd = open(path, O_RDONLY); +		if (fd >= 0) { +			*file_name = strdup(path); +			return fd; +		} +	} +	/* If failed, try to call standard method */ +	return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base, +					  file_name, elfp); +} + +static char *debuginfo_path;	/* Currently dummy */ + +static const Dwfl_Callbacks offline_callbacks = { +	.find_debuginfo = dwfl_standard_find_debuginfo, +	.debuginfo_path = &debuginfo_path, + +	.section_address = dwfl_offline_section_address, + +	/* We use this table for core files too.  */ +	.find_elf = dwfl_build_id_find_elf, +}; + +static const Dwfl_Callbacks kernel_callbacks = { +	.find_debuginfo = dwfl_standard_find_debuginfo, +	.debuginfo_path = &debuginfo_path, + +	.find_elf = __linux_kernel_find_elf, +	.section_address = dwfl_linux_kernel_module_section_address, +}; + +/* Get a Dwarf from offline image */ +static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) +{ +	Dwfl_Module *mod; +	Dwarf *dbg = NULL; + +	if (!dwflp) +		return NULL; + +	*dwflp = dwfl_begin(&offline_callbacks); +	if (!*dwflp) +		return NULL; + +	mod = dwfl_report_offline(*dwflp, "", "", fd); +	if (!mod) +		goto error; + +	dbg = dwfl_module_getdwarf(mod, bias); +	if (!dbg) { +error: +		dwfl_end(*dwflp); +		*dwflp = NULL; +	} +	return dbg; +} + +/* Get a Dwarf from live kernel image */ +static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, +					  Dwarf_Addr *bias) +{ +	Dwarf *dbg; + +	if (!dwflp) +		return NULL; + +	*dwflp = dwfl_begin(&kernel_callbacks); +	if (!*dwflp) +		return NULL; + +	/* Load the kernel dwarves: Don't care the result here */ +	dwfl_linux_kernel_report_kernel(*dwflp); +	dwfl_linux_kernel_report_modules(*dwflp); + +	dbg = dwfl_addrdwarf(*dwflp, addr, bias); +	/* Here, check whether we could get a real dwarf */ +	if (!dbg) { +		dwfl_end(*dwflp); +		*dwflp = NULL; +	} +	return dbg; +} +  /* Dwarf wrappers */  /* Find the realpath of the target file. */ @@ -160,26 +255,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)  	return name ? (strcmp(tname, name) == 0) : false;  } -/* Get type die, but skip qualifiers and typedef */ -static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) +/* Get type die */ +static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)  {  	Dwarf_Attribute attr; + +	if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && +	    dwarf_formref_die(&attr, die_mem)) +		return die_mem; +	else +		return NULL; +} + +/* Get a type die, but skip qualifiers */ +static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) +{  	int tag;  	do { -		if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || -		    dwarf_formref_die(&attr, die_mem) == NULL) -			return NULL; - -		tag = dwarf_tag(die_mem); -		vr_die = die_mem; +		vr_die = die_get_type(vr_die, die_mem); +		if (!vr_die) +			break; +		tag = dwarf_tag(vr_die);  	} while (tag == DW_TAG_const_type ||  		 tag == DW_TAG_restrict_type ||  		 tag == DW_TAG_volatile_type || -		 tag == DW_TAG_shared_type || -		 tag == DW_TAG_typedef); +		 tag == DW_TAG_shared_type); + +	return vr_die; +} -	return die_mem; +/* Get a type die, but skip qualifiers and typedef */ +static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) +{ +	do { +		vr_die = __die_get_real_type(vr_die, die_mem); +	} while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); + +	return vr_die;  }  static bool die_is_signed_type(Dwarf_Die *tp_die) @@ -320,25 +433,35 @@ 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);  } +struct __find_variable_param { +	const char *name; +	Dwarf_Addr addr; +}; +  static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)  { -	const char *name = data; +	struct __find_variable_param *fvp = data;  	int tag;  	tag = dwarf_tag(die_mem);  	if ((tag == DW_TAG_formal_parameter ||  	     tag == DW_TAG_variable) && -	    die_compare_name(die_mem, name)) +	    die_compare_name(die_mem, fvp->name))  		return DIE_FIND_CB_FOUND; -	return DIE_FIND_CB_CONTINUE; +	if (dwarf_haspc(die_mem, fvp->addr)) +		return DIE_FIND_CB_CONTINUE; +	else +		return DIE_FIND_CB_SIBLING;  } -/* Find a variable called 'name' */ -static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, -				    Dwarf_Die *die_mem) +/* Find a variable called 'name' at given address */ +static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, +				       Dwarf_Addr addr, Dwarf_Die *die_mem)  { -	return die_find_child(sp_die, __die_find_variable_cb, (void *)name, +	struct __find_variable_param fvp = { .name = name, .addr = addr}; + +	return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,  			      die_mem);  } @@ -361,6 +484,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,  			      die_mem);  } +/* Get the name of given variable DIE */ +static 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 "; +		/* 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; +} + +/* Get the name and type of given variable DIE, stored as "type\tname" */ +static 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; +} +  /*   * Probe finder related functions   */ @@ -374,8 +551,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)  	return ref;  } -/* Show a location */ -static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) +/* + * Convert a location into trace_arg. + * If tvar == NULL, this just checks variable can be converted. + */ +static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr, +				     Dwarf_Op *fb_ops, +				     struct probe_trace_arg *tvar)  {  	Dwarf_Attribute attr;  	Dwarf_Op *op; @@ -384,20 +566,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)  	Dwarf_Word offs = 0;  	bool ref = false;  	const char *regs; -	struct probe_trace_arg *tvar = pf->tvar;  	int ret; +	if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL) +		goto static_var; +  	/* TODO: handle more than 1 exprs */  	if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || -	    dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || +	    dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||  	    nops == 0) {  		/* TODO: Support const_value */ -		pr_err("Failed to find the location of %s at this address.\n" -		       " Perhaps, it has been optimized out.\n", pf->pvar->var);  		return -ENOENT;  	}  	if (op->atom == DW_OP_addr) { +static_var: +		if (!tvar) +			return 0;  		/* Static variables on memory (not stack), make @varname */  		ret = strlen(dwarf_diename(vr_die));  		tvar->value = zalloc(ret + 2); @@ -412,14 +597,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)  	/* If this is based on frame buffer, set the offset */  	if (op->atom == DW_OP_fbreg) { -		if (pf->fb_ops == NULL) { -			pr_warning("The attribute of frame base is not " -				   "supported.\n"); +		if (fb_ops == NULL)  			return -ENOTSUP; -		}  		ref = true;  		offs = op->number; -		op = &pf->fb_ops[0]; +		op = &fb_ops[0];  	}  	if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { @@ -435,13 +617,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)  	} else if (op->atom == DW_OP_regx) {  		regn = op->number;  	} else { -		pr_warning("DW_OP %x is not supported.\n", op->atom); +		pr_debug("DW_OP %x is not supported.\n", op->atom);  		return -ENOTSUP;  	} +	if (!tvar) +		return 0; +  	regs = get_arch_regstr(regn);  	if (!regs) { -		pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); +		/* This should be a bug in DWARF or this tool */ +		pr_warning("Mapping for DWARF register number %u " +			   "missing on this architecture.", regn);  		return -ERANGE;  	} @@ -666,8 +853,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)  	pr_debug("Converting variable %s into trace event.\n",  		 dwarf_diename(vr_die)); -	ret = convert_variable_location(vr_die, pf); -	if (ret == 0 && pf->pvar->field) { +	ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops, +					pf->tvar); +	if (ret == -ENOENT) +		pr_err("Failed to find the location of %s at this address.\n" +		       " Perhaps, it has been optimized out.\n", pf->pvar->var); +	else if (ret == -ENOTSUP) +		pr_err("Sorry, we don't support this variable location yet.\n"); +	else if (pf->pvar->field) {  		ret = convert_variable_fields(vr_die, pf->pvar->var,  					      pf->pvar->field, &pf->tvar->ref,  					      &die_mem); @@ -722,56 +915,39 @@ 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(sp_die, pf->pvar->var, &vr_die)) +	if (die_find_variable_at(sp_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); -		if (nscopes > 0) { -			ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, -						0, NULL, 0, 0, &vr_die); -			if (ret >= 0) +		while (nscopes-- > 1) { +			pr_debug("Searching variables in %s\n", +				 dwarf_diename(&scopes[nscopes])); +			/* We should check this scope, so give dummy address */ +			if (die_find_variable_at(&scopes[nscopes], +						 pf->pvar->var, 0, +						 &vr_die)) {  				ret = convert_variable(&vr_die, pf); -			else -				ret = -ENOENT; +				goto found; +			} +		} +		if (scopes)  			free(scopes); -		} else -			ret = -ENOENT; +		ret = -ENOENT;  	} +found:  	if (ret < 0)  		pr_warning("Failed to find '%s' in this function.\n",  			   pf->pvar->var);  	return ret;  } -/* Show a probe point to output buffer */ -static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) +/* Convert subprogram DIE to trace point */ +static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, +				  bool retprobe, struct probe_trace_point *tp)  { -	struct probe_trace_event *tev;  	Dwarf_Addr eaddr; -	Dwarf_Die die_mem;  	const char *name; -	int ret, i; -	Dwarf_Attribute fb_attr; -	size_t nops; - -	if (pf->ntevs == pf->max_tevs) { -		pr_warning("Too many( > %d) probe point found.\n", -			   pf->max_tevs); -		return -ERANGE; -	} -	tev = &pf->tevs[pf->ntevs++]; - -	/* If no real subprogram, find a real one */ -	if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { -		sp_die = die_find_real_subprogram(&pf->cu_die, -						 pf->addr, &die_mem); -		if (!sp_die) { -			pr_warning("Failed to find probe point in any " -				   "functions.\n"); -			return -ENOENT; -		} -	}  	/* Copy the name of probe point */  	name = dwarf_diename(sp_die); @@ -781,26 +957,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)  				   dwarf_diename(sp_die));  			return -ENOENT;  		} -		tev->point.symbol = strdup(name); -		if (tev->point.symbol == NULL) +		tp->symbol = strdup(name); +		if (tp->symbol == NULL)  			return -ENOMEM; -		tev->point.offset = (unsigned long)(pf->addr - eaddr); +		tp->offset = (unsigned long)(paddr - eaddr);  	} else  		/* This function has no name. */ -		tev->point.offset = (unsigned long)pf->addr; +		tp->offset = (unsigned long)paddr;  	/* Return probe must be on the head of a subprogram */ -	if (pf->pev->point.retprobe) { -		if (tev->point.offset != 0) { +	if (retprobe) { +		if (eaddr != paddr) {  			pr_warning("Return probe must be on the head of"  				   " a real function\n");  			return -EINVAL;  		} -		tev->point.retprobe = true; +		tp->retprobe = true;  	} -	pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, -		 tev->point.offset); +	return 0; +} + +/* Call probe_finder callback with real subprogram DIE */ +static int call_probe_finder(Dwarf_Die *sp_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_real_subprogram(&pf->cu_die, +						  pf->addr, &die_mem); +		if (!sp_die) { +			pr_warning("Failed to find probe point in any " +				   "functions.\n"); +			return -ENOENT; +		} +	}  	/* Get the frame base attribute/ops */  	dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); @@ -820,22 +1015,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)  #endif  	} -	/* Find each argument */ -	tev->nargs = pf->pev->nargs; -	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); -	if (tev->args == NULL) -		return -ENOMEM; -	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); -		if (ret != 0) -			return ret; -	} +	/* Call finder's callback handler */ +	ret = pf->callback(sp_die, pf);  	/* *pf->fb_ops will be cached in libdw. Don't free it. */  	pf->fb_ops = NULL; -	return 0; + +	return ret;  }  /* Find probe point from its line number */ @@ -871,7 +1057,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)  			 (int)i, lineno, (uintmax_t)addr);  		pf->addr = addr; -		ret = convert_probe_point(NULL, pf); +		ret = call_probe_finder(NULL, pf);  		/* Continuing, because target line might be inlined. */  	}  	return ret; @@ -984,7 +1170,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)  			 (int)i, lineno, (unsigned long long)addr);  		pf->addr = addr; -		ret = convert_probe_point(sp_die, pf); +		ret = call_probe_finder(sp_die, pf);  		/* Continuing, because target line might be inlined. */  	}  	/* TODO: deallocate lines, but how? */ @@ -1019,7 +1205,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)  		pr_debug("found inline addr: 0x%jx\n",  			 (uintmax_t)pf->addr); -		param->retval = convert_probe_point(in_die, pf); +		param->retval = call_probe_finder(in_die, pf);  		if (param->retval < 0)  			return DWARF_CB_ABORT;  	} @@ -1057,7 +1243,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)  			}  			pf->addr += pp->offset;  			/* TODO: Check the address in this function */ -			param->retval = convert_probe_point(sp_die, pf); +			param->retval = call_probe_finder(sp_die, pf);  		}  	} else {  		struct dwarf_callback_param _param = {.data = (void *)pf, @@ -1079,90 +1265,276 @@ static int find_probe_point_by_func(struct probe_finder *pf)  	return _param.retval;  } -/* Find probe_trace_events specified by perf_probe_event from debuginfo */ -int find_probe_trace_events(int fd, struct perf_probe_event *pev, -			     struct probe_trace_event **tevs, int max_tevs) +/* Find probe points from debuginfo */ +static int find_probes(int fd, struct probe_finder *pf)  { -	struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; -	struct perf_probe_point *pp = &pev->point; +	struct perf_probe_point *pp = &pf->pev->point;  	Dwarf_Off off, noff;  	size_t cuhl;  	Dwarf_Die *diep; -	Dwarf *dbg; +	Dwarf *dbg = NULL; +	Dwfl *dwfl; +	Dwarf_Addr bias;	/* Currently ignored */  	int ret = 0; -	pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); -	if (pf.tevs == NULL) -		return -ENOMEM; -	*tevs = pf.tevs; -	pf.ntevs = 0; - -	dbg = dwarf_begin(fd, DWARF_C_READ); +	dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);  	if (!dbg) {  		pr_warning("No dwarf info found in the vmlinux - "  			"please rebuild with CONFIG_DEBUG_INFO=y.\n"); -		free(pf.tevs); -		*tevs = NULL;  		return -EBADF;  	}  #if _ELFUTILS_PREREQ(0, 142)  	/* Get the call frame information from this dwarf */ -	pf.cfi = dwarf_getcfi(dbg); +	pf->cfi = dwarf_getcfi(dbg);  #endif  	off = 0; -	line_list__init(&pf.lcache); +	line_list__init(&pf->lcache);  	/* Loop on CUs (Compilation Unit) */  	while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&  	       ret >= 0) {  		/* Get the DIE(Debugging Information Entry) of this CU */ -		diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); +		diep = dwarf_offdie(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); +			pf->fname = cu_find_realpath(&pf->cu_die, pp->file);  		else -			pf.fname = NULL; +			pf->fname = NULL; -		if (!pp->file || pf.fname) { +		if (!pp->file || pf->fname) {  			if (pp->function) -				ret = find_probe_point_by_func(&pf); +				ret = find_probe_point_by_func(pf);  			else if (pp->lazy_line) -				ret = find_probe_point_lazy(NULL, &pf); +				ret = find_probe_point_lazy(NULL, pf);  			else { -				pf.lno = pp->line; -				ret = find_probe_point_by_line(&pf); +				pf->lno = pp->line; +				ret = find_probe_point_by_line(pf);  			}  		}  		off = noff;  	} -	line_list__free(&pf.lcache); -	dwarf_end(dbg); +	line_list__free(&pf->lcache); +	if (dwfl) +		dwfl_end(dwfl); -	return (ret < 0) ? ret : pf.ntevs; +	return ret; +} + +/* Add a found probe point into trace event list */ +static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) +{ +	struct trace_event_finder *tf = +			container_of(pf, struct trace_event_finder, pf); +	struct probe_trace_event *tev; +	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++]; + +	ret = convert_to_trace_point(sp_die, 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); + +	/* Find each argument */ +	tev->nargs = pf->pev->nargs; +	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); +	if (tev->args == NULL) +		return -ENOMEM; +	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); +		if (ret != 0) +			return ret; +	} + +	return 0; +} + +/* Find probe_trace_events specified by perf_probe_event from debuginfo */ +int find_probe_trace_events(int fd, 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}, +			.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 = find_probes(fd, &tf.pf); +	if (ret < 0) { +		free(*tevs); +		*tevs = NULL; +		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, 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 *sp_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, *scopes = NULL; +	int ret, nscopes; + +	/* 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++]; + +	ret = convert_to_trace_point(sp_die, 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(sp_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); +	while (nscopes-- > 1) +		die_find_child(&scopes[nscopes], collect_variables_cb, +			       (void *)af, &die_mem); +	if (scopes) +		free(scopes); + +out: +	if (strlist__empty(vl->vars)) { +		strlist__delete(vl->vars); +		vl->vars = NULL; +	} + +	return ret; +} + +/* Find available variables at given probe point */ +int find_available_vars_at(int fd, 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}, +			.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 = find_probes(fd, &af.pf); +	if (ret < 0) { +		/* Free vlist for error */ +		while (af.nvls--) { +			if (af.vls[af.nvls].point.symbol) +				free(af.vls[af.nvls].point.symbol); +			if (af.vls[af.nvls].vars) +				strlist__delete(af.vls[af.nvls].vars); +		} +		free(af.vls); +		*vls = NULL; +		return ret; +	} + +	return (ret < 0) ? ret : af.nvls;  }  /* Reverse search */ -int find_perf_probe_point(int fd, unsigned long addr, -			  struct perf_probe_point *ppt) +int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)  {  	Dwarf_Die cudie, spdie, indie; -	Dwarf *dbg; +	Dwarf *dbg = NULL; +	Dwfl *dwfl = NULL;  	Dwarf_Line *line; -	Dwarf_Addr laddr, eaddr; +	Dwarf_Addr laddr, eaddr, bias = 0;  	const char *tmp;  	int lineno, ret = 0;  	bool found = false; -	dbg = dwarf_begin(fd, DWARF_C_READ); -	if (!dbg) -		return -EBADF; +	/* Open the live linux kernel */ +	dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); +	if (!dbg) { +		pr_warning("No dwarf info found in the vmlinux - " +			"please rebuild with CONFIG_DEBUG_INFO=y.\n"); +		ret = -EINVAL; +		goto end; +	} +	/* Adjust address with bias */ +	addr += bias;  	/* Find cu die */ -	if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { +	if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { +		pr_warning("No CU DIE is found at %lx\n", addr);  		ret = -EINVAL;  		goto end;  	} @@ -1225,7 +1597,8 @@ found:  	}  end: -	dwarf_end(dbg); +	if (dwfl) +		dwfl_end(dwfl);  	if (ret >= 0)  		ret = found ? 1 : 0;  	return ret; @@ -1358,6 +1731,9 @@ 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); @@ -1401,10 +1777,12 @@ int find_line_range(int fd, struct line_range *lr)  	Dwarf_Off off = 0, noff;  	size_t cuhl;  	Dwarf_Die *diep; -	Dwarf *dbg; +	Dwarf *dbg = NULL; +	Dwfl *dwfl; +	Dwarf_Addr bias;	/* Currently ignored */  	const char *comp_dir; -	dbg = dwarf_begin(fd, DWARF_C_READ); +	dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);  	if (!dbg) {  		pr_warning("No dwarf info found in the vmlinux - "  			"please rebuild with CONFIG_DEBUG_INFO=y.\n"); @@ -1450,8 +1828,7 @@ int find_line_range(int fd, struct line_range *lr)  	}  	pr_debug("path: %s\n", lr->path); -	dwarf_end(dbg); - +	dwfl_end(dwfl);  	return (ret < 0) ? ret : lf.found;  } |