From: Srikar Dronamraju on

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(a)linux.vnet.ibm.com>
---

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(a)ABCD]# perf probe -p 3591 zfree(a)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(a)ABCD]# perf probe --list
probe_3591:zfree (on 3591:0x0000000000446420)
[root(a)ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/zfree 3591:0x0000000000446420
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 --del probe_3591:zfree
Remove event: probe_3591:zfree
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 zfree(a)zsh+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(a)ABCD]# perf probe --list
probe_3591:zfree (on 3591:0x0000000000446425)
[root(a)ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/zfree 3591:0x0000000000446425
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 --del probe_3591:zfree
Remove event: probe_3591:zfree
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 write(a)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(a)ABCD]# perf probe --list
probe_3591:write (on 3591:0x00000036010c6060)
[root(a)ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/write 3591:0x00000036010c6060
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 --del probe_3591:write
Remove event: probe_3591:write
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 write@/lib64/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(a)ABCD]# perf probe --list
probe_3591:write (on 3591:0x00000036010c6060)
[root(a)ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/write 3591:0x00000036010c6060
[root(a)ABCD]# 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(a)ABCD]# perf probe -p 3591 --del probe_3591:write
Remove event: probe_3591:write
[root(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]# perf report --sort comm,dso
# Samples: 628
#
# Overhead Command Shared Object
# ........ ............... .............
#
100.00% zsh zsh


[root(a)ABCD]# perf probe --list
probe_3591:zfree_446420 (on 3591:0x0000000000446420)
[root(a)ABCD]# perf list | grep probe
probe_3591:zfree_446420 [Tracepoint event]
[root(a)ABCD]# perf probe -p 3591 --del probe_3591:zfree_446420
Remove event: probe_3591:zfree_446420
[root(a)ABCD]#


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(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]#

[ Probing using a function without specifying a dso (corresponding to zfree)]
-------------------------------------------------------------------
[root(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]# 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(a)ABCD]#


tools/perf/builtin-probe.c | 12 +
tools/perf/builtin-top.c | 20 --
tools/perf/util/event.c | 20 ++
tools/perf/util/event.h | 1
tools/perf/util/probe-event.c | 395 +++++++++++++++++++++++++++++++++--------
tools/perf/util/probe-event.h | 10 +
6 files changed, 352 insertions(+), 106 deletions(-)


diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e1..cb915a5 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,6 +55,7 @@ static struct {
struct strlist *dellist;
struct line_range line_range;
int max_probe_points;
+ pid_t upid;
} params;


@@ -73,6 +74,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;
}
@@ -188,6 +190,8 @@ static const struct option options[] = {
OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
"Set how many probe points can be found for a probe."),
+ OPT_INTEGER('p', "pid", &params.upid,
+ "specify a pid for a uprobes based probe"),
OPT_END()
};

@@ -225,6 +229,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_err(" Error: Don't use --list with --line.\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",
@@ -233,7 +241,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");
@@ -248,7 +256,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/builtin-top.c b/tools/perf/builtin-top.c
index 1e8e92e..b513e40 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1082,26 +1082,6 @@ static void event__process_sample(const event_t *self,
}
}

-static int event__process(event_t *event, struct perf_session *session)
-{
- switch (event->header.type) {
- case PERF_RECORD_COMM:
- event__process_comm(event, session);
- break;
- case PERF_RECORD_MMAP:
- event__process_mmap(event, session);
- break;
- case PERF_RECORD_FORK:
- case PERF_RECORD_EXIT:
- event__process_task(event, session);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
struct mmap_data {
int counter;
void *base;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index d7f21d7..d93e0bb 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -552,6 +552,26 @@ int event__process_task(event_t *self, struct perf_session *session)
return 0;
}

+int event__process(event_t *event, struct perf_session *session)
+{
+ switch (event->header.type) {
+ case PERF_RECORD_COMM:
+ event__process_comm(event, session);
+ break;
+ case PERF_RECORD_MMAP:
+ event__process_mmap(event, session);
+ break;
+ case PERF_RECORD_FORK:
+ case PERF_RECORD_EXIT:
+ event__process_task(event, session);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
void thread__find_addr_map(struct thread *self,
struct perf_session *session, u8 cpumode,
enum map_type type, pid_t pid, u64 addr,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 887ee63..8e790da 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -154,6 +154,7 @@ int event__process_comm(event_t *self, struct perf_session *session);
int event__process_lost(event_t *self, struct perf_session *session);
int event__process_mmap(event_t *self, struct perf_session *session);
int event__process_task(event_t *self, struct perf_session *session);
+int event__process(event_t *event, struct perf_session *session);

struct addr_location;
int event__preprocess_sample(const event_t *self, struct perf_session *session,
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index db3d619..20aeb35 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -46,6 +46,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
@@ -72,6 +73,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 */
@@ -109,6 +111,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)
{
@@ -162,6 +190,14 @@ 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 supported.\n");
+ return -ENOSYS;
+ }
+ return convert_name_to_addr(pev);
+ }
+
fd = open_vmlinux();
if (fd < 0) {
if (need_dwarf) {
@@ -384,13 +420,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,
@@ -401,6 +431,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;
}

@@ -476,6 +509,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)
{
@@ -615,6 +751,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.");
@@ -624,6 +765,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;
}

@@ -775,7 +921,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++)
@@ -1066,10 +1213,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\n",
+ 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;

@@ -1088,7 +1247,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;
@@ -1100,7 +1259,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;

@@ -1194,7 +1357,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;
@@ -1205,8 +1368,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)
@@ -1217,16 +1385,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;
@@ -1291,36 +1472,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);
}
@@ -1330,6 +1501,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;
}
@@ -1439,7 +1635,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 */
@@ -1453,17 +1652,28 @@ 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;
+ }
+
+ tev->group = strdup(group);
+
+ 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,
@@ -1471,9 +1681,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
if (ret < 0)
break;
event = buf;
-
tev->event = strdup(event);
- tev->group = strdup(group);
+
if (tev->event == NULL || tev->group == NULL) {
ret = -ENOMEM;
break;
@@ -1571,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);
@@ -1598,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)
return ret;

@@ -1666,23 +1884,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;
@@ -1691,40 +1901,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, ':');
@@ -1736,15 +1950,36 @@ 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 5af3924..27f052c 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);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/