[PATCH perf/core 5/8] perf probe: Allow to use filter on --del command

Masami Hiramatsu
2015-04-21T11:45:39Z

This makes perf-probe --del option to accept filter rules
not only simple glob pattern. This simplifies the code and
improve the flexibility.

E.g. if we remove 2 different pattern events, we need 2
-d options.
----
# ./perf probe -d vfs\* -d malloc
Removed event: probe_libc:malloc
Removed event: probe:vfs_read
----

This allows you to joint the 2 patterns with '|'.

----
# ./perf probe -d 'vfs*|malloc'
Removed event: probe:vfs_read
Removed event: probe_libc:malloc
----

Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
---
tools/perf/builtin-probe.c | 16 +++----
tools/perf/util/probe-event.c | 89 +++++++++++------------------------------
tools/perf/util/probe-event.h | 2 -
tools/perf/util/strfilter.c | 66 ++++++++++++++++++++++++++++++
tools/perf/util/strfilter.h | 9 ++++
5 files changed, 106 insertions(+), 76 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index feca316..1f41b4e 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,12 +55,12 @@ static struct {
bool show_ext_vars;
bool show_funcs;
bool mod_events;
+ bool del_events;
bool uprobes;
bool quiet;
bool target_used;
int nevents;
struct perf_probe_event events[MAX_PROBES];
- struct strlist *dellist;
struct line_range line_range;
char *target;
int max_probe_points;
@@ -195,10 +195,8 @@ static int opt_del_probe_event(const struct option *opt __maybe_unused,
const char *str, int unset __maybe_unused)
{
if (str) {
- params.mod_events = true;
- if (!params.dellist)
- params.dellist = strlist__new(true, NULL);
- strlist__add(params.dellist, str);
+ params.del_events = true;
+ return params_add_filter(str);
}
return 0;
}
@@ -313,8 +311,6 @@ static void cleanup_params(void)

for (i = 0; i < params.nevents; i++)
clear_perf_probe_event(params.events + i);
- if (params.dellist)
- strlist__delete(params.dellist);
line_range__clear(&params.line_range);
free(params.target);
if (params.filter)
@@ -454,7 +450,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
if (params.max_probe_points == 0)
params.max_probe_points = MAX_PROBES;

- if ((!params.nevents && !params.dellist && !params.list_events &&
+ if ((!params.nevents && !params.del_events && !params.list_events &&
!params.show_lines && !params.show_funcs))
usage_with_options(probe_usage, options);

@@ -514,8 +510,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
}
#endif

- if (params.dellist) {
- ret = del_perf_probe_events(params.dellist);
+ if (params.del_events) {
+ ret = del_perf_probe_events(params.filter);
if (ret < 0) {
pr_err_with_code(" Error: Failed to delete events.", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 38bb282..2b08992 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2701,40 +2701,36 @@ error:
return ret;
}

-static int del_trace_probe_event(int fd, const char *buf,
- struct strlist *namelist)
+static int del_trace_probe_events(int fd, struct strfilter *filter,
+ struct strlist *namelist)
{
- struct str_node *ent, *n;
+ struct str_node *ent;
+ const char *p;
int ret = -1;

- if (strpbrk(buf, "*?")) { /* Glob-exp */
- strlist__for_each_safe(ent, n, namelist)
- if (strglobmatch(ent->s, buf)) {
- ret = __del_trace_probe_event(fd, ent);
- if (ret < 0)
- break;
- strlist__remove(namelist, ent);
- }
- } else {
- ent = strlist__find(namelist, buf);
- if (ent) {
+ if (!namelist)
+ return -ENOENT;
+
+ strlist__for_each(ent, namelist) {
+ p = strchr(ent->s, ':');
+ if ((p && strfilter__compare(filter, p + 1)) ||
+ strfilter__compare(filter, ent->s)) {
ret = __del_trace_probe_event(fd, ent);
- if (ret >= 0)
- strlist__remove(namelist, ent);
+ if (ret < 0)
+ break;
}
}

return ret;
}

-int del_perf_probe_events(struct strlist *dellist)
+int del_perf_probe_events(struct strfilter *filter)
{
- int ret = -1, ret2, ufd = -1, kfd = -1;
- char buf[128];
- const char *group, *event;
- char *p, *str;
- struct str_node *ent;
+ int ret = 0, ufd = -1, kfd = -1;
struct strlist *namelist = NULL, *unamelist = NULL;
+ char *str = strfilter__string(filter);
+
+ pr_debug("Filter: \'%s\'\n", str);

/* Get current event names */
kfd = open_kprobe_events(true);
@@ -2747,53 +2743,15 @@ int del_perf_probe_events(struct strlist *dellist)

if (kfd < 0 && ufd < 0) {
print_both_open_warning(kfd, ufd);
+ ret = kfd;
goto error;
}

- if (namelist == NULL && unamelist == NULL) {
- ret = -ENOENT;
- goto error;
- }
-
- strlist__for_each(ent, dellist) {
- str = strdup(ent->s);
- if (str == NULL) {
- ret = -ENOMEM;
- goto error;
- }
- pr_debug("Parsing: %s\n", str);
- p = strchr(str, ':');
- if (p) {
- group = str;
- *p = '\0';
- event = p + 1;
- } else {
- 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 = ret2 = -ENOENT;
- if (namelist)
- ret = del_trace_probe_event(kfd, buf, namelist);
-
- if (unamelist)
- ret2 = del_trace_probe_event(ufd, buf, unamelist);
-
- /* Since we can remove probes which already removed, no error */
- if (ret != 0 && ret2 != 0)
- pr_debug("Event \"%s\" does not exist.\n", buf);
+ ret = del_trace_probe_events(kfd, filter, namelist);
+ if (del_trace_probe_events(ufd, filter, unamelist) < 0 && ret < 0) {
+ pr_debug("\"%s\" does not hit any event.\n", str);
+ /* Note that this is silently ignored */
ret = 0;
-
- free(str);
}

error:
@@ -2806,6 +2764,7 @@ error:
strlist__delete(unamelist);
close(ufd);
}
+ free(str);

return ret;
}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 819e825..027356f 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -126,7 +126,7 @@ extern const char *kernel_get_module_path(const char *module);

extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
int max_probe_points, bool force_add);
-extern int del_perf_probe_events(struct strlist *dellist);
+extern int del_perf_probe_events(struct strfilter *filter);
extern int show_perf_probe_events(struct strfilter *filter);
extern int show_line_range(struct line_range *lr, const char *module,
bool user);
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c
index cd659e6..9206618 100644
--- a/tools/perf/util/strfilter.c
+++ b/tools/perf/util/strfilter.c
@@ -231,3 +231,69 @@ bool strfilter__compare(struct strfilter *filter, const char *str)
return false;
return strfilter_node__compare(filter->root, str);
}
+
+static int strfilter_node__sprint(struct strfilter_node *node, char *buf);
+
+/* sprint node in parenthesis if needed */
+static int strfilter_node__sprint_pt(struct strfilter_node *node, char *buf)
+{
+ int len;
+ int pt = node->r ? 2 : 0; /* don't need to check node->l */
+
+ if (buf && pt)
+ *buf++ = '(';
+ len = strfilter_node__sprint(node, buf);
+ if (len < 0)
+ return len;
+ if (buf && pt)
+ *(buf + len) = ')';
+ return len + pt;
+}
+
+static int strfilter_node__sprint(struct strfilter_node *node, char *buf)
+{
+ int len = 0, rlen;
+
+ if (!node || !node->p)
+ return -EINVAL;
+
+ switch (*node->p) {
+ case '|':
+ case '&':
+ len = strfilter_node__sprint_pt(node->l, buf);
+ if (len < 0)
+ return len;
+ case '!':
+ if (buf) {
+ *(buf + len++) = *node->p;
+ buf += len;
+ } else
+ len++;
+ rlen = strfilter_node__sprint_pt(node->r, buf);
+ if (rlen < 0)
+ return rlen;
+ len += rlen;
+ break;
+ default:
+ len = strlen(node->p);
+ if (buf)
+ strcpy(buf, node->p);
+ }
+
+ return len;
+}
+
+char *strfilter__string(struct strfilter *filter)
+{
+ int len;
+ char *ret = NULL;
+
+ len = strfilter_node__sprint(filter->root, NULL);
+ if (len < 0)
+ return NULL;
+
+ ret = malloc(len + 1);
+ strfilter_node__sprint(filter->root, ret);
+
+ return ret;
+}
diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h
index c81ff97..d0b772c 100644
--- a/tools/perf/util/strfilter.h
+++ b/tools/perf/util/strfilter.h
@@ -58,4 +58,13 @@ bool strfilter__compare(struct strfilter *filter, const char *str);
*/
void strfilter__delete(struct strfilter *filter);

+/**
+ * strfilter__string - Reconstruct a rule string from filter
+ * @filter: String filter to reconstruct
+ *
+ * Reconstruct a rule string from @filter. This will be good for
+ * debug messages. Note that returning string must be freed afterward.
+ */
+char *strfilter__string(struct strfilter *filter);
+
#endif