[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20100825134411.5447.68193.sendpatchset@localhost6.localdomain6>
Date: Wed, 25 Aug 2010 19:14:11 +0530
From: Srikar Dronamraju <srikar@...ux.vnet.ibm.com>
To: Peter Zijlstra <peterz@...radead.org>, Ingo Molnar <mingo@...e.hu>
Cc: Steven Rostedt <rostedt@...dmis.org>,
Srikar Dronamraju <srikar@...ux.vnet.ibm.com>,
Randy Dunlap <rdunlap@...otime.net>,
Arnaldo Carvalho de Melo <acme@...radead.org>,
Linus Torvalds <torvalds@...ux-foundation.org>,
Christoph Hellwig <hch@...radead.org>,
Masami Hiramatsu <masami.hiramatsu.pt@...achi.com>,
Oleg Nesterov <oleg@...hat.com>,
Mark Wielaard <mjw@...hat.com>,
Mathieu Desnoyers <mathieu.desnoyers@...icios.com>,
LKML <linux-kernel@...r.kernel.org>,
Naren A Devaiah <naren.devaiah@...ibm.com>,
Jim Keniston <jkenisto@...ux.vnet.ibm.com>,
Frederic Weisbecker <fweisbec@...il.com>,
"Frank Ch. Eigler" <fche@...hat.com>,
Ananth N Mavinakayanahalli <ananth@...ibm.com>,
Andrew Morton <akpm@...ux-foundation.org>,
"Paul E. McKenney" <paulmck@...ux.vnet.ibm.com>
Subject: [PATCHv11 2.6.36-rc2-tip 14/15] 14: perf: perf interface for uprobes
Enhances perf probe to accept pid and user vaddr.
Provides very basic support for uprobes.
TODO:
Update perf-probes.txt.
Global tracing.
Signed-off-by: Srikar Dronamraju <srikar@...ux.vnet.ibm.com>
---
Changelog from V10: split few independent hunks into different
patches as suggested by Arnaldo.
Changelog from v9: Renaming common fields/functions to refer to
probe instead of kprobe. This was suggested by Arnaldo.
Changelog from v8: Fixed a build break reported by Christoph Hellwig.
Changelog from v6: Changelog from v6: Fixed a bug reported by Masami.
i.e Throw an error message and exit if perf probe is for a dwarf
based probes.
Changelog from v4: Merged to 2.6.35-rc3-tip.
Changelog from v3: (addressed comments from Masami Hiramatsu)
* Every process id has a different group name.
* event name starts with function name.
* If vaddr is specified, event name has vaddr appended
along with function name, (this is to avoid subsequent probes
using same event name.)
* warning if -p and --list options are used together.
Also dso can either be a short name or absolute path.
Here is a terminal snapshot of placing, using and removing a probe on a
process with pid 3591 (corresponding to zsh)
[ Probing a function in the executable using function name ]
-------------------------------------------------------------
[root@...D]# perf probe -p 3591 zfree@zsh
Added new event:
probe_3591:zfree (on 0x446420)
You can now use it on all perf tools, such as:
perf record -e probe_3591:zfree -a sleep 1
[root@...D]# perf probe --list
probe_3591:zfree (on 3591:0x0000000000446420)
[root@...D]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/zfree 3591:0x0000000000446420
[root@...D]# perf record -f -e probe_3591:zfree -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.039 MB perf.data (~1716 samples) ]
[root@...D]# perf probe -p 3591 --del probe_3591:zfree
Remove event: probe_3591:zfree
[root@...D]# perf report
# Samples: 447
#
# Overhead Command Shared Object Symbol
# ........ ............... ............. ......
#
100.00% zsh zsh [.] zfree
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[ Probing a function + offset ]
-------------------------------
[root@...D]# perf probe -p 3591 zfree@...+5
Added new event:
probe_3591:zfree (on 0x446425)
You can now use it on all perf tools, such as:
perf record -e probe_3591:zfree -a sleep 1
[root@...D]# perf probe --list
probe_3591:zfree (on 3591:0x0000000000446425)
[root@...D]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/zfree 3591:0x0000000000446425
[root@...D]# perf record -f -e probe_3591:zfree -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.036 MB perf.data (~1590 samples) ]
[root@...D]# perf probe -p 3591 --del probe_3591:zfree
Remove event: probe_3591:zfree
[root@...D]# perf report
# Samples: 18
#
# Overhead Command Shared Object Symbol
# ........ ............... ............. ......
#
100.00% zsh zsh [.] zfree
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[ Probing a library function using function name ]
--------------------------------------------------
[root@...D]# perf probe -p 3591 write@...c-2.5.so
Added new event:
probe_3591:write (on 0x36010c6060)
You can now use it on all perf tools, such as:
perf record -e probe_3591:write -a sleep 1
[root@...D]# perf probe --list
probe_3591:write (on 3591:0x00000036010c6060)
[root@...D]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/write 3591:0x00000036010c6060
[root@...D]# perf record -f -e probe_3591:write -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.040 MB perf.data (~1738 samples) ]
[root@...D]# perf probe -p 3591 --del probe_3591:write
Remove event: probe_3591:write
[root@...D]# perf report
# Samples: 11
#
# Overhead Command Shared Object Symbol
# ........ ............... .................. ......
#
100.00% zsh libc-2.5.so [.] __GI___libc_write
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[ Probing a library function using function name and absolute path ]
---------------------------------------------------------------------
[root@...D]# perf probe -p 3591 write@...b64/libc-2.5.so
Added new event:
probe_3591:write (on 0x36010c6060)
You can now use it on all perf tools, such as:
perf record -e probe_3591:write -a sleep 1
[root@...D]# perf probe --list
probe_3591:write (on 3591:0x00000036010c6060)
[root@...D]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/write 3591:0x00000036010c6060
[root@...D]# perf record -f -e probe_3591:write -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.040 MB perf.data (~1738 samples) ]
[root@...D]# perf probe -p 3591 --del probe_3591:write
Remove event: probe_3591:write
[root@...D]# perf report
# Samples: 11
#
# Overhead Command Shared Object Symbol
# ........ ............... .................. ......
#
100.00% zsh libc-2.5.so [.] __GI___libc_write
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[ Probing using vaddr 0x0000000000446420 (corresponding to zfree)]
-------------------------------------------------------------------
[root@...D]# perf probe -p 3591 0x0000000000446420
Added new event:
probe_3591:zfree_446420 (on 0x0000000000446420)
You can now use it on all perf tools, such as:
perf record -e probe_3591:zfree_446420 -a sleep 1
[root@...D]# perf record -e probe_3591:zfree_446420 -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.041 MB perf.data (~1797 samples) ]
[root@...D]# perf report
#
# Samples: 628
#
# Overhead Command Shared Object Symbol
# ........ ............... ............. ......
#
100.00% zsh zsh [.] zfree
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@...D]# perf report --sort comm,dso
# Samples: 628
#
# Overhead Command Shared Object
# ........ ............... .............
#
100.00% zsh zsh
[root@...D]# perf probe --list
probe_3591:zfree_446420 (on 3591:0x0000000000446420)
[root@...D]# perf list | grep probe
probe_3591:zfree_446420 [Tracepoint event]
[root@...D]# perf probe -p 3591 --del probe_3591:zfree_446420
Remove event: probe_3591:zfree_446420
[root@...D]#
Another example for a shared library: write stub in libc. (corresponds to
0x00000036010c6060)
on a vaddr
[ Probing a libc vaddr 0x00000036010c6060 (corresponding to write) ]
[root@...D]# perf probe -p 3591 0x00000036010c6060
dded new event:
probe_3591:__GI___libc_write_36010c6060 (on 0x00000036010c6060)
You can now use it on all perf tools, such as:
perf record -e probe_3591:__GI___libc_write_36010c6060 -a sleep 1
[root@...D]# perf record -f -e probe_3591:__GI___libc_write_36010c6060 -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.040 MB perf.data (~1748 samples) ]
[root@...D]# perf report
# Samples: 24
#
# Overhead Command Shared Object Symbol
# ........ ............... .................. ......
#
100.00% zsh libc-2.5.so [.] __GI___libc_write
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@...D]#
[ Probing using a function without specifying a dso (corresponding to zfree)]
-------------------------------------------------------------------
[root@...D]# perf probe -p 3591 zfree
Added new event:
probe_3591:zfree (on 0x0000000000446420)
You can now use it on all perf tools, such as:
perf record -e probe_3591:zfree -a sleep 1
[root@...D]# perf record -e probe_3591:zfree -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.041 MB perf.data (~1797 samples) ]
[root@...D]# perf report
#
# Samples: 628
#
# Overhead Command Shared Object Symbol
# ........ ............... ............. ......
#
100.00% zsh zsh [.] zfree
#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@...D]#
tools/perf/builtin-probe.c | 12 +
tools/perf/util/probe-event.c | 389 +++++++++++++++++++++++++++++++++--------
tools/perf/util/probe-event.h | 10 +
3 files changed, 328 insertions(+), 83 deletions(-)
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index fa63245..afca6ae 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -57,6 +57,7 @@ static struct {
struct strlist *limitlist;
struct line_range line_range;
int max_probe_points;
+ pid_t upid;
} params;
@@ -75,6 +76,7 @@ static int parse_probe_event(const char *str)
/* Parse a perf-probe command into event */
ret = parse_perf_probe_command(str, pev);
pr_debug("%d arguments\n", pev->nargs);
+ pev->upid = params.upid;
return ret;
}
@@ -207,6 +209,8 @@ static const struct option options[] = {
"Show potential probes."),
OPT_CALLBACK('D', "limit_dsos", NULL, "DSO",
"Limit Dsos.", opt_limit_dsos),
+ OPT_INTEGER('p', "pid", ¶ms.upid,
+ "Specify pid of process where probe will be added"),
OPT_END()
};
@@ -249,6 +253,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
" --show_functions.\n");
usage_with_options(probe_usage, options);
}
+ if (params.upid) {
+ pr_warning(" Error: Don't use --list with --pid.\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",
@@ -274,7 +282,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.upid) {
if (params.nevents != 0 || params.dellist) {
pr_warning(" Error: Don't use --line with"
" --add/--del.\n");
@@ -289,7 +297,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
#endif
if (params.dellist) {
- ret = del_perf_probe_events(params.dellist);
+ ret = del_perf_probe_events(params.dellist, params.upid);
strlist__delete(params.dellist);
if (ret < 0) {
pr_err(" Error: Failed to delete events. (%d)\n", ret);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2efbe4b..17a89b1 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,6 +74,7 @@ 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);
static struct machine machine;
/* Initialize symbol maps and path of vmlinux */
@@ -111,6 +112,32 @@ out:
return ret;
}
+static int init_perf_uprobes(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
static int open_vmlinux(void)
{
@@ -163,6 +190,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
bool need_dwarf = perf_probe_event_need_dwarf(pev);
int fd, ntevs;
+ if (pev->upid) {
+ if (need_dwarf) {
+ pr_warning("Debuginfo-analysis is not yet supported"
+ " with -p/--pid option.\n");
+ return -ENOSYS;
+ }
+ return convert_name_to_addr(pev);
+ }
+
fd = open_vmlinux();
if (fd < 0) {
if (need_dwarf) {
@@ -385,13 +421,7 @@ end:
static int kprobe_convert_to_perf_probe(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;
+ return convert_to_perf_probe_point(tp, pp);
}
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
@@ -402,6 +432,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
}
+ if (pev->upid)
+ return convert_name_to_addr(pev);
+
return 0;
}
@@ -477,6 +510,109 @@ static bool check_event_name(const char *name)
return true;
}
+/*
+ * uprobe_events only accepts address:
+ * Convert function and any offset to address
+ */
+static int convert_name_to_addr(struct perf_probe_event *pev)
+{
+ struct perf_probe_point *pp = &pev->point;
+ struct perf_session *session;
+ struct thread *thread;
+ struct symbol *sym;
+ struct map *map;
+ char *name = NULL, *tmpname = NULL;
+ int ret = -EINVAL;
+ unsigned long long vaddr;
+
+ /* check if user has specifed a virtual address */
+ vaddr = strtoul(pp->function, NULL, 0);
+ session = perf_session__new(NULL, O_WRONLY, false, false);
+ if (!session) {
+ pr_warning("Cannot initialize perf session.\n");
+ return -ENOMEM;
+ }
+
+ event__synthesize_thread(pev->upid, event__process, session);
+ thread = perf_session__findnew(session, pev->upid);
+ if (!thread) {
+ pr_warning("Cannot initialize perf session.\n");
+ goto free_session;
+ }
+
+ if (vaddr) {
+ if (pev->event) {
+ ret = 0;
+ goto free_session;
+ }
+
+ pev->event = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
+ if (!pev->event) {
+ ret = -ENOMEM;
+ pr_warning("Cannot allocate memory for event.\n");
+ goto free_session;
+ }
+
+ sym = map_groups__find_symbol(&thread->mg, MAP__FUNCTION,
+ vaddr, NULL, NULL);
+ if (!sym)
+ snprintf(pev->event, MAX_PROBE_ARGS, "p_%llx", vaddr);
+ else
+ snprintf(pev->event, MAX_PROBE_ARGS, "%s_%llx",
+ sym->name, vaddr);
+ ret = 0;
+ goto free_session;
+ }
+
+ if (!pp->file)
+ /* Lets find the function in the executable. */
+ name = thread->comm;
+ else {
+ tmpname = realpath(pp->file, NULL);
+ if (tmpname)
+ name = basename(tmpname);
+ else
+ name = pp->file;
+ }
+
+ map = map_groups__find_by_name(&thread->mg, MAP__FUNCTION, name);
+ if (pp->file && tmpname)
+ free(tmpname);
+ if (!map) {
+ pr_warning("Cannot find appropriate DSO.\n");
+ goto free_session;
+ }
+
+ sym = map__find_symbol_by_name(map, pp->function, NULL);
+ if (!sym) {
+ pr_warning("Cannot find appropriate DSO.\n");
+ goto free_session;
+ }
+
+ if (map->start > sym->start)
+ vaddr = map->start;
+ vaddr += sym->start + pp->offset + map->pgoff;
+ pp->offset = 0;
+
+ if (!pev->event)
+ pev->event = pp->function;
+ else
+ 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 free_session;
+ }
+ e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
+ ret = 0;
+
+free_session:
+ if (session)
+ perf_session__delete(session);
+ return ret;
+}
+
/* Parse probepoint definition. */
static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
{
@@ -616,6 +752,11 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
return -EINVAL;
}
+ if (pev->upid && !pp->function) {
+ semantic_error("No function specified for uprobes");
+ 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.");
@@ -625,6 +766,11 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
pp->lazy_line);
+
+ if (pev->upid && perf_probe_event_need_dwarf(pev)) {
+ semantic_error("no dwarf based probes for uprobes.");
+ return -EINVAL;
+ }
return 0;
}
@@ -776,7 +922,8 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
{
int i;
- if (pev->point.file || pev->point.line || pev->point.lazy_line)
+ if ((pev->point.file && !pev->upid) || pev->point.line ||
+ pev->point.lazy_line)
return true;
for (i = 0; i < pev->nargs; i++)
@@ -1067,10 +1214,22 @@ 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);
+ if (tev->upid)
+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %d:%s",
+ tp->retprobe ? 'r' : 'p',
+ tev->group, tev->event,
+ tev->upid, tp->symbol);
+ else if (tp->offset)
+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
+ tp->retprobe ? 'r' : 'p',
+ tev->group, tev->event,
+ tp->symbol, tp->offset);
+ else
+ len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s",
+ tp->retprobe ? 'r' : 'p',
+ tev->group, tev->event,
+ tp->symbol);
+
if (len <= 0)
goto error;
@@ -1089,7 +1248,7 @@ error:
}
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;
@@ -1101,7 +1260,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;
@@ -1195,7 +1358,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
memset(tev, 0, sizeof(*tev));
}
-static int open_kprobe_events(bool readwrite)
+static int open_probe_events(bool readwrite, bool is_kprobe)
{
char buf[PATH_MAX];
const char *__debugfs;
@@ -1206,8 +1369,13 @@ static int open_kprobe_events(bool readwrite)
pr_warning("Debugfs is not mounted.\n");
return -ENOENT;
}
+ if (is_kprobe)
+ ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events",
+ __debugfs);
+ else
+ ret = e_snprintf(buf, PATH_MAX, "%stracing/uprobe_events",
+ __debugfs);
- ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs);
if (ret >= 0) {
pr_debug("Opening %s write=%d\n", buf, readwrite);
if (readwrite && !probe_event_dry_run)
@@ -1218,16 +1386,29 @@ static int open_kprobe_events(bool readwrite)
if (ret < 0) {
if (errno == ENOENT)
- pr_warning("kprobe_events file does not exist - please"
- " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
+ pr_warning("%s file does not exist - please"
+ " rebuild kernel with CONFIG_%s_EVENT.\n",
+ is_kprobe ? "kprobe_events" : "uprobe_events",
+ is_kprobe ? "KPROBE" : "UPROBE");
else
- pr_warning("Failed to open kprobe_events file: %s\n",
- strerror(errno));
+ pr_warning("Failed to open %s file: %s\n",
+ is_kprobe ? "kprobe_events" : "uprobe_events",
+ strerror(errno));
}
return ret;
}
-/* Get raw string list of current kprobe_events */
+static int open_kprobe_events(bool readwrite)
+{
+ return open_probe_events(readwrite, 1);
+}
+
+static int open_uprobe_events(bool readwrite)
+{
+ return open_probe_events(readwrite, 0);
+}
+
+/* Get raw string list of current kprobe_events or uprobe_events */
static struct strlist *get_probe_trace_command_rawlist(int fd)
{
int ret, idx;
@@ -1292,36 +1473,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);
}
@@ -1331,6 +1502,31 @@ int show_perf_probe_events(void)
break;
}
strlist__delete(rawlist);
+ 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;
}
@@ -1440,7 +1636,10 @@ 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->upid)
+ fd = open_uprobe_events(true);
+ else
+ fd = open_kprobe_events(true);
if (fd < 0)
return fd;
/* Get current event names */
@@ -1454,17 +1653,26 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
- if (pev->event)
- event = pev->event;
- else
- if (pev->point.function)
- event = pev->point.function;
- else
- event = tev->point.symbol;
+
if (pev->group)
group = pev->group;
- else
+ else if (!pev->upid)
group = PERFPROBE_GROUP;
+ else {
+ /*
+ * For uprobes based probes create a group
+ * probe_<pid>.
+ */
+ snprintf(buf, 64, "%s_%d", PERFPROBE_GROUP, pev->upid);
+ group = buf;
+ }
+
+ if (pev->event)
+ event = pev->event;
+ else if (pev->point.function)
+ event = pev->point.function;
+ else
+ event = tev->point.symbol;
/* Get an unused new event name */
ret = get_new_event_name(buf, 64, event,
@@ -1572,6 +1780,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
}
}
+ if (pev->upid) {
+ tev->upid = pev->upid;
+ return 1;
+ }
+
/* Currently just checking function name from symbol map */
sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
tev->point.symbol, NULL);
@@ -1599,15 +1812,19 @@ struct __event_package {
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
bool force_add, int max_tevs)
{
- int i, j, ret;
+ int i, j, ret = 0;
struct __event_package *pkgs;
pkgs = zalloc(sizeof(struct __event_package) * npevs);
if (pkgs == NULL)
return -ENOMEM;
- /* Init vmlinux path */
- ret = init_vmlinux();
+ if (!pevs->upid)
+ /* Init vmlinux path */
+ ret = init_vmlinux();
+ else
+ ret = init_perf_uprobes();
+
if (ret < 0) {
free(pkgs);
return ret;
@@ -1672,23 +1889,15 @@ error:
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;
@@ -1697,40 +1906,44 @@ 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 del_perf_probe_events(struct strlist *dellist, pid_t pid)
{
- 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;
+ struct strlist *namelist = NULL, *unamelist = NULL;
- fd = open_kprobe_events(true);
- if (fd < 0)
- return fd;
/* Get current event names */
- namelist = get_probe_trace_event_names(fd, true);
- if (namelist == NULL)
- return -EINVAL;
+ if (!pid) {
+ 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, ':');
@@ -1742,15 +1955,37 @@ 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 (!pid && namelist)
+ ret = del_trace_probe_event(kfd, buf, namelist);
+ if (unamelist && ret != 0)
+ ret = del_trace_probe_event(ufd, buf, unamelist);
+
free(str);
- if (ret < 0)
- break;
+ if (ret != 0)
+ pr_info("Info: Event \"%s\" does not exist.\n", buf);
}
- 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;
}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index fcd0f08..9cf1bcc 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -6,7 +6,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 */
unsigned long offset; /* Offset from symbol */
@@ -19,7 +19,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 */
@@ -27,12 +27,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 */
+ pid_t upid; /* uprobes only */
struct probe_trace_arg *args; /* Arguments */
};
@@ -68,6 +69,7 @@ struct perf_probe_event {
char *group; /* Group name */
struct perf_probe_point point; /* Probe point */
int nargs; /* Number of arguments */
+ pid_t upid;
struct perf_probe_arg *args; /* Arguments */
};
@@ -112,7 +114,7 @@ extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
bool force_add, int max_probe_points);
-extern int del_perf_probe_events(struct strlist *dellist);
+extern int del_perf_probe_events(struct strlist *dellist, pid_t pid);
extern int show_perf_probe_events(void);
extern int show_line_range(struct line_range *lr);
extern int show_possible_probes(struct strlist *availlist);
--
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