lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20120411144918.GD16257@infradead.org>
Date:	Wed, 11 Apr 2012 11:49:18 -0300
From:	Arnaldo Carvalho de Melo <acme@...radead.org>
To:	Srikar Dronamraju <srikar@...ux.vnet.ibm.com>
Cc:	Peter Zijlstra <peterz@...radead.org>, Ingo Molnar <mingo@...e.hu>,
	Andrew Morton <akpm@...ux-foundation.org>,
	Linus Torvalds <torvalds@...ux-foundation.org>,
	Ananth N Mavinakayanahalli <ananth@...ibm.com>,
	Jim Keniston <jkenisto@...ux.vnet.ibm.com>,
	LKML <linux-kernel@...r.kernel.org>,
	Linux-mm <linux-mm@...ck.org>, Oleg Nesterov <oleg@...hat.com>,
	Andi Kleen <andi@...stfloor.org>,
	Christoph Hellwig <hch@...radead.org>,
	Steven Rostedt <rostedt@...dmis.org>,
	Masami Hiramatsu <masami.hiramatsu.pt@...achi.com>,
	Thomas Gleixner <tglx@...utronix.de>,
	Anton Arapov <anton@...hat.com>
Subject: Re: [PATCH] perf/probe: Provide perf interface for uprobes

Em Wed, Apr 11, 2012 at 07:27:42PM +0530, Srikar Dronamraju escreveu:
> From: Srikar Dronamraju <srikar@...ux.vnet.ibm.com>
> 
> - Enhances perf to probe user space executables and libraries.
> - Enhances -F/--funcs option of "perf probe" to list possible probe points in
>   an executable file or library.
> - Documents userspace probing support in perf.
> 
> [ Probing a function in the executable using function name  ]
> perf probe -x /bin/zsh zfree
> 
> [ Probing a library function using function name ]
> perf probe -x /lib64/libc.so.6 malloc
> 
> [ list probe-able functions in an executable ]
> perf probe -F -x /bin/zsh
> 
> [ list probe-able functions in an library]
> perf probe -F -x /lib/libc.so.6

Can we avoid the need for -x? I.e. we could figure out it is userspace
and act accordingly.

- Arnaldo
 
> Signed-off-by: Srikar Dronamraju <srikar@...ux.vnet.ibm.com>
> ---
> 
> Changelog:
> (v9)
> - Handled comments from Masami Hiramatsu that helps reduce code changes.
> - Handled usability comment(print complete name of file) from Ingo Molnar.
> 
> (v5)
> - Removed the separate documentation change patch and added the
>   documentation changes as part of this patch.
> 
> Usage:
> [root@...alhost ~]# perf probe -x /bin/zsh zfree
> Add new event:
>   probe_zsh:zfree      (on /bin/zsh:0x45400)
> 
> You can now use it on all perf tools, such as:
> 
> 	perf record -e probe_zsh:zfree -aR sleep 1
> 
> [root@...alhost ~]# perf record -e probe_zsh:zfree -aR sleep 15
> [ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.314 MB perf.data (~13715 samples) ]
> [root@...alhost ~]# perf report --stdio
> # Events: 3K probe_zsh:zfree
> #
> # Overhead  Command  Shared Object  Symbol
> # ........  .......  .............  ......
> #
>    100.00%              zsh  zsh            [.] zfree
> 
> 
> #
> # (For a higher level overview, try: perf report --sort comm,dso)
> #
> [root@...alhost ~]
> 
> [ Probing a library function using function name ]
> --------------------------------------------------
> [root@...alhost]#
> [root@...alhost]# perf probe -x /lib64/libc.so.6 malloc
> Add new event:
>   probe_libc:malloc    (on /lib64/libc-2.5.so:0x74dc0)
> 
> You can now use it on all perf tools, such as:
> 
> 	perf record -e probe_libc:malloc -aR sleep 1
> 
> [root@...alhost]#
> [root@...alhost]# perf probe --list
>   probe_libc:malloc    (on /lib64/libc-2.5.so:0x0000000000074dc0)
> 
> 
> Show last 10 functions in /bin/zsh.
> 
> # perf probe -F -x /bin/zsh | tail
> zstrtol
> ztrcmp
> ztrdup
> ztrduppfx
> ztrftime
> ztrlen
> ztrncpy
> ztrsub
> zwarn
> zwarnnam
> 
> Show first 10 functions in /lib/libc.so.6
> 
> # perf probe -F -x /lib/libc.so.6 | head
> _IO_adjust_column
> _IO_adjust_wcolumn
> _IO_default_doallocate
> _IO_default_finish
> _IO_default_pbackfail
> _IO_default_uflow
> _IO_default_xsgetn
> _IO_default_xsputn
> _IO_do_write@@GLIBC_2.2.5
> _IO_doallocbuf
> 
>  tools/perf/Documentation/perf-probe.txt |   15 +
>  tools/perf/builtin-probe.c              |   42 +++
>  tools/perf/util/probe-event.c           |  424 +++++++++++++++++++++++++------
>  tools/perf/util/probe-event.h           |   12 +
>  tools/perf/util/symbol.c                |    8 +
>  tools/perf/util/symbol.h                |    1 
>  6 files changed, 405 insertions(+), 97 deletions(-)
> 
> diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
> index 2780d9c..fb673be 100644
> --- a/tools/perf/Documentation/perf-probe.txt
> +++ b/tools/perf/Documentation/perf-probe.txt
> @@ -77,7 +77,8 @@ OPTIONS
>  
>  -F::
>  --funcs::
> -	Show available functions in given module or kernel.
> +	Show available functions in given module or kernel. With -x/--exec,
> +	can also list functions in a user space executable / shared library.
>  
>  --filter=FILTER::
>  	(Only for --vars and --funcs) Set filter. FILTER is a combination of glob
> @@ -98,6 +99,11 @@ OPTIONS
>  --max-probes::
>  	Set the maximum number of probe points for an event. Default is 128.
>  
> +-x::
> +--exec=PATH::
> +	Specify path to the executable or shared library file for user
> +	space tracing. Can also be used with --funcs option.
> +
>  PROBE SYNTAX
>  ------------
>  Probe points are defined by following syntax.
> @@ -182,6 +188,13 @@ Delete all probes on schedule().
>  
>   ./perf probe --del='schedule*'
>  
> +Add probes at zfree() function on /bin/zsh
> +
> + ./perf probe -x /bin/zsh zfree
> +
> +Add probes at malloc() function on libc
> +
> + ./perf probe -x /lib/libc.so.6 malloc
>  
>  SEE ALSO
>  --------
> diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
> index 4935c09..c1bf0d8 100644
> --- a/tools/perf/builtin-probe.c
> +++ b/tools/perf/builtin-probe.c
> @@ -54,6 +54,7 @@ static struct {
>  	bool show_ext_vars;
>  	bool show_funcs;
>  	bool mod_events;
> +	bool uprobes;
>  	int nevents;
>  	struct perf_probe_event events[MAX_PROBES];
>  	struct strlist *dellist;
> @@ -75,6 +76,7 @@ static int parse_probe_event(const char *str)
>  		return -1;
>  	}
>  
> +	pev->uprobes = params.uprobes;
>  	/* Parse a perf-probe command into event */
>  	ret = parse_perf_probe_command(str, pev);
>  	pr_debug("%d arguments\n", pev->nargs);
> @@ -125,6 +127,28 @@ static int opt_del_probe_event(const struct option *opt __used,
>  	return 0;
>  }
>  
> +static int opt_set_target(const struct option *opt, const char *str,
> +			int unset __used)
> +{
> +	int ret = -ENOENT;
> +
> +	if  (str && !params.target) {
> +		if (!strcmp(opt->long_name, "exec"))
> +			params.uprobes = true;
> +#ifdef DWARF_SUPPORT
> +		else if (!strcmp(opt->long_name, "module"))
> +			params.uprobes = false;
> +#endif
> +		else
> +			return ret;
> +
> +		params.target = str;
> +		ret = 0;
> +	}
> +
> +	return ret;
> +}
> +
>  #ifdef DWARF_SUPPORT
>  static int opt_show_lines(const struct option *opt __used,
>  			  const char *str, int unset __used)
> @@ -246,9 +270,9 @@ static const struct option options[] = {
>  		   "file", "vmlinux pathname"),
>  	OPT_STRING('s', "source", &symbol_conf.source_prefix,
>  		   "directory", "path to kernel source"),
> -	OPT_STRING('m', "module", &params.target,
> -		   "modname|path",
> -		   "target module name (for online) or path (for offline)"),
> +	OPT_CALLBACK('m', "module", NULL, "modname|path",
> +		"target module name (for online) or path (for offline)",
> +		opt_set_target),
>  #endif
>  	OPT__DRY_RUN(&probe_event_dry_run),
>  	OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
> @@ -260,6 +284,8 @@ static const struct option options[] = {
>  		     "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n"
>  		     "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)",
>  		     opt_set_filter),
> +	OPT_CALLBACK('x', "exec", NULL, "executable|path",
> +			"target executable name or path", opt_set_target),
>  	OPT_END()
>  };
>  
> @@ -310,6 +336,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
>  			pr_err("  Error: Don't use --list with --funcs.\n");
>  			usage_with_options(probe_usage, options);
>  		}
> +		if (params.uprobes) {
> +			pr_warning("  Error: Don't use --list with --exec.\n");
> +			usage_with_options(probe_usage, options);
> +		}
>  		ret = show_perf_probe_events();
>  		if (ret < 0)
>  			pr_err("  Error: Failed to show event list. (%d)\n",
> @@ -333,8 +363,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
>  		if (!params.filter)
>  			params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
>  						       NULL);
> -		ret = show_available_funcs(params.target,
> -					   params.filter);
> +		ret = show_available_funcs(params.target, params.filter,
> +					params.uprobes);
>  		strfilter__delete(params.filter);
>  		if (ret < 0)
>  			pr_err("  Error: Failed to show functions."
> @@ -343,7 +373,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
>  	}
>  
>  #ifdef DWARF_SUPPORT
> -	if (params.show_lines) {
> +	if (params.show_lines && !params.uprobes) {
>  		if (params.mod_events) {
>  			pr_err("  Error: Don't use --line with"
>  			       " --add/--del.\n");
> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> index 8a8ee64..b7dec82 100644
> --- a/tools/perf/util/probe-event.c
> +++ b/tools/perf/util/probe-event.c
> @@ -44,6 +44,7 @@
>  #include "trace-event.h"	/* For __unused */
>  #include "probe-event.h"
>  #include "probe-finder.h"
> +#include "session.h"
>  
>  #define MAX_CMDLEN 256
>  #define MAX_PROBE_ARGS 128
> @@ -70,6 +71,8 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
>  }
>  
>  static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
> +static int convert_name_to_addr(struct perf_probe_event *pev,
> +				const char *exec);
>  static struct machine machine;
>  
>  /* Initialize symbol maps and path of vmlinux/modules */
> @@ -170,6 +173,34 @@ const char *kernel_get_module_path(const char *module)
>  	return (dso) ? dso->long_name : NULL;
>  }
>  
> +static int init_user_exec(void)
> +{
> +	int ret = 0;
> +
> +	symbol_conf.try_vmlinux_path = false;
> +	symbol_conf.sort_by_name = true;
> +	ret = symbol__init();
> +
> +	if (ret < 0)
> +		pr_debug("Failed to init symbol map.\n");
> +
> +	return ret;
> +}
> +
> +static int convert_to_perf_probe_point(struct probe_trace_point *tp,
> +					struct perf_probe_point *pp)
> +{
> +	pp->function = strdup(tp->symbol);
> +
> +	if (pp->function == NULL)
> +		return -ENOMEM;
> +
> +	pp->offset = tp->offset;
> +	pp->retprobe = tp->retprobe;
> +
> +	return 0;
> +}
> +
>  #ifdef DWARF_SUPPORT
>  /* Open new debuginfo of given module */
>  static struct debuginfo *open_debuginfo(const char *module)
> @@ -224,10 +255,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
>  	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)
> -			return -ENOMEM;
> -		pp->offset = tp->offset;
> +		return convert_to_perf_probe_point(tp, pp);
>  	}
>  	pp->retprobe = tp->retprobe;
>  
> @@ -275,9 +303,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
>  					  int max_tevs, const char *target)
>  {
>  	bool need_dwarf = perf_probe_event_need_dwarf(pev);
> -	struct debuginfo *dinfo = open_debuginfo(target);
> +	struct debuginfo *dinfo;
>  	int ntevs, ret = 0;
>  
> +	if (pev->uprobes) {
> +		if (need_dwarf) {
> +			pr_warning("Debuginfo-analysis is not yet supported"
> +					" with -x/--exec option.\n");
> +			return -ENOSYS;
> +		}
> +		return convert_name_to_addr(pev, target);
> +	}
> +
> +	dinfo = open_debuginfo(target);
> +
>  	if (!dinfo) {
>  		if (need_dwarf) {
>  			pr_warning("Failed to open debuginfo file.\n");
> @@ -603,23 +642,22 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
>  		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 convert_to_perf_probe_point(tp, pp);
>  }
>  
>  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)
> +				int max_tevs __unused, const char *target)
>  {
>  	if (perf_probe_event_need_dwarf(pev)) {
>  		pr_warning("Debuginfo-analysis is not supported.\n");
>  		return -ENOSYS;
>  	}
> +
> +	if (pev->uprobes)
> +		return convert_name_to_addr(pev, target);
> +
>  	return 0;
>  }
>  
> @@ -1341,11 +1379,18 @@ 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%s%s+%lu",
> -			 tp->retprobe ? 'r' : 'p',
> -			 tev->group, tev->event,
> -			 tp->module ?: "", tp->module ? ":" : "",
> -			 tp->symbol, tp->offset);
> +	if (tev->uprobes)
> +		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s",
> +				 tp->retprobe ? 'r' : 'p',
> +				 tev->group, tev->event,
> +				 tp->module, tp->symbol);
> +	else
> +		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
> +				 tp->retprobe ? 'r' : 'p',
> +				 tev->group, tev->event,
> +				 tp->module ?: "", tp->module ? ":" : "",
> +				 tp->symbol, tp->offset);
> +
>  	if (len <= 0)
>  		goto error;
>  
> @@ -1364,7 +1409,7 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
>  }
>  
>  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;
> @@ -1376,7 +1421,11 @@ 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);
> +	if (is_kprobe)
> +		ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
> +	else
> +		ret = convert_to_perf_probe_point(&tev->point, &pev->point);
> +
>  	if (ret < 0)
>  		return ret;
>  
> @@ -1472,7 +1521,26 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
>  	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;
> @@ -1484,27 +1552,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;
> @@ -1569,36 +1641,26 @@ 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);
>  		}
> @@ -1612,6 +1674,33 @@ 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_vmlinux();
> +	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);
> +	}
> +
> +	return ret;
> +}
> +
>  /* Get current perf-probe event names */
>  static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
>  {
> @@ -1717,7 +1806,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 */
> @@ -1829,6 +1922,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
>  	tev->point.offset = pev->point.offset;
>  	tev->point.retprobe = pev->point.retprobe;
>  	tev->nargs = pev->nargs;
> +	tev->uprobes = pev->uprobes;
>  	if (tev->nargs) {
>  		tev->args = zalloc(sizeof(struct probe_trace_arg)
>  				   * tev->nargs);
> @@ -1859,6 +1953,9 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
>  		}
>  	}
>  
> +	if (pev->uprobes)
> +		return 1;
> +
>  	/* Currently just checking function name from symbol map */
>  	sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
>  	if (!sym) {
> @@ -1894,12 +1991,18 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
>  	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();
> +	if (!pevs->uprobes)
> +		/* Init vmlinux path */
> +		ret = init_vmlinux();
> +	else
> +		ret = init_user_exec();
> +
>  	if (ret < 0) {
>  		free(pkgs);
>  		return ret;
> @@ -1971,23 +2074,15 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
>  	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.\n");
> -		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;
> @@ -1996,40 +2091,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, ':');
> @@ -2041,17 +2139,46 @@ 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) {
> +		if (namelist)
> +			strlist__delete(namelist);
> +
> +		close(kfd);
> +	}
> +
> +	if (ufd >= 0) {
> +		if (unamelist)
> +			strlist__delete(unamelist);
> +
> +		close(ufd);
> +	}
>  
>  	return ret;
>  }
> +
>  /* TODO: don't use a global variable for filter ... */
>  static struct strfilter *available_func_filter;
>  
> @@ -2068,30 +2195,157 @@ static int filter_available_functions(struct map *map __unused,
>  	return 1;
>  }
>  
> -int show_available_funcs(const char *target, struct strfilter *_filter)
> +static int __show_available_funcs(struct map *map)
> +{
> +	if (map__load(map, filter_available_functions)) {
> +		pr_err("Failed to load map.\n");
> +		return -EINVAL;
> +	}
> +	if (!dso__sorted_by_name(map->dso, map->type))
> +		dso__sort_by_name(map->dso, map->type);
> +
> +	dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
> +	return 0;
> +}
> +
> +static int available_kernel_funcs(const char *module)
>  {
>  	struct map *map;
>  	int ret;
>  
> -	setup_pager();
> -
>  	ret = init_vmlinux();
>  	if (ret < 0)
>  		return ret;
>  
> -	map = kernel_get_module_map(target);
> +	map = kernel_get_module_map(module);
>  	if (!map) {
> -		pr_err("Failed to find %s map.\n", (target) ? : "kernel");
> +		pr_err("Failed to find %s map.\n", (module) ? : "kernel");
>  		return -EINVAL;
>  	}
> +	return __show_available_funcs(map);
> +}
> +
> +static int available_user_funcs(const char *target)
> +{
> +	struct map *map;
> +	int ret;
> +
> +	ret = init_user_exec();
> +	if (ret < 0)
> +		return ret;
> +
> +	map = dso__new_map(target);
> +	ret = __show_available_funcs(map);
> +	dso__delete(map->dso);
> +	map__delete(map);
> +	return ret;
> +}
> +
> +int show_available_funcs(const char *target, struct strfilter *_filter,
> +					bool user)
> +{
> +	setup_pager();
>  	available_func_filter = _filter;
> +
> +	if (!user)
> +		return available_kernel_funcs(target);
> +
> +	return available_user_funcs(target);
> +}
> +
> +/*
> + * uprobe_events only accepts address:
> + * Convert function and any offset to address
> + */
> +static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
> +{
> +	struct perf_probe_point *pp = &pev->point;
> +	struct symbol *sym;
> +	struct map *map = NULL;
> +	char *function = NULL, *name = NULL;
> +	int ret = -EINVAL;
> +	unsigned long long vaddr = 0;
> +
> +	if (!pp->function) {
> +		pr_warning("No function specified for uprobes");
> +		goto out;
> +	}
> +
> +	if (perf_probe_event_need_dwarf(pev)) {
> +		pr_warning("No dwarf based probes for uprobes.");
> +		goto out;
> +	}
> +
> +	function = strdup(pp->function);
> +	if (!function) {
> +		pr_warning("Failed to allocate memory by strdup.\n");
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	name = realpath(exec, NULL);
> +	if (!name) {
> +		pr_warning("Cannot find realpath for %s.\n", exec);
> +		goto out;
> +	}
> +	map = dso__new_map(name);
> +	if (!map) {
> +		pr_warning("Cannot find appropriate DSO for %s.\n", exec);
> +		goto out;
> +	}
> +	available_func_filter = strfilter__new(function, NULL);
>  	if (map__load(map, filter_available_functions)) {
>  		pr_err("Failed to load map.\n");
>  		return -EINVAL;
>  	}
> -	if (!dso__sorted_by_name(map->dso, map->type))
> -		dso__sort_by_name(map->dso, map->type);
>  
> -	dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
> -	return 0;
> +	sym = map__find_symbol_by_name(map, function, NULL);
> +	if (!sym) {
> +		pr_warning("Cannot find %s in DSO %s\n", function, exec);
> +		goto out;
> +	}
> +
> +	if (map->start > sym->start)
> +		vaddr = map->start;
> +	vaddr += sym->start + pp->offset + map->pgoff;
> +	pp->offset = 0;
> +
> +	if (!pev->event) {
> +		pev->event = function;
> +		function = NULL;
> +	}
> +	if (!pev->group) {
> +		char *ptr1, *ptr2;
> +
> +		pev->group = zalloc(sizeof(char *) * 64);
> +		ptr1 = strdup(basename(exec));
> +		if (ptr1) {
> +			ptr2 = strpbrk(ptr1, "-._");
> +			if (ptr2)
> +				*ptr2 = '\0';
> +			e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
> +					ptr1);
> +			free(ptr1);
> +		}
> +	}
> +	free(pp->function);
> +	pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
> +	if (!pp->function) {
> +		ret = -ENOMEM;
> +		pr_warning("Failed to allocate memory by zalloc.\n");
> +		goto out;
> +	}
> +	e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
> +	ret = 0;
> +
> +out:
> +	if (map) {
> +		dso__delete(map->dso);
> +		map__delete(map);
> +	}
> +	if (function)
> +		free(function);
> +	if (name)
> +		free(name);
> +	return ret;
>  }
> diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
> index a7dee83..f9f3de8 100644
> --- a/tools/perf/util/probe-event.h
> +++ b/tools/perf/util/probe-event.h
> @@ -7,7 +7,7 @@
>  
>  extern bool probe_event_dry_run;
>  
> -/* kprobe-tracer tracing point */
> +/* kprobe-tracer and uprobe-tracer tracing point */
>  struct probe_trace_point {
>  	char		*symbol;	/* Base symbol */
>  	char		*module;	/* Module name */
> @@ -21,7 +21,7 @@ struct probe_trace_arg_ref {
>  	long				offset;	/* Offset value */
>  };
>  
> -/* kprobe-tracer tracing argument */
> +/* kprobe-tracer and uprobe-tracer tracing argument */
>  struct probe_trace_arg {
>  	char				*name;	/* Argument name */
>  	char				*value;	/* Base value */
> @@ -29,12 +29,13 @@ struct probe_trace_arg {
>  	struct probe_trace_arg_ref	*ref;	/* Referencing offset */
>  };
>  
> -/* kprobe-tracer tracing event (point + arg) */
> +/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
>  struct probe_trace_event {
>  	char				*event;	/* Event name */
>  	char				*group;	/* Group name */
>  	struct probe_trace_point	point;	/* Trace point */
>  	int				nargs;	/* Number of args */
> +	bool				uprobes;	/* uprobes only */
>  	struct probe_trace_arg		*args;	/* Arguments */
>  };
>  
> @@ -70,6 +71,7 @@ struct perf_probe_event {
>  	char			*group;	/* Group name */
>  	struct perf_probe_point	point;	/* Probe point */
>  	int			nargs;	/* Number of arguments */
> +	bool			uprobes;
>  	struct perf_probe_arg	*args;	/* Arguments */
>  };
>  
> @@ -129,8 +131,8 @@ extern int show_line_range(struct line_range *lr, const char *module);
>  extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
>  			       int max_probe_points, const char *module,
>  			       struct strfilter *filter, bool externs);
> -extern int show_available_funcs(const char *module, struct strfilter *filter);
> -
> +extern int show_available_funcs(const char *module, struct strfilter *filter,
> +				bool user);
>  
>  /* Maximum index number of event-name postfix */
>  #define MAX_EVENT_INDEX	1024
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index c0a028c..caaf75a 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -2784,3 +2784,11 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
>  
>  	return ret;
>  }
> +
> +struct map *dso__new_map(const char *name)
> +{
> +	struct dso *dso = dso__new(name);
> +	struct map *map = map__new2(0, dso, MAP__FUNCTION);
> +
> +	return map;
> +}
> diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
> index ac49ef2..9e7742c 100644
> --- a/tools/perf/util/symbol.h
> +++ b/tools/perf/util/symbol.h
> @@ -237,6 +237,7 @@ void dso__set_long_name(struct dso *dso, char *name);
>  void dso__set_build_id(struct dso *dso, void *build_id);
>  void dso__read_running_kernel_build_id(struct dso *dso,
>  				       struct machine *machine);
> +struct map *dso__new_map(const char *name);
>  struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
>  				u64 addr);
>  struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ