diff options
Diffstat (limited to 'tools/perf/util/probe-event.c')
| -rw-r--r-- | tools/perf/util/probe-event.c | 1529 | 
1 files changed, 1120 insertions, 409 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 3b6a5297bf1..9a0a1839a37 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -19,7 +19,6 @@   *   */ -#define _GNU_SOURCE  #include <sys/utsname.h>  #include <sys/types.h>  #include <sys/stat.h> @@ -31,24 +30,23 @@  #include <string.h>  #include <stdarg.h>  #include <limits.h> +#include <elf.h> -#undef _GNU_SOURCE  #include "util.h"  #include "event.h" -#include "string.h"  #include "strlist.h"  #include "debug.h"  #include "cache.h"  #include "color.h"  #include "symbol.h"  #include "thread.h" -#include "debugfs.h" -#include "trace-event.h"	/* For __unused */ +#include <api/fs/debugfs.h> +#include "trace-event.h"	/* For __maybe_unused */  #include "probe-event.h"  #include "probe-finder.h" +#include "session.h"  #define MAX_CMDLEN 256 -#define MAX_PROBE_ARGS 128  #define PERFPROBE_GROUP "probe"  bool probe_event_dry_run;	/* Dry run flag */ @@ -72,31 +70,32 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)  }  static char *synthesize_perf_probe_point(struct perf_probe_point *pp); -static struct machine machine; +static void clear_probe_trace_event(struct probe_trace_event *tev); +static struct machine *host_machine;  /* Initialize symbol maps and path of vmlinux/modules */ -static int init_vmlinux(void) +static int init_symbol_maps(bool user_only)  {  	int ret;  	symbol_conf.sort_by_name = true; -	if (symbol_conf.vmlinux_name == NULL) -		symbol_conf.try_vmlinux_path = true; -	else -		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);  	ret = symbol__init();  	if (ret < 0) {  		pr_debug("Failed to init symbol map.\n");  		goto out;  	} -	ret = machine__init(&machine, "", HOST_KERNEL_ID); -	if (ret < 0) -		goto out; +	if (host_machine || user_only)	/* already initialized */ +		return 0; -	if (machine__create_kernel_maps(&machine) < 0) { -		pr_debug("machine__create_kernel_maps "); -		goto out; +	if (symbol_conf.vmlinux_name) +		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); + +	host_machine = machine__new_host(); +	if (!host_machine) { +		pr_debug("machine__new_host() failed.\n"); +		symbol__exit(); +		ret = -1;  	}  out:  	if (ret < 0) @@ -104,106 +103,386 @@ out:  	return ret;  } +static void exit_symbol_maps(void) +{ +	if (host_machine) { +		machine__delete(host_machine); +		host_machine = NULL; +	} +	symbol__exit(); +} +  static struct symbol *__find_kernel_function_by_name(const char *name,  						     struct map **mapp)  { -	return machine__find_kernel_function_by_name(&machine, name, mapp, +	return machine__find_kernel_function_by_name(host_machine, name, mapp,  						     NULL);  } -const char *kernel_get_module_path(const char *module) +static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) +{ +	return machine__find_kernel_function(host_machine, addr, mapp, NULL); +} + +static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) +{ +	/* kmap->ref_reloc_sym should be set if host_machine is initialized */ +	struct kmap *kmap; + +	if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0) +		return NULL; + +	kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]); +	return kmap->ref_reloc_sym; +} + +static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) +{ +	struct ref_reloc_sym *reloc_sym; +	struct symbol *sym; +	struct map *map; + +	/* ref_reloc_sym is just a label. Need a special fix*/ +	reloc_sym = kernel_get_ref_reloc_sym(); +	if (reloc_sym && strcmp(name, reloc_sym->name) == 0) +		return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; +	else { +		sym = __find_kernel_function_by_name(name, &map); +		if (sym) +			return map->unmap_ip(map, sym->start) - +				(reloc) ? 0 : map->reloc; +	} +	return 0; +} + +static struct map *kernel_get_module_map(const char *module) +{ +	struct rb_node *nd; +	struct map_groups *grp = &host_machine->kmaps; + +	/* A file path -- this is an offline module */ +	if (module && strchr(module, '/')) +		return machine__new_module(host_machine, 0, module); + +	if (!module) +		module = "kernel"; + +	for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { +		struct map *pos = rb_entry(nd, struct map, rb_node); +		if (strncmp(pos->dso->short_name + 1, module, +			    pos->dso->short_name_len - 2) == 0) { +			return pos; +		} +	} +	return NULL; +} + +static struct dso *kernel_get_module_dso(const char *module)  {  	struct dso *dso; +	struct map *map; +	const char *vmlinux_name;  	if (module) { -		list_for_each_entry(dso, &machine.kernel_dsos, node) { +		list_for_each_entry(dso, &host_machine->kernel_dsos, node) {  			if (strncmp(dso->short_name + 1, module,  				    dso->short_name_len - 2) == 0)  				goto found;  		}  		pr_debug("Failed to find module %s.\n", module);  		return NULL; +	} + +	map = host_machine->vmlinux_maps[MAP__FUNCTION]; +	dso = map->dso; + +	vmlinux_name = symbol_conf.vmlinux_name; +	if (vmlinux_name) { +		if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) +			return NULL;  	} else { -		dso = machine.vmlinux_maps[MAP__FUNCTION]->dso; -		if (dso__load_vmlinux_path(dso, -			 machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { +		if (dso__load_vmlinux_path(dso, map, NULL) <= 0) {  			pr_debug("Failed to load kernel map.\n");  			return NULL;  		}  	}  found: -	return dso->long_name; +	return dso;  } -#ifdef DWARF_SUPPORT -static int open_vmlinux(const char *module) +const char *kernel_get_module_path(const char *module)  { -	const char *path = kernel_get_module_path(module); -	if (!path) { -		pr_err("Failed to find path of %s module", module ?: "kernel"); -		return -ENOENT; +	struct dso *dso = kernel_get_module_dso(module); +	return (dso) ? dso->long_name : NULL; +} + +static int convert_exec_to_group(const char *exec, char **result) +{ +	char *ptr1, *ptr2, *exec_copy; +	char buf[64]; +	int ret; + +	exec_copy = strdup(exec); +	if (!exec_copy) +		return -ENOMEM; + +	ptr1 = basename(exec_copy); +	if (!ptr1) { +		ret = -EINVAL; +		goto out;  	} -	pr_debug("Try to open %s\n", path); -	return open(path, O_RDONLY); + +	ptr2 = strpbrk(ptr1, "-._"); +	if (ptr2) +		*ptr2 = '\0'; +	ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); +	if (ret < 0) +		goto out; + +	*result = strdup(buf); +	ret = *result ? 0 : -ENOMEM; + +out: +	free(exec_copy); +	return ret; +} + +static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) +{ +	int i; + +	for (i = 0; i < ntevs; i++) +		clear_probe_trace_event(tevs + i); +} + +#ifdef HAVE_DWARF_SUPPORT + +/* Open new debuginfo of given module */ +static struct debuginfo *open_debuginfo(const char *module) +{ +	const char *path = module; + +	if (!module || !strchr(module, '/')) { +		path = kernel_get_module_path(module); +		if (!path) { +			pr_err("Failed to find path of %s module.\n", +			       module ?: "kernel"); +			return NULL; +		} +	} +	return debuginfo__new(path); +} + +static int get_text_start_address(const char *exec, unsigned long *address) +{ +	Elf *elf; +	GElf_Ehdr ehdr; +	GElf_Shdr shdr; +	int fd, ret = -ENOENT; + +	fd = open(exec, O_RDONLY); +	if (fd < 0) +		return -errno; + +	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); +	if (elf == NULL) +		return -EINVAL; + +	if (gelf_getehdr(elf, &ehdr) == NULL) +		goto out; + +	if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) +		goto out; + +	*address = shdr.sh_addr - shdr.sh_offset; +	ret = 0; +out: +	elf_end(elf); +	return ret;  }  /*   * Convert trace point to probe point with debuginfo - * Currently only handles kprobes.   */ -static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, +					    struct perf_probe_point *pp, +					    bool is_kprobe)  { -	struct symbol *sym; -	struct map *map; -	u64 addr; +	struct debuginfo *dinfo = NULL; +	unsigned long stext = 0; +	u64 addr = tp->address;  	int ret = -ENOENT; -	sym = __find_kernel_function_by_name(tp->symbol, &map); -	if (sym) { -		addr = map->unmap_ip(map, sym->start + tp->offset); -		pr_debug("try to find %s+%ld@%llx\n", tp->symbol, -			 tp->offset, addr); -		ret = find_perf_probe_point((unsigned long)addr, pp); +	/* convert the address to dwarf address */ +	if (!is_kprobe) { +		if (!addr) { +			ret = -EINVAL; +			goto error; +		} +		ret = get_text_start_address(tp->module, &stext); +		if (ret < 0) +			goto error; +		addr += stext; +	} else { +		addr = kernel_get_symbol_address_by_name(tp->symbol, false); +		if (addr == 0) +			goto error; +		addr += tp->offset;  	} -	if (ret <= 0) { -		pr_debug("Failed to find corresponding probes from " -			 "debuginfo. Use kprobe event information.\n"); -		pp->function = strdup(tp->symbol); -		if (pp->function == NULL) + +	pr_debug("try to find information at %" PRIx64 " in %s\n", addr, +		 tp->module ? : "kernel"); + +	dinfo = open_debuginfo(tp->module); +	if (dinfo) { +		ret = debuginfo__find_probe_point(dinfo, +						 (unsigned long)addr, pp); +		debuginfo__delete(dinfo); +	} else { +		pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); +		ret = -ENOENT; +	} + +	if (ret > 0) { +		pp->retprobe = tp->retprobe; +		return 0; +	} +error: +	pr_debug("Failed to find corresponding probes from debuginfo.\n"); +	return ret ? : -ENOENT; +} + +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, +					  int ntevs, const char *exec) +{ +	int i, ret = 0; +	unsigned long stext = 0; + +	if (!exec) +		return 0; + +	ret = get_text_start_address(exec, &stext); +	if (ret < 0) +		return ret; + +	for (i = 0; i < ntevs && ret >= 0; i++) { +		/* point.address is the addres of point.symbol + point.offset */ +		tevs[i].point.address -= stext; +		tevs[i].point.module = strdup(exec); +		if (!tevs[i].point.module) { +			ret = -ENOMEM; +			break; +		} +		tevs[i].uprobes = true; +	} + +	return ret; +} + +static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, +					    int ntevs, const char *module) +{ +	int i, ret = 0; +	char *tmp; + +	if (!module) +		return 0; + +	tmp = strrchr(module, '/'); +	if (tmp) { +		/* This is a module path -- get the module name */ +		module = strdup(tmp + 1); +		if (!module)  			return -ENOMEM; -		pp->offset = tp->offset; +		tmp = strchr(module, '.'); +		if (tmp) +			*tmp = '\0'; +		tmp = (char *)module;	/* For free() */  	} -	pp->retprobe = tp->retprobe; +	for (i = 0; i < ntevs; i++) { +		tevs[i].point.module = strdup(module); +		if (!tevs[i].point.module) { +			ret = -ENOMEM; +			break; +		} +	} + +	free(tmp); +	return ret; +} + +/* Post processing the probe events */ +static int post_process_probe_trace_events(struct probe_trace_event *tevs, +					   int ntevs, const char *module, +					   bool uprobe) +{ +	struct ref_reloc_sym *reloc_sym; +	char *tmp; +	int i; + +	if (uprobe) +		return add_exec_to_probe_trace_events(tevs, ntevs, module); + +	/* Note that currently ref_reloc_sym based probe is not for drivers */ +	if (module) +		return add_module_to_probe_trace_events(tevs, ntevs, module); + +	reloc_sym = kernel_get_ref_reloc_sym(); +	if (!reloc_sym) { +		pr_warning("Relocated base symbol is not found!\n"); +		return -EINVAL; +	} + +	for (i = 0; i < ntevs; i++) { +		if (tevs[i].point.address) { +			tmp = strdup(reloc_sym->name); +			if (!tmp) +				return -ENOMEM; +			free(tevs[i].point.symbol); +			tevs[i].point.symbol = tmp; +			tevs[i].point.offset = tevs[i].point.address - +					       reloc_sym->unrelocated_addr; +		} +	}  	return 0;  }  /* Try to find perf_probe_event with debuginfo */  static int try_to_find_probe_trace_events(struct perf_probe_event *pev, -					   struct probe_trace_event **tevs, -					   int max_tevs, const char *module) +					  struct probe_trace_event **tevs, +					  int max_tevs, const char *target)  {  	bool need_dwarf = perf_probe_event_need_dwarf(pev); -	int fd, ntevs; +	struct debuginfo *dinfo; +	int ntevs, ret = 0; + +	dinfo = open_debuginfo(target); -	fd = open_vmlinux(module); -	if (fd < 0) { +	if (!dinfo) {  		if (need_dwarf) {  			pr_warning("Failed to open debuginfo file.\n"); -			return fd; +			return -ENOENT;  		} -		pr_debug("Could not open vmlinux. Try to use symbols.\n"); +		pr_debug("Could not open debuginfo. Try to use symbols.\n");  		return 0;  	} -	/* Searching trace events corresponding to probe event */ -	ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); -	close(fd); +	pr_debug("Try to find probe point from debuginfo.\n"); +	/* Searching trace events corresponding to a probe event */ +	ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); + +	debuginfo__delete(dinfo);  	if (ntevs > 0) {	/* Succeeded to find trace events */ -		pr_debug("find %d probe_trace_events.\n", ntevs); -		return ntevs; +		pr_debug("Found %d probe_trace_events.\n", ntevs); +		ret = post_process_probe_trace_events(*tevs, ntevs, +							target, pev->uprobes); +		if (ret < 0) { +			clear_probe_trace_events(*tevs, ntevs); +			zfree(tevs); +		} +		return ret < 0 ? ret : ntevs;  	}  	if (ntevs == 0)	{	/* No error but failed to find probe point. */ @@ -217,7 +496,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,  		pr_warning("Warning: No dwarf info found in the vmlinux - "  			"please rebuild kernel with CONFIG_DEBUG_INFO=y.\n");  		if (!need_dwarf) { -			pr_debug("Trying to use symbols.\nn"); +			pr_debug("Trying to use symbols.\n");  			return 0;  		}  	} @@ -269,15 +548,13 @@ static int get_real_path(const char *raw_path, const char *comp_dir,  		case EFAULT:  			raw_path = strchr(++raw_path, '/');  			if (!raw_path) { -				free(*new_path); -				*new_path = NULL; +				zfree(new_path);  				return -ENOENT;  			}  			continue;  		default: -			free(*new_path); -			*new_path = NULL; +			zfree(new_path);  			return -errno;  		}  	} @@ -286,72 +563,76 @@ static int get_real_path(const char *raw_path, const char *comp_dir,  #define LINEBUF_SIZE 256  #define NR_ADDITIONAL_LINES 2 -static int show_one_line(FILE *fp, int l, bool skip, bool show_num) +static int __show_one_line(FILE *fp, int l, bool skip, bool show_num)  {  	char buf[LINEBUF_SIZE]; -	const char *color = PERF_COLOR_BLUE; - -	if (fgets(buf, LINEBUF_SIZE, fp) == NULL) -		goto error; -	if (!skip) { -		if (show_num) -			fprintf(stdout, "%7d  %s", l, buf); -		else -			color_fprintf(stdout, color, "         %s", buf); -	} +	const char *color = show_num ? "" : PERF_COLOR_BLUE; +	const char *prefix = NULL; -	while (strlen(buf) == LINEBUF_SIZE - 1 && -	       buf[LINEBUF_SIZE - 2] != '\n') { +	do {  		if (fgets(buf, LINEBUF_SIZE, fp) == NULL)  			goto error; -		if (!skip) { -			if (show_num) -				fprintf(stdout, "%s", buf); -			else -				color_fprintf(stdout, color, "%s", buf); +		if (skip) +			continue; +		if (!prefix) { +			prefix = show_num ? "%7d  " : "         "; +			color_fprintf(stdout, color, prefix, l);  		} -	} +		color_fprintf(stdout, color, "%s", buf); -	return 0; +	} while (strchr(buf, '\n') == NULL); + +	return 1;  error: -	if (feof(fp)) -		pr_warning("Source file is shorter than expected.\n"); -	else +	if (ferror(fp)) {  		pr_warning("File read error: %s\n", strerror(errno)); +		return -1; +	} +	return 0; +} -	return -1; +static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) +{ +	int rv = __show_one_line(fp, l, skip, show_num); +	if (rv == 0) { +		pr_warning("Source file is shorter than expected.\n"); +		rv = -1; +	} +	return rv;  } +#define show_one_line_with_num(f,l)	_show_one_line(f,l,false,true) +#define show_one_line(f,l)		_show_one_line(f,l,false,false) +#define skip_one_line(f,l)		_show_one_line(f,l,true,false) +#define show_one_line_or_eof(f,l)	__show_one_line(f,l,false,false) +  /*   * Show line-range always requires debuginfo to find source file and   * line number.   */ -int show_line_range(struct line_range *lr, const char *module) +static int __show_line_range(struct line_range *lr, const char *module)  {  	int l = 1; -	struct line_node *ln; +	struct int_node *ln; +	struct debuginfo *dinfo;  	FILE *fp; -	int fd, ret; +	int ret;  	char *tmp;  	/* Search a line range */ -	ret = init_vmlinux(); -	if (ret < 0) -		return ret; - -	fd = open_vmlinux(module); -	if (fd < 0) { +	dinfo = open_debuginfo(module); +	if (!dinfo) {  		pr_warning("Failed to open debuginfo file.\n"); -		return fd; +		return -ENOENT;  	} -	ret = find_line_range(fd, lr); -	close(fd); -	if (ret == 0) { +	ret = debuginfo__find_line_range(dinfo, lr); +	debuginfo__delete(dinfo); +	if (ret == 0 || ret == -ENOENT) {  		pr_warning("Specified source line is not found.\n");  		return -ENOENT;  	} else if (ret < 0) { -		pr_warning("Debuginfo analysis failed. (%d)\n", ret); +		pr_warning("Debuginfo analysis failed.\n");  		return ret;  	} @@ -360,17 +641,17 @@ int show_line_range(struct line_range *lr, const char *module)  	ret = get_real_path(tmp, lr->comp_dir, &lr->path);  	free(tmp);	/* Free old path */  	if (ret < 0) { -		pr_warning("Failed to find source file. (%d)\n", ret); +		pr_warning("Failed to find source file path.\n");  		return ret;  	}  	setup_pager();  	if (lr->function) -		fprintf(stdout, "<%s:%d>\n", lr->function, +		fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path,  			lr->start - lr->offset);  	else -		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); +		fprintf(stdout, "<%s:%d>\n", lr->path, lr->start);  	fp = fopen(lr->path, "r");  	if (fp == NULL) { @@ -379,194 +660,288 @@ int show_line_range(struct line_range *lr, const char *module)  		return -errno;  	}  	/* Skip to starting line number */ -	while (l < lr->start && ret >= 0) -		ret = show_one_line(fp, l++, true, false); -	if (ret < 0) -		goto end; +	while (l < lr->start) { +		ret = skip_one_line(fp, l++); +		if (ret < 0) +			goto end; +	} -	list_for_each_entry(ln, &lr->line_list, list) { -		while (ln->line > l && ret >= 0) -			ret = show_one_line(fp, (l++) - lr->offset, -					    false, false); -		if (ret >= 0) -			ret = show_one_line(fp, (l++) - lr->offset, -					    false, true); +	intlist__for_each(ln, lr->line_list) { +		for (; ln->i > l; l++) { +			ret = show_one_line(fp, l - lr->offset); +			if (ret < 0) +				goto end; +		} +		ret = show_one_line_with_num(fp, l++ - lr->offset);  		if (ret < 0)  			goto end;  	}  	if (lr->end == INT_MAX)  		lr->end = l + NR_ADDITIONAL_LINES; -	while (l <= lr->end && !feof(fp) && ret >= 0) -		ret = show_one_line(fp, (l++) - lr->offset, false, false); +	while (l <= lr->end) { +		ret = show_one_line_or_eof(fp, l++ - lr->offset); +		if (ret <= 0) +			break; +	}  end:  	fclose(fp);  	return ret;  } -static int show_available_vars_at(int fd, struct perf_probe_event *pev, -				  int max_vls, bool externs) +int show_line_range(struct line_range *lr, const char *module) +{ +	int ret; + +	ret = init_symbol_maps(false); +	if (ret < 0) +		return ret; +	ret = __show_line_range(lr, module); +	exit_symbol_maps(); + +	return ret; +} + +static int show_available_vars_at(struct debuginfo *dinfo, +				  struct perf_probe_event *pev, +				  int max_vls, struct strfilter *_filter, +				  bool externs)  {  	char *buf; -	int ret, i; +	int ret, i, nvars;  	struct str_node *node;  	struct variable_list *vls = NULL, *vl; +	const char *var;  	buf = synthesize_perf_probe_point(&pev->point);  	if (!buf)  		return -EINVAL;  	pr_debug("Searching variables at %s\n", buf); -	ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); -	if (ret > 0) { -		/* Some variables were found */ -		fprintf(stdout, "Available variables at %s\n", buf); -		for (i = 0; i < ret; i++) { -			vl = &vls[i]; -			/* -			 * A probe point might be converted to -			 * several trace points. -			 */ -			fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, -				vl->point.offset); -			free(vl->point.symbol); -			if (vl->vars) { -				strlist__for_each(node, vl->vars) +	ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, +						max_vls, externs); +	if (ret <= 0) { +		if (ret == 0 || ret == -ENOENT) { +			pr_err("Failed to find the address of %s\n", buf); +			ret = -ENOENT; +		} else +			pr_warning("Debuginfo analysis failed.\n"); +		goto end; +	} + +	/* Some variables are found */ +	fprintf(stdout, "Available variables at %s\n", buf); +	for (i = 0; i < ret; i++) { +		vl = &vls[i]; +		/* +		 * A probe point might be converted to +		 * several trace points. +		 */ +		fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, +			vl->point.offset); +		zfree(&vl->point.symbol); +		nvars = 0; +		if (vl->vars) { +			strlist__for_each(node, vl->vars) { +				var = strchr(node->s, '\t') + 1; +				if (strfilter__compare(_filter, var)) {  					fprintf(stdout, "\t\t%s\n", node->s); -				strlist__delete(vl->vars); -			} else -				fprintf(stdout, "(No variables)\n"); +					nvars++; +				} +			} +			strlist__delete(vl->vars);  		} -		free(vls); -	} else -		pr_err("Failed to find variables at %s (%d)\n", buf, ret); - +		if (nvars == 0) +			fprintf(stdout, "\t\t(No matched variables)\n"); +	} +	free(vls); +end:  	free(buf);  	return ret;  }  /* Show available variables on given probe point */  int show_available_vars(struct perf_probe_event *pevs, int npevs, -			int max_vls, const char *module, bool externs) +			int max_vls, const char *module, +			struct strfilter *_filter, bool externs)  { -	int i, fd, ret = 0; +	int i, ret = 0; +	struct debuginfo *dinfo; -	ret = init_vmlinux(); +	ret = init_symbol_maps(false);  	if (ret < 0)  		return ret; -	fd = open_vmlinux(module); -	if (fd < 0) { +	dinfo = open_debuginfo(module); +	if (!dinfo) {  		pr_warning("Failed to open debuginfo file.\n"); -		return fd; +		ret = -ENOENT; +		goto out;  	}  	setup_pager();  	for (i = 0; i < npevs && ret >= 0; i++) -		ret = show_available_vars_at(fd, &pevs[i], max_vls, externs); +		ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, +					     externs); -	close(fd); +	debuginfo__delete(dinfo); +out: +	exit_symbol_maps();  	return ret;  } -#else	/* !DWARF_SUPPORT */ +#else	/* !HAVE_DWARF_SUPPORT */ -static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, -					struct perf_probe_point *pp) +static int +find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, +				 struct perf_probe_point *pp __maybe_unused, +				 bool is_kprobe __maybe_unused)  { -	struct symbol *sym; - -	sym = __find_kernel_function_by_name(tp->symbol, NULL); -	if (!sym) { -		pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); -		return -ENOENT; -	} -	pp->function = strdup(tp->symbol); -	if (pp->function == NULL) -		return -ENOMEM; -	pp->offset = tp->offset; -	pp->retprobe = tp->retprobe; - -	return 0; +	return -ENOSYS;  }  static int try_to_find_probe_trace_events(struct perf_probe_event *pev, -				struct probe_trace_event **tevs __unused, -				int max_tevs __unused, const char *mod __unused) +				struct probe_trace_event **tevs __maybe_unused, +				int max_tevs __maybe_unused, +				const char *target __maybe_unused)  {  	if (perf_probe_event_need_dwarf(pev)) {  		pr_warning("Debuginfo-analysis is not supported.\n");  		return -ENOSYS;  	} +  	return 0;  } -int show_line_range(struct line_range *lr __unused, const char *module __unused) +int show_line_range(struct line_range *lr __maybe_unused, +		    const char *module __maybe_unused)  {  	pr_warning("Debuginfo-analysis is not supported.\n");  	return -ENOSYS;  } -int show_available_vars(struct perf_probe_event *pevs __unused, -			int npevs __unused, int max_vls __unused, -			const char *module __unused, bool externs __unused) +int show_available_vars(struct perf_probe_event *pevs __maybe_unused, +			int npevs __maybe_unused, int max_vls __maybe_unused, +			const char *module __maybe_unused, +			struct strfilter *filter __maybe_unused, +			bool externs __maybe_unused)  {  	pr_warning("Debuginfo-analysis is not supported.\n");  	return -ENOSYS;  }  #endif +void line_range__clear(struct line_range *lr) +{ +	free(lr->function); +	free(lr->file); +	free(lr->path); +	free(lr->comp_dir); +	intlist__delete(lr->line_list); +	memset(lr, 0, sizeof(*lr)); +} + +int line_range__init(struct line_range *lr) +{ +	memset(lr, 0, sizeof(*lr)); +	lr->line_list = intlist__new(NULL); +	if (!lr->line_list) +		return -ENOMEM; +	else +		return 0; +} + +static int parse_line_num(char **ptr, int *val, const char *what) +{ +	const char *start = *ptr; + +	errno = 0; +	*val = strtol(*ptr, ptr, 0); +	if (errno || *ptr == start) { +		semantic_error("'%s' is not a valid number.\n", what); +		return -EINVAL; +	} +	return 0; +} + +/* + * Stuff 'lr' according to the line range described by 'arg'. + * The line range syntax is described by: + * + *         SRC[:SLN[+NUM|-ELN]] + *         FNC[@SRC][:SLN[+NUM|-ELN]] + */  int parse_line_range_desc(const char *arg, struct line_range *lr)  { -	const char *ptr; -	char *tmp; -	/* -	 * <Syntax> -	 * SRC:SLN[+NUM|-ELN] -	 * FUNC[:SLN[+NUM|-ELN]] -	 */ -	ptr = strchr(arg, ':'); -	if (ptr) { -		lr->start = (int)strtoul(ptr + 1, &tmp, 0); -		if (*tmp == '+') { -			lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); -			lr->end--;	/* -					 * Adjust the number of lines here. -					 * If the number of lines == 1, the -					 * the end of line should be equal to -					 * the start of line. -					 */ -		} else if (*tmp == '-') -			lr->end = (int)strtoul(tmp + 1, &tmp, 0); -		else -			lr->end = INT_MAX; +	char *range, *file, *name = strdup(arg); +	int err; + +	if (!name) +		return -ENOMEM; + +	lr->start = 0; +	lr->end = INT_MAX; + +	range = strchr(name, ':'); +	if (range) { +		*range++ = '\0'; + +		err = parse_line_num(&range, &lr->start, "start line"); +		if (err) +			goto err; + +		if (*range == '+' || *range == '-') { +			const char c = *range++; + +			err = parse_line_num(&range, &lr->end, "end line"); +			if (err) +				goto err; + +			if (c == '+') { +				lr->end += lr->start; +				/* +				 * Adjust the number of lines here. +				 * If the number of lines == 1, the +				 * the end of line should be equal to +				 * the start of line. +				 */ +				lr->end--; +			} +		} +  		pr_debug("Line range is %d to %d\n", lr->start, lr->end); + +		err = -EINVAL;  		if (lr->start > lr->end) {  			semantic_error("Start line must be smaller"  				       " than end line.\n"); -			return -EINVAL; +			goto err;  		} -		if (*tmp != '\0') { -			semantic_error("Tailing with invalid character '%d'.\n", -				       *tmp); -			return -EINVAL; +		if (*range != '\0') { +			semantic_error("Tailing with invalid str '%s'.\n", range); +			goto err;  		} -		tmp = strndup(arg, (ptr - arg)); -	} else { -		tmp = strdup(arg); -		lr->end = INT_MAX;  	} -	if (tmp == NULL) -		return -ENOMEM; - -	if (strchr(tmp, '.')) -		lr->file = tmp; +	file = strchr(name, '@'); +	if (file) { +		*file = '\0'; +		lr->file = strdup(++file); +		if (lr->file == NULL) { +			err = -ENOMEM; +			goto err; +		} +		lr->function = name; +	} else if (strchr(name, '.')) +		lr->file = name;  	else -		lr->function = tmp; +		lr->function = name;  	return 0; +err: +	free(name); +	return err;  }  /* Check the name is good for event/group */ @@ -690,39 +1065,40 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)  	/* Exclusion check */  	if (pp->lazy_line && pp->line) { -		semantic_error("Lazy pattern can't be used with line number."); +		semantic_error("Lazy pattern can't be used with" +			       " line number.\n");  		return -EINVAL;  	}  	if (pp->lazy_line && pp->offset) { -		semantic_error("Lazy pattern can't be used with offset."); +		semantic_error("Lazy pattern can't be used with offset.\n");  		return -EINVAL;  	}  	if (pp->line && pp->offset) { -		semantic_error("Offset can't be used with line number."); +		semantic_error("Offset can't be used with line number.\n");  		return -EINVAL;  	}  	if (!pp->line && !pp->lazy_line && pp->file && !pp->function) {  		semantic_error("File always requires line number or " -			       "lazy pattern."); +			       "lazy pattern.\n");  		return -EINVAL;  	}  	if (pp->offset && !pp->function) { -		semantic_error("Offset requires an entry function."); +		semantic_error("Offset requires an entry function.\n");  		return -EINVAL;  	}  	if (pp->retprobe && !pp->function) { -		semantic_error("Return probe requires an entry function."); +		semantic_error("Return probe requires an entry function.\n");  		return -EINVAL;  	}  	if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {  		semantic_error("Offset/Line/Lazy pattern can't be used with " -			       "return probe."); +			       "return probe.\n");  		return -EINVAL;  	} @@ -892,11 +1268,12 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)  /* Parse probe_events event into struct probe_point */  static int parse_probe_trace_command(const char *cmd, -					struct probe_trace_event *tev) +				     struct probe_trace_event *tev)  {  	struct probe_trace_point *tp = &tev->point;  	char pr;  	char *p; +	char *argv0_str = NULL, *fmt, *fmt1_str, *fmt2_str, *fmt3_str;  	int ret, i, argc;  	char **argv; @@ -913,23 +1290,54 @@ static int parse_probe_trace_command(const char *cmd,  	}  	/* Scan event and group name. */ -	ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", -		     &pr, (float *)(void *)&tev->group, -		     (float *)(void *)&tev->event); -	if (ret != 3) { +	argv0_str = strdup(argv[0]); +	if (argv0_str == NULL) { +		ret = -ENOMEM; +		goto out; +	} +	fmt1_str = strtok_r(argv0_str, ":", &fmt); +	fmt2_str = strtok_r(NULL, "/", &fmt); +	fmt3_str = strtok_r(NULL, " \t", &fmt); +	if (fmt1_str == NULL || strlen(fmt1_str) != 1 || fmt2_str == NULL +	    || fmt3_str == NULL) {  		semantic_error("Failed to parse event name: %s\n", argv[0]);  		ret = -EINVAL;  		goto out;  	} +	pr = fmt1_str[0]; +	tev->group = strdup(fmt2_str); +	tev->event = strdup(fmt3_str); +	if (tev->group == NULL || tev->event == NULL) { +		ret = -ENOMEM; +		goto out; +	}  	pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr);  	tp->retprobe = (pr == 'r'); -	/* Scan function name and offset */ -	ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, -		     &tp->offset); -	if (ret == 1) -		tp->offset = 0; +	/* Scan module name(if there), function name and offset */ +	p = strchr(argv[1], ':'); +	if (p) { +		tp->module = strndup(argv[1], p - argv[1]); +		p++; +	} else +		p = argv[1]; +	fmt1_str = strtok_r(p, "+", &fmt); +	if (fmt1_str[0] == '0')	/* only the address started with 0x */ +		tp->address = strtoul(fmt1_str, NULL, 0); +	else { +		/* Only the symbol-based probe has offset */ +		tp->symbol = strdup(fmt1_str); +		if (tp->symbol == NULL) { +			ret = -ENOMEM; +			goto out; +		} +		fmt2_str = strtok_r(NULL, "", &fmt); +		if (fmt2_str == NULL) +			tp->offset = 0; +		else +			tp->offset = strtoul(fmt2_str, NULL, 10); +	}  	tev->nargs = argc - 2;  	tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); @@ -953,6 +1361,7 @@ static int parse_probe_trace_command(const char *cmd,  	}  	ret = 0;  out: +	free(argv0_str);  	argv_free(argv);  	return ret;  } @@ -996,7 +1405,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)  	return tmp - buf;  error: -	pr_debug("Failed to synthesize perf probe argument: %s", +	pr_debug("Failed to synthesize perf probe argument: %s\n",  		 strerror(-ret));  	return ret;  } @@ -1024,13 +1433,13 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)  			goto error;  	}  	if (pp->file) { -		len = strlen(pp->file) - 31; -		if (len < 0) -			len = 0; -		tmp = strchr(pp->file + len, '/'); -		if (!tmp) -			tmp = pp->file + len; -		ret = e_snprintf(file, 32, "@%s", tmp + 1); +		tmp = pp->file; +		len = strlen(tmp); +		if (len > 30) { +			tmp = strchr(pp->file + len - 30, '/'); +			tmp = tmp ? tmp + 1 : pp->file + len - 30; +		} +		ret = e_snprintf(file, 32, "@%s", tmp);  		if (ret <= 0)  			goto error;  	} @@ -1046,10 +1455,9 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)  	return buf;  error: -	pr_debug("Failed to synthesize perf probe point: %s", +	pr_debug("Failed to synthesize perf probe point: %s\n",  		 strerror(-ret)); -	if (buf) -		free(buf); +	free(buf);  	return NULL;  } @@ -1171,13 +1579,28 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)  	if (buf == NULL)  		return NULL; -	len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", -			 tp->retprobe ? 'r' : 'p', -			 tev->group, tev->event, -			 tp->symbol, tp->offset); +	len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', +			 tev->group, tev->event);  	if (len <= 0)  		goto error; +	/* Uprobes must have tp->address and tp->module */ +	if (tev->uprobes && (!tp->address || !tp->module)) +		goto error; + +	/* Use the tp->address for uprobes */ +	if (tev->uprobes) +		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", +				 tp->module, tp->address); +	else +		ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", +				 tp->module ?: "", tp->module ? ":" : "", +				 tp->symbol, tp->offset); + +	if (ret <= 0) +		goto error; +	len += ret; +  	for (i = 0; i < tev->nargs; i++) {  		ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,  						  MAX_CMDLEN - len); @@ -1192,8 +1615,81 @@ error:  	return NULL;  } +static int find_perf_probe_point_from_map(struct probe_trace_point *tp, +					  struct perf_probe_point *pp, +					  bool is_kprobe) +{ +	struct symbol *sym = NULL; +	struct map *map; +	u64 addr; +	int ret = -ENOENT; + +	if (!is_kprobe) { +		map = dso__new_map(tp->module); +		if (!map) +			goto out; +		addr = tp->address; +		sym = map__find_symbol(map, addr, NULL); +	} else { +		addr = kernel_get_symbol_address_by_name(tp->symbol, true); +		if (addr) { +			addr += tp->offset; +			sym = __find_kernel_function(addr, &map); +		} +	} +	if (!sym) +		goto out; + +	pp->retprobe = tp->retprobe; +	pp->offset = addr - map->unmap_ip(map, sym->start); +	pp->function = strdup(sym->name); +	ret = pp->function ? 0 : -ENOMEM; + +out: +	if (map && !is_kprobe) { +		dso__delete(map->dso); +		map__delete(map); +	} + +	return ret; +} + +static int convert_to_perf_probe_point(struct probe_trace_point *tp, +					struct perf_probe_point *pp, +					bool is_kprobe) +{ +	char buf[128]; +	int ret; + +	ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe); +	if (!ret) +		return 0; +	ret = find_perf_probe_point_from_map(tp, pp, is_kprobe); +	if (!ret) +		return 0; + +	pr_debug("Failed to find probe point from both of dwarf and map.\n"); + +	if (tp->symbol) { +		pp->function = strdup(tp->symbol); +		pp->offset = tp->offset; +	} else if (!tp->module && !is_kprobe) { +		ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); +		if (ret < 0) +			return ret; +		pp->function = strdup(buf); +		pp->offset = 0; +	} +	if (pp->function == NULL) +		return -ENOMEM; + +	pp->retprobe = tp->retprobe; + +	return 0; +} +  static int convert_to_perf_probe_event(struct probe_trace_event *tev, -				       struct perf_probe_event *pev) +			       struct perf_probe_event *pev, bool is_kprobe)  {  	char buf[64] = "";  	int i, ret; @@ -1205,7 +1701,7 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,  		return -ENOMEM;  	/* Convert trace_point to probe_point */ -	ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); +	ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe);  	if (ret < 0)  		return ret; @@ -1238,34 +1734,25 @@ void clear_perf_probe_event(struct perf_probe_event *pev)  	struct perf_probe_arg_field *field, *next;  	int i; -	if (pev->event) -		free(pev->event); -	if (pev->group) -		free(pev->group); -	if (pp->file) -		free(pp->file); -	if (pp->function) -		free(pp->function); -	if (pp->lazy_line) -		free(pp->lazy_line); +	free(pev->event); +	free(pev->group); +	free(pp->file); +	free(pp->function); +	free(pp->lazy_line); +  	for (i = 0; i < pev->nargs; i++) { -		if (pev->args[i].name) -			free(pev->args[i].name); -		if (pev->args[i].var) -			free(pev->args[i].var); -		if (pev->args[i].type) -			free(pev->args[i].type); +		free(pev->args[i].name); +		free(pev->args[i].var); +		free(pev->args[i].type);  		field = pev->args[i].field;  		while (field) {  			next = field->next; -			if (field->name) -				free(field->name); +			zfree(&field->name);  			free(field);  			field = next;  		}  	} -	if (pev->args) -		free(pev->args); +	free(pev->args);  	memset(pev, 0, sizeof(*pev));  } @@ -1274,19 +1761,14 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)  	struct probe_trace_arg_ref *ref, *next;  	int i; -	if (tev->event) -		free(tev->event); -	if (tev->group) -		free(tev->group); -	if (tev->point.symbol) -		free(tev->point.symbol); +	free(tev->event); +	free(tev->group); +	free(tev->point.symbol); +	free(tev->point.module);  	for (i = 0; i < tev->nargs; i++) { -		if (tev->args[i].name) -			free(tev->args[i].name); -		if (tev->args[i].value) -			free(tev->args[i].value); -		if (tev->args[i].type) -			free(tev->args[i].type); +		free(tev->args[i].name); +		free(tev->args[i].value); +		free(tev->args[i].type);  		ref = tev->args[i].ref;  		while (ref) {  			next = ref->next; @@ -1294,12 +1776,30 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)  			ref = next;  		}  	} -	if (tev->args) -		free(tev->args); +	free(tev->args);  	memset(tev, 0, sizeof(*tev));  } -static int open_kprobe_events(bool readwrite) +static void print_warn_msg(const char *file, bool is_kprobe) +{ + +	if (errno == ENOENT) { +		const char *config; + +		if (!is_kprobe) +			config = "CONFIG_UPROBE_EVENTS"; +		else +			config = "CONFIG_KPROBE_EVENTS"; + +		pr_warning("%s file does not exist - please rebuild kernel" +				" with %s.\n", file, config); +	} else +		pr_warning("Failed to open %s file: %s\n", file, +				strerror(errno)); +} + +static int open_probe_events(const char *trace_file, bool readwrite, +				bool is_kprobe)  {  	char buf[PATH_MAX];  	const char *__debugfs; @@ -1311,27 +1811,31 @@ static int open_kprobe_events(bool readwrite)  		return -ENOENT;  	} -	ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); +	ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file);  	if (ret >= 0) {  		pr_debug("Opening %s write=%d\n", buf, readwrite);  		if (readwrite && !probe_event_dry_run)  			ret = open(buf, O_RDWR, O_APPEND);  		else  			ret = open(buf, O_RDONLY, 0); -	} -	if (ret < 0) { -		if (errno == ENOENT) -			pr_warning("kprobe_events file does not exist - please" -				 " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); -		else -			pr_warning("Failed to open kprobe_events file: %s\n", -				   strerror(errno)); +		if (ret < 0) +			print_warn_msg(buf, is_kprobe);  	}  	return ret;  } -/* Get raw string list of current kprobe_events */ +static int open_kprobe_events(bool readwrite) +{ +	return open_probe_events("tracing/kprobe_events", readwrite, true); +} + +static int open_uprobe_events(bool readwrite) +{ +	return open_probe_events("tracing/uprobe_events", readwrite, false); +} + +/* Get raw string list of current kprobe_events  or uprobe_events */  static struct strlist *get_probe_trace_command_rawlist(int fd)  {  	int ret, idx; @@ -1364,7 +1868,8 @@ static struct strlist *get_probe_trace_command_rawlist(int fd)  }  /* Show an event */ -static int show_perf_probe_event(struct perf_probe_event *pev) +static int show_perf_probe_event(struct perf_probe_event *pev, +				 const char *module)  {  	int i, ret;  	char buf[128]; @@ -1380,6 +1885,8 @@ static int show_perf_probe_event(struct perf_probe_event *pev)  		return ret;  	printf("  %-20s (on %s", buf, place); +	if (module) +		printf(" in %s", module);  	if (pev->nargs > 0) {  		printf(" with"); @@ -1396,38 +1903,29 @@ static int show_perf_probe_event(struct perf_probe_event *pev)  	return ret;  } -/* List up current perf-probe events */ -int show_perf_probe_events(void) +static int __show_perf_probe_events(int fd, bool is_kprobe)  { -	int fd, ret; +	int ret = 0;  	struct probe_trace_event tev;  	struct perf_probe_event pev;  	struct strlist *rawlist;  	struct str_node *ent; -	setup_pager(); -	ret = init_vmlinux(); -	if (ret < 0) -		return ret; -  	memset(&tev, 0, sizeof(tev));  	memset(&pev, 0, sizeof(pev)); -	fd = open_kprobe_events(false); -	if (fd < 0) -		return fd; -  	rawlist = get_probe_trace_command_rawlist(fd); -	close(fd);  	if (!rawlist)  		return -ENOENT;  	strlist__for_each(ent, rawlist) {  		ret = parse_probe_trace_command(ent->s, &tev);  		if (ret >= 0) { -			ret = convert_to_perf_probe_event(&tev, &pev); +			ret = convert_to_perf_probe_event(&tev, &pev, +								is_kprobe);  			if (ret >= 0) -				ret = show_perf_probe_event(&pev); +				ret = show_perf_probe_event(&pev, +							    tev.point.module);  		}  		clear_perf_probe_event(&pev);  		clear_probe_trace_event(&tev); @@ -1439,6 +1937,34 @@ int show_perf_probe_events(void)  	return ret;  } +/* List up current perf-probe events */ +int show_perf_probe_events(void) +{ +	int fd, ret; + +	setup_pager(); +	fd = open_kprobe_events(false); + +	if (fd < 0) +		return fd; + +	ret = init_symbol_maps(false); +	if (ret < 0) +		return ret; + +	ret = __show_perf_probe_events(fd, true); +	close(fd); + +	fd = open_uprobe_events(false); +	if (fd >= 0) { +		ret = __show_perf_probe_events(fd, false); +		close(fd); +	} + +	exit_symbol_maps(); +	return ret; +} +  /* Get current perf-probe event names */  static struct strlist *get_probe_trace_event_names(int fd, bool include_group)  { @@ -1544,7 +2070,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  	const char *event, *group;  	struct strlist *namelist; -	fd = open_kprobe_events(true); +	if (pev->uprobes) +		fd = open_uprobe_events(true); +	else +		fd = open_kprobe_events(true); +  	if (fd < 0)  		return fd;  	/* Get current event names */ @@ -1555,7 +2085,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  	}  	ret = 0; -	printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); +	printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":");  	for (i = 0; i < ntevs; i++) {  		tev = &tevs[i];  		if (pev->event) @@ -1594,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  		group = pev->group;  		pev->event = tev->event;  		pev->group = tev->group; -		show_perf_probe_event(pev); +		show_perf_probe_event(pev, tev->point.module);  		/* Trick here - restore current event/group */  		pev->event = (char *)event;  		pev->group = (char *)group; @@ -1610,7 +2140,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  	if (ret >= 0) {  		/* Show how to use the event. */ -		printf("\nYou can now use it on all perf tools, such as:\n\n"); +		printf("\nYou can now use it in all perf tools, such as:\n\n");  		printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,  			 tev->event);  	} @@ -1620,78 +2150,175 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,  	return ret;  } -static int convert_to_probe_trace_events(struct perf_probe_event *pev, -					  struct probe_trace_event **tevs, -					  int max_tevs, const char *module) +static char *looking_function_name; +static int num_matched_functions; + +static int probe_function_filter(struct map *map __maybe_unused, +				      struct symbol *sym) +{ +	if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && +	    strcmp(looking_function_name, sym->name) == 0) { +		num_matched_functions++; +		return 0; +	} +	return 1; +} + +#define strdup_or_goto(str, label)	\ +	({ char *__p = strdup(str); if (!__p) goto label; __p; }) + +/* + * Find probe function addresses from map. + * Return an error or the number of found probe_trace_event + */ +static int find_probe_trace_events_from_map(struct perf_probe_event *pev, +					    struct probe_trace_event **tevs, +					    int max_tevs, const char *target)  { +	struct map *map = NULL; +	struct kmap *kmap = NULL; +	struct ref_reloc_sym *reloc_sym = NULL;  	struct symbol *sym; -	int ret = 0, i; +	struct rb_node *nd;  	struct probe_trace_event *tev; +	struct perf_probe_point *pp = &pev->point; +	struct probe_trace_point *tp; +	int ret, i; -	/* Convert perf_probe_event with debuginfo */ -	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); -	if (ret != 0) -		return ret; +	/* Init maps of given executable or kernel */ +	if (pev->uprobes) +		map = dso__new_map(target); +	else +		map = kernel_get_module_map(target); +	if (!map) { +		ret = -EINVAL; +		goto out; +	} -	/* Allocate trace event buffer */ -	tev = *tevs = zalloc(sizeof(struct probe_trace_event)); -	if (tev == NULL) -		return -ENOMEM; +	/* +	 * Load matched symbols: Since the different local symbols may have +	 * same name but different addresses, this lists all the symbols. +	 */ +	num_matched_functions = 0; +	looking_function_name = pp->function; +	ret = map__load(map, probe_function_filter); +	if (ret || num_matched_functions == 0) { +		pr_err("Failed to find symbol %s in %s\n", pp->function, +			target ? : "kernel"); +		ret = -ENOENT; +		goto out; +	} else if (num_matched_functions > max_tevs) { +		pr_err("Too many functions matched in %s\n", +			target ? : "kernel"); +		ret = -E2BIG; +		goto out; +	} + +	if (!pev->uprobes) { +		kmap = map__kmap(map); +		reloc_sym = kmap->ref_reloc_sym; +		if (!reloc_sym) { +			pr_warning("Relocated base symbol is not found!\n"); +			ret = -EINVAL; +			goto out; +		} +	} -	/* Copy parameters */ -	tev->point.symbol = strdup(pev->point.function); -	if (tev->point.symbol == NULL) { +	/* Setup result trace-probe-events */ +	*tevs = zalloc(sizeof(*tev) * num_matched_functions); +	if (!*tevs) {  		ret = -ENOMEM; -		goto error; +		goto out;  	} -	tev->point.offset = pev->point.offset; -	tev->point.retprobe = pev->point.retprobe; -	tev->nargs = pev->nargs; -	if (tev->nargs) { -		tev->args = zalloc(sizeof(struct probe_trace_arg) -				   * tev->nargs); -		if (tev->args == NULL) { -			ret = -ENOMEM; -			goto error; + +	ret = 0; +	map__for_each_symbol(map, sym, nd) { +		tev = (*tevs) + ret; +		tp = &tev->point; +		if (ret == num_matched_functions) { +			pr_warning("Too many symbols are listed. Skip it.\n"); +			break; +		} +		ret++; + +		if (pp->offset > sym->end - sym->start) { +			pr_warning("Offset %ld is bigger than the size of %s\n", +				   pp->offset, sym->name); +			ret = -ENOENT; +			goto err_out; +		} +		/* Add one probe point */ +		tp->address = map->unmap_ip(map, sym->start) + pp->offset; +		if (reloc_sym) { +			tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out); +			tp->offset = tp->address - reloc_sym->addr; +		} else { +			tp->symbol = strdup_or_goto(sym->name, nomem_out); +			tp->offset = pp->offset; +		} +		tp->retprobe = pp->retprobe; +		if (target) +			tev->point.module = strdup_or_goto(target, nomem_out); +		tev->uprobes = pev->uprobes; +		tev->nargs = pev->nargs; +		if (tev->nargs) { +			tev->args = zalloc(sizeof(struct probe_trace_arg) * +					   tev->nargs); +			if (tev->args == NULL) +				goto nomem_out;  		}  		for (i = 0; i < tev->nargs; i++) { -			if (pev->args[i].name) { -				tev->args[i].name = strdup(pev->args[i].name); -				if (tev->args[i].name == NULL) { -					ret = -ENOMEM; -					goto error; -				} -			} -			tev->args[i].value = strdup(pev->args[i].var); -			if (tev->args[i].value == NULL) { -				ret = -ENOMEM; -				goto error; -			} -			if (pev->args[i].type) { -				tev->args[i].type = strdup(pev->args[i].type); -				if (tev->args[i].type == NULL) { -					ret = -ENOMEM; -					goto error; -				} -			} +			if (pev->args[i].name) +				tev->args[i].name = +					strdup_or_goto(pev->args[i].name, +							nomem_out); + +			tev->args[i].value = strdup_or_goto(pev->args[i].var, +							    nomem_out); +			if (pev->args[i].type) +				tev->args[i].type = +					strdup_or_goto(pev->args[i].type, +							nomem_out);  		}  	} -	/* Currently just checking function name from symbol map */ -	sym = __find_kernel_function_by_name(tev->point.symbol, NULL); -	if (!sym) { -		pr_warning("Kernel symbol \'%s\' not found.\n", -			   tev->point.symbol); -		ret = -ENOENT; -		goto error; +out: +	if (map && pev->uprobes) { +		/* Only when using uprobe(exec) map needs to be released */ +		dso__delete(map->dso); +		map__delete(map);  	} - -	return 1; -error: -	clear_probe_trace_event(tev); -	free(tev); -	*tevs = NULL;  	return ret; + +nomem_out: +	ret = -ENOMEM; +err_out: +	clear_probe_trace_events(*tevs, num_matched_functions); +	zfree(tevs); +	goto out; +} + +static int convert_to_probe_trace_events(struct perf_probe_event *pev, +					  struct probe_trace_event **tevs, +					  int max_tevs, const char *target) +{ +	int ret; + +	if (pev->uprobes && !pev->group) { +		/* Replace group name if not given */ +		ret = convert_exec_to_group(target, &pev->group); +		if (ret != 0) { +			pr_warning("Failed to make a group name.\n"); +			return ret; +		} +	} + +	/* Convert perf_probe_event with debuginfo */ +	ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); +	if (ret != 0) +		return ret;	/* Found in debuginfo or got an error */ + +	return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);  }  struct __event_package { @@ -1701,17 +2328,18 @@ struct __event_package {  };  int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, -			  int max_tevs, const char *module, bool force_add) +			  int max_tevs, const char *target, bool force_add)  {  	int i, j, ret;  	struct __event_package *pkgs; +	ret = 0;  	pkgs = zalloc(sizeof(struct __event_package) * npevs); +  	if (pkgs == NULL)  		return -ENOMEM; -	/* Init vmlinux path */ -	ret = init_vmlinux(); +	ret = init_symbol_maps(pevs->uprobes);  	if (ret < 0) {  		free(pkgs);  		return ret; @@ -1724,24 +2352,28 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,  		ret  = convert_to_probe_trace_events(pkgs[i].pev,  						     &pkgs[i].tevs,  						     max_tevs, -						     module); +						     target);  		if (ret < 0)  			goto end;  		pkgs[i].ntevs = ret;  	}  	/* Loop 2: add all events */ -	for (i = 0; i < npevs && ret >= 0; i++) +	for (i = 0; i < npevs; i++) {  		ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,  						pkgs[i].ntevs, force_add); +		if (ret < 0) +			break; +	}  end:  	/* Loop 3: cleanup and free trace events  */  	for (i = 0; i < npevs; i++) {  		for (j = 0; j < pkgs[i].ntevs; j++)  			clear_probe_trace_event(&pkgs[i].tevs[j]); -		free(pkgs[i].tevs); +		zfree(&pkgs[i].tevs);  	}  	free(pkgs); +	exit_symbol_maps();  	return ret;  } @@ -1768,33 +2400,27 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)  	pr_debug("Writing event: %s\n", buf);  	ret = write(fd, buf, strlen(buf)); -	if (ret < 0) +	if (ret < 0) { +		ret = -errno;  		goto error; +	} -	printf("Remove event: %s\n", ent->s); +	printf("Removed event: %s\n", ent->s);  	return 0;  error:  	pr_warning("Failed to delete event: %s\n", strerror(-ret));  	return ret;  } -static int del_trace_probe_event(int fd, const char *group, -				  const char *event, struct strlist *namelist) +static int del_trace_probe_event(int fd, const char *buf, +						  struct strlist *namelist)  { -	char buf[128];  	struct str_node *ent, *n; -	int found = 0, ret = 0; - -	ret = e_snprintf(buf, 128, "%s:%s", group, event); -	if (ret < 0) { -		pr_err("Failed to copy event."); -		return ret; -	} +	int ret = -1;  	if (strpbrk(buf, "*?")) { /* Glob-exp */  		strlist__for_each_safe(ent, n, namelist)  			if (strglobmatch(ent->s, buf)) { -				found++;  				ret = __del_trace_probe_event(fd, ent);  				if (ret < 0)  					break; @@ -1803,40 +2429,43 @@ static int del_trace_probe_event(int fd, const char *group,  	} else {  		ent = strlist__find(namelist, buf);  		if (ent) { -			found++;  			ret = __del_trace_probe_event(fd, ent);  			if (ret >= 0)  				strlist__remove(namelist, ent);  		}  	} -	if (found == 0 && ret >= 0) -		pr_info("Info: Event \"%s\" does not exist.\n", buf);  	return ret;  }  int del_perf_probe_events(struct strlist *dellist)  { -	int fd, ret = 0; +	int ret = -1, ufd = -1, kfd = -1; +	char buf[128];  	const char *group, *event;  	char *p, *str;  	struct str_node *ent; -	struct strlist *namelist; - -	fd = open_kprobe_events(true); -	if (fd < 0) -		return fd; +	struct strlist *namelist = NULL, *unamelist = NULL;  	/* Get current event names */ -	namelist = get_probe_trace_event_names(fd, true); -	if (namelist == NULL) -		return -EINVAL; +	kfd = open_kprobe_events(true); +	if (kfd < 0) +		return kfd; + +	namelist = get_probe_trace_event_names(kfd, true); +	ufd = open_uprobe_events(true); + +	if (ufd >= 0) +		unamelist = get_probe_trace_event_names(ufd, true); + +	if (namelist == NULL && unamelist == NULL) +		goto error;  	strlist__for_each(ent, dellist) {  		str = strdup(ent->s);  		if (str == NULL) {  			ret = -ENOMEM; -			break; +			goto error;  		}  		pr_debug("Parsing: %s\n", str);  		p = strchr(str, ':'); @@ -1848,14 +2477,96 @@ int del_perf_probe_events(struct strlist *dellist)  			group = "*";  			event = str;  		} + +		ret = e_snprintf(buf, 128, "%s:%s", group, event); +		if (ret < 0) { +			pr_err("Failed to copy event."); +			free(str); +			goto error; +		} +  		pr_debug("Group: %s, Event: %s\n", group, event); -		ret = del_trace_probe_event(fd, group, event, namelist); + +		if (namelist) +			ret = del_trace_probe_event(kfd, buf, namelist); + +		if (unamelist && ret != 0) +			ret = del_trace_probe_event(ufd, buf, unamelist); + +		if (ret != 0) +			pr_info("Info: Event \"%s\" does not exist.\n", buf); +  		free(str); -		if (ret < 0) -			break;  	} -	strlist__delete(namelist); -	close(fd); + +error: +	if (kfd >= 0) { +		strlist__delete(namelist); +		close(kfd); +	} + +	if (ufd >= 0) { +		strlist__delete(unamelist); +		close(ufd); +	} + +	return ret; +} + +/* TODO: don't use a global variable for filter ... */ +static struct strfilter *available_func_filter; + +/* + * If a symbol corresponds to a function with global binding and + * matches filter return 0. For all others return 1. + */ +static int filter_available_functions(struct map *map __maybe_unused, +				      struct symbol *sym) +{ +	if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && +	    strfilter__compare(available_func_filter, sym->name)) +		return 0; +	return 1; +} + +int show_available_funcs(const char *target, struct strfilter *_filter, +					bool user) +{ +	struct map *map; +	int ret; + +	ret = init_symbol_maps(user); +	if (ret < 0) +		return ret; + +	/* Get a symbol map */ +	if (user) +		map = dso__new_map(target); +	else +		map = kernel_get_module_map(target); +	if (!map) { +		pr_err("Failed to get a map for %s\n", (target) ? : "kernel"); +		return -EINVAL; +	} + +	/* Load symbols with given filter */ +	available_func_filter = _filter; +	if (map__load(map, filter_available_functions)) { +		pr_err("Failed to load symbols in %s\n", (target) ? : "kernel"); +		goto end; +	} +	if (!dso__sorted_by_name(map->dso, map->type)) +		dso__sort_by_name(map->dso, map->type); + +	/* Show all (filtered) symbols */ +	setup_pager(); +	dso__fprintf_symbols_by_name(map->dso, map->type, stdout); +end: +	if (user) { +		dso__delete(map->dso); +		map__delete(map); +	} +	exit_symbol_maps();  	return ret;  }  | 
