[PATCH v4 13/13] perf: perf interface for uprobes.

From: Srikar Dronamraju
Date: Tue May 18 2010 - 13:01:30 EST



This patch enhances perf probe to accept pid and user vaddr.
This patch provides very basic support for uprobes.

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.

TODO:
Rebase to -tip tree. (targeted for v5)
Update perf-probes.txt.
Global tracing.

Here is a terminal snapshot of placing, using and removing a probe on a
process with pid 3329 (corresponding to zsh)

[ Probing a function in the executable using function name ]
-------------------------------------------------------------
[root@ABCD]# perf probe -p 7057 zfree@zsh
Added new event:
probe_7057:zfree (on 0x446420)

You can now use it on all perf tools, such as:

perf record -e probe_7057:zfree -a sleep 1
[root@ABCD]# perf probe --list
probe_7057:zfree (on 7057:0x0000000000446420)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_7057/zfree 7057:0x0000000000446420
[root@ABCD]# perf record -f -e probe_7057: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@ABCD]# perf probe -p 7057 --del probe_7057:zfree
Remove event: probe_7057:zfree
[root@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@ABCD]# perf probe -p 7057 zfree@zsh+5
Added new event:
probe_7057:zfree (on 0x446425)

You can now use it on all perf tools, such as:

perf record -e probe_7057:zfree -a sleep 1
[root@ABCD]# perf probe --list
probe_7057:zfree (on 7057:0x0000000000446425)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_7057/zfree 7057:0x0000000000446425
[root@ABCD]# perf record -f -e probe_7057: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@ABCD]# perf probe -p 7057 --del probe_7057:zfree
Remove event: probe_7057:zfree
[root@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@ABCD]# perf probe -p 7057 write@xxxxxxxxxxx
Added new event:
probe_7057:write (on 0x36010c6060)

You can now use it on all perf tools, such as:

perf record -e probe_7057:write -a sleep 1
[root@ABCD]# perf probe --list
probe_7057:write (on 7057:0x00000036010c6060)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_7057/write 7057:0x00000036010c6060
[root@ABCD]# perf record -f -e probe_7057: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@ABCD]# perf probe -p 7057 --del probe_7057:write
Remove event: probe_7057:write
[root@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@ABCD]# perf probe -p 7057 write@/lib64/libc-2.5.so
Added new event:
probe_7057:write (on 0x36010c6060)

You can now use it on all perf tools, such as:

perf record -e probe_7057:write -a sleep 1
[root@ABCD]# perf probe --list
probe_7057:write (on 7057:0x00000036010c6060)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_7057/write 7057:0x00000036010c6060
[root@ABCD]# perf record -f -e probe_7057: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@ABCD]# perf probe -p 7057 --del probe_7057:write
Remove event: probe_7057:write
[root@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@ABCD]# perf probe -p 7057 0x0000000000446420
Added new event:
probe_7057:zfree_446420 (on 0x0000000000446420)

You can now use it on all perf tools, such as:

perf record -e probe_7057:zfree_446420 -a sleep 1

[root@ABCD]# perf record -e probe_7057: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@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@ABCD]# perf report --sort comm,dso
# Samples: 628
#
# Overhead Command Shared Object
# ........ ............... .............
#
100.00% zsh zsh


[root@ABCD]# perf probe --list
probe_7057:zfree_446420 (on 7057:0x0000000000446420)
[root@ABCD]# perf list | grep probe
probe_7057:zfree_446420 [Tracepoint event]
[root@ABCD]# perf probe -p 7057 --del probe_7057:zfree_446420
Remove event: probe_7057:zfree_446420
[root@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@ABCD]# perf probe -p 7057 0x00000036010c6060
dded new event:
probe_7057:__GI___libc_write_36010c6060 (on 0x00000036010c6060)

You can now use it on all perf tools, such as:

perf record -e probe_7057:__GI___libc_write_36010c6060 -a sleep 1

[root@ABCD]# perf record -f -e probe_7057:__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@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@ABCD]#

[ Probing using a function without specifying a dso (corresponding to zfree)]
-------------------------------------------------------------------
[root@ABCD]# perf probe -p 7057 zfree
Added new event:
probe_7057:zfree (on 0x0000000000446420)

You can now use it on all perf tools, such as:

perf record -e probe_7057:zfree -a sleep 1

[root@ABCD]# perf record -e probe_7057: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@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@ABCD]#

Signed-off-by: Srikar Dronamraju <srikar@xxxxxxxxxxxxxxxxxx>
---

tools/perf/builtin-probe.c | 38 +++++-
tools/perf/builtin-top.c | 20 ---
tools/perf/util/event.c | 20 +++
tools/perf/util/event.h | 1
tools/perf/util/probe-event.c | 237 +++++++++++++++++++++++++++++++++-------
tools/perf/util/probe-event.h | 9 +-
tools/perf/util/probe-finder.h | 1
7 files changed, 252 insertions(+), 74 deletions(-)


diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 152d6c9..ce59282 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,6 +55,7 @@ static struct {
bool force_add;
bool show_lines;
int nr_probe;
+ pid_t pid;
struct probe_point probes[MAX_PROBES];
struct strlist *dellist;
struct map_groups kmap_groups;
@@ -73,7 +74,7 @@ static void parse_probe_event(const char *str)
die("Too many probes (> %d) are specified.", MAX_PROBES);

/* Parse perf-probe event into probe_point */
- parse_perf_probe_event(str, pp, &session.need_dwarf);
+ parse_perf_probe_event(str, pp, &session.need_dwarf, session.pid);

pr_debug("%d arguments\n", pp->nr_args);
}
@@ -203,6 +204,8 @@ static const struct option options[] = {
"FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
"Show source code lines.", opt_show_lines),
#endif
+ OPT_INTEGER('p', "pid", &session.pid,
+ "specify a pid for a uprobes based probe"),
OPT_END()
};

@@ -258,12 +261,16 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
pr_warning(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
+ if (session.pid) {
+ pr_warning(" Error: Don't use --list with -pid.\n");
+ usage_with_options(probe_usage, options);
+ }
show_perf_probe_events();
return 0;
}

#ifndef NO_DWARF_SUPPORT
- if (session.show_lines) {
+ if (session.show_lines && !session.pid) {
if (session.nr_probe != 0 || session.dellist) {
pr_warning(" Error: Don't use --line with"
" --add/--del.\n");
@@ -283,12 +290,19 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
#endif

if (session.dellist) {
- del_trace_kprobe_events(session.dellist);
+ if (session.pid)
+ del_trace_uprobe_events(session.dellist);
+ else
+ del_trace_kprobe_events(session.dellist);
+
strlist__delete(session.dellist);
if (session.nr_probe == 0)
return 0;
}

+ if (session.pid)
+ goto end_dwarf;
+
/* Add probes */
init_vmlinux();

@@ -334,18 +348,19 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
break;
}
close(fd);
-
-end_dwarf:
#endif /* !NO_DWARF_SUPPORT */

+end_dwarf:
/* Synthesize probes without dwarf */
for (i = 0; i < session.nr_probe; i++) {
pp = &session.probes[i];
if (pp->found) /* This probe is already found. */
continue;

- evaluate_probe_point(pp);
- ret = synthesize_trace_kprobe_event(pp);
+ if (!session.pid)
+ evaluate_probe_point(pp);
+
+ ret = synthesize_trace_probe_event(pp);
if (ret == -E2BIG)
die("probe point definition becomes too long.");
else if (ret < 0)
@@ -353,8 +368,11 @@ end_dwarf:
}

/* Settng up probe points */
- add_trace_kprobe_events(session.probes, session.nr_probe,
- session.force_add);
+ if (session.pid)
+ add_trace_uprobe_events(session.probes, session.nr_probe,
+ session.force_add, session.pid);
+ else
+ add_trace_kprobe_events(session.probes, session.nr_probe,
+ session.force_add);
return 0;
}
-
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 1f52932..430d910 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1005,26 +1005,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 705ec63..e5190f2 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -432,6 +432,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, u64 addr,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index a33b949..282486b 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -128,6 +128,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 7c004b6..afa52a0 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -41,6 +41,7 @@
#include "color.h"
#include "parse-events.h" /* For debugfs_path */
#include "probe-event.h"
+#include "session.h"

#define MAX_CMDLEN 256
#define MAX_PROBE_ARGS 128
@@ -112,6 +113,79 @@ static bool check_event_name(const char *name)
return true;
}

+/*
+ * uprobe_events only accepts address:
+ * Convert function and any offset to address
+ */
+static void convert_name_to_addr(struct probe_point *pp)
+{
+ struct perf_session *session;
+ struct thread *thread;
+ struct symbol *sym;
+ struct map *map;
+ char *name;
+ 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);
+ DIE_IF(session == NULL);
+ symbol_conf.try_vmlinux_path = false;
+ if (!vaddr)
+ symbol_conf.sort_by_name = true;
+ if (symbol__init() < 0)
+ semantic_error("Cannot initialize symbols.");
+
+ event__synthesize_thread(pp->upid, event__process, session);
+
+ thread = perf_session__findnew(session, pp->upid);
+ DIE_IF(thread == NULL);
+
+ if (vaddr) {
+ if (pp->event)
+ return;
+
+ pp->event = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
+ sym = map_groups__find_function(&thread->mg, vaddr, NULL);
+ if (!sym)
+ snprintf(pp->event, MAX_PROBE_ARGS, "p_%llx", vaddr);
+ else
+ snprintf(pp->event, MAX_PROBE_ARGS, "%s_%llx",
+ sym->name, vaddr);
+ return;
+ }
+
+ if (!pp->file)
+ /* Lets find the function in the executable. */
+ name = strdup(thread->comm);
+ else
+ name = basename(make_absolute_path(pp->file));
+
+ DIE_IF(name == NULL);
+
+ map = map_groups__find_by_name(&thread->mg, MAP__FUNCTION, name);
+ if (!map)
+ semantic_error("Cannot find appropriate DSO.");
+
+ sym = map__find_symbol_by_name(map, pp->function, NULL);
+ if (!sym)
+ semantic_error("Cannot find appropriate Symbol.");
+
+ if (map->start > sym->start)
+ vaddr = map->start;
+ vaddr += sym->start + pp->offset + map->pgoff;
+ pp->offset = 0;
+
+ if (!pp->event)
+ pp->event = pp->function;
+ else
+ free(pp->function);
+ pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
+ if (!pp->function)
+ die("Failed to allocate memory by zalloc.");
+ e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
+}
+
/* Parse probepoint definition. */
static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
{
@@ -166,7 +240,8 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
*ptr++ = '\0';
}
switch (c) {
- case ':': /* Line number */
+ case ':':
+ /* Line number */
pp->line = strtoul(arg, &tmp, 0);
if (*tmp != '\0')
semantic_error("There is non-digit char"
@@ -216,6 +291,9 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
if (pp->retprobe && !pp->function)
semantic_error("Return probe requires an entry function.");

+ if (pp->upid && !pp->function)
+ semantic_error("No function specified for uprobes");
+
if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe)
semantic_error("Offset/Line/Lazy pattern can't be used with "
"return probe.");
@@ -223,11 +301,14 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n",
pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
pp->lazy_line);
+
+ if (pp->upid)
+ convert_name_to_addr(pp);
}

/* Parse perf-probe event definition */
void parse_perf_probe_event(const char *str, struct probe_point *pp,
- bool *need_dwarf)
+ bool *need_dwarf, pid_t pid)
{
char **argv;
int argc, i;
@@ -241,6 +322,7 @@ void parse_perf_probe_event(const char *str, struct probe_point *pp,
semantic_error("Too many arguments");

/* Parse probe point */
+ pp->upid = pid;
parse_perf_probe_probepoint(argv[0], pp);
if (pp->file || pp->line || pp->lazy_line)
*need_dwarf = true;
@@ -263,15 +345,15 @@ void parse_perf_probe_event(const char *str, struct probe_point *pp,
argv_free(argv);
}

-/* Parse kprobe_events event into struct probe_point */
-void parse_trace_kprobe_event(const char *str, struct probe_point *pp)
+/* Parse kprobe_events (uprobe_events) event into struct probe_point */
+void parse_trace_probe_event(const char *str, struct probe_point *pp)
{
char pr;
char *p;
int ret, i, argc;
char **argv;

- pr_debug("Parsing kprobe_events: %s\n", str);
+ pr_debug("Parsing probe_events: %s\n", str);
argv = argv_split(str, &argc);
if (!argv)
die("argv_split failed.");
@@ -375,7 +457,7 @@ error:
return ret;
}

-int synthesize_trace_kprobe_event(struct probe_point *pp)
+int synthesize_trace_probe_event(struct probe_point *pp)
{
char *buf;
int i, len, ret;
@@ -383,7 +465,11 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
pp->probes[0] = buf = zalloc(MAX_CMDLEN);
if (!buf)
die("Failed to allocate memory by zalloc.");
- ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
+ if (pp->offset)
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function,
+ pp->offset);
+ else
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s", pp->function);
if (ret <= 0)
goto error;
len = ret;
@@ -426,8 +512,8 @@ static int open_kprobe_events(int flags, int mode)
return ret;
}

-/* Get raw string list of current kprobe_events */
-static struct strlist *get_trace_kprobe_event_rawlist(int fd)
+/* Get raw string list of current kprobe_events or uprobe_events */
+static struct strlist *get_trace_probe_event_rawlist(int fd)
{
int ret, idx;
FILE *fp;
@@ -455,6 +541,27 @@ static struct strlist *get_trace_kprobe_event_rawlist(int fd)
return sl;
}

+static int open_uprobe_events(int flags, int mode)
+{
+ char buf[PATH_MAX];
+ int ret;
+
+ ret = e_snprintf(buf, PATH_MAX, "%s/../uprobe_events", debugfs_path);
+ if (ret < 0)
+ die("Failed to make uprobe_events path.");
+
+ ret = open(buf, flags, mode);
+ if (ret < 0) {
+ if (errno == ENOENT)
+ die("uprobe_events file does not exist -"
+ " please rebuild with CONFIG_UPROBE_EVENT.");
+ else
+ die("Could not open uprobe_events file: %s",
+ strerror(errno));
+ }
+ return ret;
+}
+
/* Free and zero clear probe_point */
static void clear_probe_point(struct probe_point *pp)
{
@@ -500,9 +607,8 @@ static void show_perf_probe_event(const char *event, const char *place,
}

/* List up current perf-probe events */
-void show_perf_probe_events(void)
+static void __show_perf_probe_events(int fd)
{
- int fd;
struct probe_point pp;
struct strlist *rawlist;
struct str_node *ent;
@@ -510,22 +616,31 @@ void show_perf_probe_events(void)
setup_pager();
memset(&pp, 0, sizeof(pp));

- fd = open_kprobe_events(O_RDONLY, 0);
- rawlist = get_trace_kprobe_event_rawlist(fd);
- close(fd);
-
+ rawlist = get_trace_probe_event_rawlist(fd);
strlist__for_each(ent, rawlist) {
- parse_trace_kprobe_event(ent->s, &pp);
+ parse_trace_probe_event(ent->s, &pp);
/* Synthesize only event probe point */
synthesize_perf_probe_point(&pp);
/* Show an event */
show_perf_probe_event(pp.event, pp.probes[0], &pp);
clear_probe_point(&pp);
}
-
strlist__delete(rawlist);
}

+void show_perf_probe_events(void)
+{
+ int fd;
+
+ fd = open_kprobe_events(O_RDONLY, 0);
+ __show_perf_probe_events(fd);
+ close(fd);
+
+ fd = open_uprobe_events(O_RDONLY, 0);
+ __show_perf_probe_events(fd);
+ close(fd);
+}
+
/* Get current perf-probe event names */
static struct strlist *get_perf_event_names(int fd, bool include_group)
{
@@ -535,11 +650,11 @@ static struct strlist *get_perf_event_names(int fd, bool include_group)
struct probe_point pp;

memset(&pp, 0, sizeof(pp));
- rawlist = get_trace_kprobe_event_rawlist(fd);
+ rawlist = get_trace_probe_event_rawlist(fd);

sl = strlist__new(true, NULL);
strlist__for_each(ent, rawlist) {
- parse_trace_kprobe_event(ent->s, &pp);
+ parse_trace_probe_event(ent->s, &pp);
if (include_group) {
if (e_snprintf(buf, 128, "%s:%s", pp.group,
pp.event) < 0)
@@ -555,7 +670,7 @@ static struct strlist *get_perf_event_names(int fd, bool include_group)
return sl;
}

-static void write_trace_kprobe_event(int fd, const char *buf)
+static void write_trace_probe_event(int fd, const char *buf)
{
int ret;

@@ -566,11 +681,10 @@ static void write_trace_kprobe_event(int fd, const char *buf)
}

static void get_new_event_name(char *buf, size_t len, const char *base,
- struct strlist *namelist, bool allow_suffix)
+ struct strlist *namelist, bool allow_suffix)
{
int i, ret;

- /* Try no suffix */
ret = e_snprintf(buf, len, "%s", base);
if (ret < 0)
die("snprintf() failed: %s", strerror(-ret));
@@ -595,38 +709,54 @@ static void get_new_event_name(char *buf, size_t len, const char *base,
die("Too many events are on the same function.");
}

-void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
- bool force_add)
+static void add_trace_probe_events(int fd, struct probe_point *probes,
+ int nr_probes, bool force_add, pid_t pid)
{
- int i, j, fd;
+ int i, j;
struct probe_point *pp;
char buf[MAX_CMDLEN];
+ char tempbuf[MAX_CMDLEN];
char event[64];
struct strlist *namelist;
bool allow_suffix;

- fd = open_kprobe_events(O_RDWR, O_APPEND);
/* Get current event names */
namelist = get_perf_event_names(fd, false);

for (j = 0; j < nr_probes; j++) {
pp = probes + j;
+ pp->upid = pid;
+ if (pid)
+ snprintf(tempbuf, MAX_CMDLEN, "%d:", pid);
if (!pp->event)
pp->event = strdup(pp->function);
- if (!pp->group)
- pp->group = strdup(PERFPROBE_GROUP);
+ if (!pp->group) {
+ if (!pid)
+ pp->group = strdup(PERFPROBE_GROUP);
+ else {
+ /*
+ * For uprobes based probes create a group
+ * probe_<pid>.
+ */
+ pp->group = zalloc(sizeof(char *)
+ * MAX_PROBE_ARGS);
+ snprintf(pp->group, MAX_PROBE_ARGS, "%s_%d",
+ PERFPROBE_GROUP, pid);
+ }
+ }
DIE_IF(!pp->event || !pp->group);
/* If force_add is true, suffix search is allowed */
allow_suffix = force_add;
for (i = 0; i < pp->found; i++) {
/* Get an unused new event name */
get_new_event_name(event, 64, pp->event, namelist,
- allow_suffix);
- snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n",
+ allow_suffix);
+ snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s\n",
pp->retprobe ? 'r' : 'p',
pp->group, event,
+ pp->upid ? tempbuf : " ",
pp->probes[i]);
- write_trace_kprobe_event(fd, buf);
+ write_trace_probe_event(fd, buf);
printf("Added new event:\n");
/* Get the first parameter (probe-point) */
sscanf(pp->probes[i], "%s", buf);
@@ -647,10 +777,25 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event);

strlist__delete(namelist);
+}
+
+void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
+ bool force_add)
+{
+ int fd = open_kprobe_events(O_RDWR, O_APPEND);
+ add_trace_probe_events(fd, probes, nr_probes, force_add, 0);
+ close(fd);
+}
+
+void add_trace_uprobe_events(struct probe_point *probes, int nr_probes,
+ bool force_add, pid_t pid)
+{
+ int fd = open_uprobe_events(O_RDWR, O_APPEND);
+ add_trace_probe_events(fd, probes, nr_probes, force_add, pid);
close(fd);
}

-static void __del_trace_kprobe_event(int fd, struct str_node *ent)
+static void __del_trace_probe_event(int fd, struct str_node *ent)
{
char *p;
char buf[128];
@@ -663,11 +808,11 @@ static void __del_trace_kprobe_event(int fd, struct str_node *ent)
die("Internal error: %s should have ':' but not.", ent->s);
*p = '/';

- write_trace_kprobe_event(fd, buf);
+ write_trace_probe_event(fd, buf);
printf("Remove event: %s\n", ent->s);
}

-static void del_trace_kprobe_event(int fd, const char *group,
+static void del_trace_probe_event(int fd, const char *group,
const char *event, struct strlist *namelist)
{
char buf[128];
@@ -681,14 +826,14 @@ static void del_trace_kprobe_event(int fd, const char *group,
strlist__for_each_safe(ent, n, namelist)
if (strglobmatch(ent->s, buf)) {
found++;
- __del_trace_kprobe_event(fd, ent);
+ __del_trace_probe_event(fd, ent);
strlist__remove(namelist, ent);
}
} else {
ent = strlist__find(namelist, buf);
if (ent) {
found++;
- __del_trace_kprobe_event(fd, ent);
+ __del_trace_probe_event(fd, ent);
strlist__remove(namelist, ent);
}
}
@@ -696,16 +841,13 @@ static void del_trace_kprobe_event(int fd, const char *group,
pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf);
}

-void del_trace_kprobe_events(struct strlist *dellist)
+static void del_trace_probe_events(int fd, struct strlist *dellist)
{
- int fd;
const char *group, *event;
char *p, *str;
struct str_node *ent;
struct strlist *namelist;

- fd = open_kprobe_events(O_RDWR, O_APPEND);
- /* Get current event names */
namelist = get_perf_event_names(fd, true);

strlist__for_each(ent, dellist) {
@@ -723,10 +865,23 @@ void del_trace_kprobe_events(struct strlist *dellist)
event = str;
}
pr_debug("Group: %s, Event: %s\n", group, event);
- del_trace_kprobe_event(fd, group, event, namelist);
+ del_trace_probe_event(fd, group, event, namelist);
free(str);
}
strlist__delete(namelist);
+}
+
+void del_trace_kprobe_events(struct strlist *dellist)
+{
+ int fd = open_kprobe_events(O_RDWR, O_APPEND);
+ del_trace_probe_events(fd, dellist);
+ close(fd);
+}
+
+void del_trace_uprobe_events(struct strlist *dellist)
+{
+ int fd = open_uprobe_events(O_RDWR, O_APPEND);
+ del_trace_probe_events(fd, dellist);
close(fd);
}

diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 711287d..bcb7ab2 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -7,14 +7,17 @@

extern void parse_line_range_desc(const char *arg, struct line_range *lr);
extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
- bool *need_dwarf);
+ bool *need_dwarf, pid_t pid);
extern int synthesize_perf_probe_point(struct probe_point *pp);
extern int synthesize_perf_probe_event(struct probe_point *pp);
-extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp);
-extern int synthesize_trace_kprobe_event(struct probe_point *pp);
+extern int synthesize_trace_probe_event(struct probe_point *pp);
+extern void parse_trace_probe_event(const char *str, struct probe_point *pp);
extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
bool force_add);
extern void del_trace_kprobe_events(struct strlist *dellist);
+extern void add_trace_uprobe_events(struct probe_point *probes, int nr_probes,
+ bool force_add, pid_t pid);
+extern void del_trace_uprobe_events(struct strlist *dellist);
extern void show_perf_probe_events(void);
extern void show_line_range(struct line_range *lr);

diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 21f7354..b4d8ebc 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -33,6 +33,7 @@ struct probe_point {

/* Output */
int found; /* Number of found probe points */
+ pid_t upid; /* uprobes only */
char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/
};

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