[RFC PATCH 5/5] tracing: Add generic event-name based remove event method

From: Masami Hiramatsu
Date: Thu Sep 27 2018 - 12:01:21 EST


Add a generic method to remove event from dynamic event
list. This is same as other system under ftrace. You
just need to pass the event name with '!' prefix, e.g.

# echo p:new_grp/new_event _do_fork > dynamic_events

This creates an event, and

# echo '!new_grp/new_event' > dynamic_events

Or,

# echo '!new_event' > dynamic_events

will remove new_grp/new_event event.

Signed-off-by: Masami Hiramatsu <mhiramat@xxxxxxxxxx>
---
kernel/trace/trace_dynevent.c | 36 +++++++++++++++++++++++++++++++++++-
kernel/trace/trace_dynevent.h | 2 ++
kernel/trace/trace_kprobe.c | 12 ++++++++++++
kernel/trace/trace_uprobe.c | 12 ++++++++++++
4 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c
index c829742cfe5d..c33551ad0b15 100644
--- a/kernel/trace/trace_dynevent.c
+++ b/kernel/trace/trace_dynevent.c
@@ -19,7 +19,8 @@ static LIST_HEAD(dyn_event_ops_list);

int dyn_event_register(struct dyn_event_operations *ops)
{
- if (!ops || !ops->create || !ops->show || !ops->is_busy || !ops->free)
+ if (!ops || !ops->create || !ops->show || !ops->is_busy ||
+ !ops->free || !ops->match)
return -EINVAL;

INIT_LIST_HEAD(&ops->list);
@@ -29,11 +30,19 @@ int dyn_event_register(struct dyn_event_operations *ops)
return 0;
}

+static int delete_dyn_event(int argc, char **argv);
+
static int create_dyn_event(int argc, char **argv)
{
struct dyn_event_operations *ops;
int ret;

+ if (argc == 0)
+ return 0;
+
+ if (argv[0][0] == '!')
+ return delete_dyn_event(argc, argv);
+
mutex_lock(&dyn_event_ops_mutex);
list_for_each_entry(ops, &dyn_event_ops_list, list) {
ret = ops->create(argc, argv);
@@ -80,6 +89,31 @@ static const struct seq_operations dyn_event_seq_op = {
.show = dyn_event_seq_show
};

+static int delete_dyn_event(int argc, char **argv)
+{
+ struct dyn_event *pos, *n;
+ char *system = NULL, *event, *p;
+ int ret = -ENOENT;
+
+ event = &argv[0][1];
+ p = strchr(event, '/');
+ if (p) {
+ system = event;
+ event = p + 1;
+ *p = '\0';
+ }
+ mutex_lock(&dyn_event_mutex);
+ for_each_dyn_event_safe(pos, n) {
+ if (pos->ops->match(system, event, pos)) {
+ ret = pos->ops->free(pos);
+ break;
+ }
+ }
+ mutex_unlock(&dyn_event_mutex);
+
+ return ret;
+}
+
static int release_all_dyn_events(void)
{
struct dyn_event *ev, *tmp;
diff --git a/kernel/trace/trace_dynevent.h b/kernel/trace/trace_dynevent.h
index 96cf9ca7adb9..705c95b435a3 100644
--- a/kernel/trace/trace_dynevent.h
+++ b/kernel/trace/trace_dynevent.h
@@ -20,6 +20,8 @@ struct dyn_event_operations {
int (*show)(struct seq_file *m, struct dyn_event *ev);
bool (*is_busy)(struct dyn_event *ev);
int (*free)(struct dyn_event *ev);
+ bool (*match)(const char *system, const char *event,
+ struct dyn_event *ev);
};

/* Register new dyn_event type -- must be called at first */
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 421b9d71fedf..b1602f3584d5 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -23,12 +23,15 @@ static int trace_kprobe_create(int argc, char **argv);
static int trace_kprobe_show(struct seq_file *m, struct dyn_event *ev);
static int trace_kprobe_release(struct dyn_event *ev);
static bool trace_kprobe_is_busy(struct dyn_event *ev);
+static bool trace_kprobe_match(const char *system, const char *event,
+ struct dyn_event *ev);

static struct dyn_event_operations trace_kprobe_ops = {
.create = trace_kprobe_create,
.show = trace_kprobe_show,
.is_busy = trace_kprobe_is_busy,
.free = trace_kprobe_release,
+ .match = trace_kprobe_match,
};

/**
@@ -115,6 +118,15 @@ static bool trace_kprobe_is_busy(struct dyn_event *ev)
return trace_probe_is_enabled(&tk->tp);
}

+static bool trace_kprobe_match(const char *system, const char *event,
+ struct dyn_event *ev)
+{
+ struct trace_kprobe *tk = to_trace_kprobe(ev);
+
+ return strcmp(trace_event_name(&tk->tp.call), event) == 0 &&
+ (!system || strcmp(tk->tp.call.class->system, system) == 0);
+}
+
static nokprobe_inline unsigned long trace_kprobe_nhit(struct trace_kprobe *tk)
{
unsigned long nhit = 0;
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 90d10ef02f6b..7c15e22098af 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -41,12 +41,15 @@ static int trace_uprobe_create(int argc, char **argv);
static int trace_uprobe_show(struct seq_file *m, struct dyn_event *ev);
static int trace_uprobe_release(struct dyn_event *ev);
static bool trace_uprobe_is_busy(struct dyn_event *ev);
+static bool trace_uprobe_match(const char *system, const char *event,
+ struct dyn_event *ev);

static struct dyn_event_operations trace_uprobe_ops = {
.create = trace_uprobe_create,
.show = trace_uprobe_show,
.is_busy = trace_uprobe_is_busy,
.free = trace_uprobe_release,
+ .match = trace_uprobe_match,
};

/*
@@ -276,6 +279,15 @@ static bool trace_uprobe_is_busy(struct dyn_event *ev)
return trace_probe_is_enabled(&tu->tp);
}

+static bool trace_uprobe_match(const char *system, const char *event,
+ struct dyn_event *ev)
+{
+ struct trace_uprobe *tu = to_trace_uprobe(ev);
+
+ return strcmp(trace_event_name(&tu->tp.call), event) == 0 &&
+ (!system || strcmp(tu->tp.call.class->system, system) == 0);
+}
+
/*
* Allocate new trace_uprobe and initialize it (including uprobes).
*/