[PATCH v2 5/5] tracing: Support enable/disable multiple eventstrigger by wild cards

From: Masami Hiramatsu
Date: Tue May 21 2013 - 22:22:31 EST


Support enable/disable multiple events trigger on ftrace
by using wild cards. This makes enabling multiple events
at once easy.

e.g.)
# echo vfs_symlink:enable_event:\*:\*rq\* > set_ftrace_filter

# cat set_ftrace_filter
#### all functions enabled ####
vfs_symlink:enable_event:*:*rq*:unlimited

# grep 0\\* -r events/*/*/enable
events/block/block_getrq/enable:0*
events/block/block_rq_abort/enable:0*
events/block/block_rq_complete/enable:0*
events/block/block_rq_insert/enable:0*
events/block/block_rq_issue/enable:0*
events/block/block_rq_remap/enable:0*
events/block/block_rq_requeue/enable:0*
events/block/block_sleeprq/enable:0*
events/irq/irq_handler_entry/enable:0*
events/irq/irq_handler_exit/enable:0*
events/irq/softirq_entry/enable:0*
events/irq/softirq_exit/enable:0*
events/irq/softirq_raise/enable:0*

Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>
Cc: Steven Rostedt <rostedt@xxxxxxxxxxx>
Cc: Frederic Weisbecker <fweisbec@xxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
---
Documentation/trace/ftrace.txt | 12 ++--
kernel/trace/trace_events.c | 123 +++++++++++++++++++++++++++++-----------
2 files changed, 95 insertions(+), 40 deletions(-)

diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
index bfe8c29..92ca5236 100644
--- a/Documentation/trace/ftrace.txt
+++ b/Documentation/trace/ftrace.txt
@@ -2407,11 +2407,11 @@ The following commands are supported:
echo '!native_flush_tlb_others:snapshot:0' > set_ftrace_filter

- enable_event/disable_event
- These commands can enable or disable a trace event. Note, because
+ These commands can enable or disable trace events. Note, because
function tracing callbacks are very sensitive, when these commands
- are registered, the trace point is activated, but disabled in
- a "soft" mode. That is, the tracepoint will be called, but
- just will not be traced. The event tracepoint stays in this mode
+ are registered, the tracepoints are activated, but disabled in
+ a "soft" mode. That is, the tracepoints will be called, but
+ just will not be traced. The event tracepoints stay in this mode
as long as there's a command that triggers it.

echo 'try_to_wake_up:enable_event:sched:sched_switch:2' > \
@@ -2422,8 +2422,10 @@ The following commands are supported:
<function>:enable_event:<system>:<event>[:count]
<function>:disable_event:<system>:<event>[:count]

- To remove the events commands:
+ Note that the system and event accept wildcards for operating
+ multiple events at once.

+ To remove the events commands:

echo '!try_to_wake_up:enable_event:sched:sched_switch:0' > \
set_ftrace_filter
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 27963e2..1376bb4 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1850,17 +1850,21 @@ __trace_add_event_dirs(struct trace_array *tr)
#define DISABLE_EVENT_STR "disable_event"

struct event_probe_data {
- struct ftrace_event_file *file;
+ struct ftrace_event_file **files;
+ char *event;
+ char *system;
unsigned long count;
int ref;
bool enable;
};

-static struct ftrace_event_file *
-find_event_file(struct trace_array *tr, const char *system, const char *event)
+static int
+find_event_files(struct trace_array *tr, const char *system, const char *event,
+ struct ftrace_event_file **files, int size)
{
struct ftrace_event_file *file;
struct ftrace_event_call *call;
+ int nr = 0;

list_for_each_entry(file, &tr->events, list) {

@@ -1872,11 +1876,14 @@ find_event_file(struct trace_array *tr, const char *system, const char *event)
if (call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)
continue;

- if (strcmp(event, call->name) == 0 &&
- strcmp(system, call->class->system) == 0)
- return file;
+ if (strglobmatch(event, call->name) &&
+ strglobmatch(system, call->class->system)) {
+ if (files && nr < size)
+ files[nr] = file;
+ nr++;
+ }
}
- return NULL;
+ return nr;
}

static void
@@ -1884,14 +1891,24 @@ event_enable_probe(unsigned long ip, unsigned long parent_ip, void **_data)
{
struct event_probe_data **pdata = (struct event_probe_data **)_data;
struct event_probe_data *data = *pdata;
+ struct ftrace_event_file **file;

if (!data)
return;

- if (data->enable)
- clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &data->file->flags);
- else
- set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &data->file->flags);
+ file = data->files;
+ if (unlikely(!file))
+ return;
+
+ while (*file) {
+ if (data->enable)
+ clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT,
+ &(*file)->flags);
+ else
+ set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT,
+ &(*file)->flags);
+ file++;
+ }
}

static void
@@ -1906,10 +1923,6 @@ event_enable_count_probe(unsigned long ip, unsigned long parent_ip, void **_data
if (!data->count)
return;

- /* Skip if the event is in a state we want to switch to */
- if (data->enable == !(data->file->flags & FTRACE_EVENT_FL_SOFT_DISABLED))
- return;
-
if (data->count != -1)
(data->count)--;

@@ -1926,8 +1939,7 @@ event_enable_print(struct seq_file *m, unsigned long ip,

seq_printf(m, "%s:%s:%s",
data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR,
- data->file->event_call->class->system,
- data->file->event_call->name);
+ data->system, data->event);

if (data->count == -1)
seq_printf(m, ":unlimited\n");
@@ -1948,6 +1960,40 @@ event_enable_init(struct ftrace_probe_ops *ops, unsigned long ip,
return 0;
}

+static int event_files_soft_mode(struct ftrace_event_file **files, int enable)
+{
+ struct ftrace_event_file **file = files;
+
+ if (!file)
+ return -EINVAL;
+
+ while (*file) {
+ /* Don't let event modules unload while probe registered */
+ if (enable) {
+ if (!try_module_get((*file)->event_call->mod))
+ goto rollback;
+ }
+
+ __ftrace_event_enable_disable(*file, enable, 1);
+
+ if (!enable)
+ module_put((*file)->event_call->mod);
+
+ file++;
+ }
+
+ return 0;
+
+ rollback:
+ while (file != files) {
+ file--;
+ __ftrace_event_enable_disable(*file, 0, 1);
+ module_put((*file)->event_call->mod);
+ }
+
+ return -EBUSY;
+}
+
static void
event_enable_free(struct ftrace_probe_ops *ops, unsigned long ip,
void **_data)
@@ -1960,9 +2006,11 @@ event_enable_free(struct ftrace_probe_ops *ops, unsigned long ip,

data->ref--;
if (!data->ref) {
- /* Remove the SOFT_MODE flag */
- __ftrace_event_enable_disable(data->file, 0, 1);
- module_put(data->file->event_call->mod);
+ /* We don't need wait rcu because no one refers data here */
+ event_files_soft_mode(data->files, 0);
+ kfree(data->files);
+ kfree(data->event);
+ kfree(data->system);
kfree(data);
}
*pdata = NULL;
@@ -2001,13 +2049,13 @@ event_enable_func(struct ftrace_hash *hash,
char *glob, char *cmd, char *param, int enabled)
{
struct trace_array *tr = top_trace_array();
- struct ftrace_event_file *file;
struct ftrace_probe_ops *ops;
struct event_probe_data *data;
const char *system;
const char *event;
char *number;
bool enable;
+ int nr_files;
int ret;

/* hash funcs only work with set_ftrace_filter */
@@ -2026,8 +2074,8 @@ event_enable_func(struct ftrace_hash *hash,
mutex_lock(&event_mutex);

ret = -EINVAL;
- file = find_event_file(tr, system, event);
- if (!file)
+ nr_files = find_event_files(tr, system, event, NULL, 0);
+ if (nr_files == 0)
goto out;

enable = strcmp(cmd, ENABLE_EVENT_STR) == 0;
@@ -2050,7 +2098,17 @@ event_enable_func(struct ftrace_hash *hash,

data->enable = enable;
data->count = -1;
- data->file = file;
+ data->files = kzalloc((nr_files + 1) * sizeof(*data->files),
+ GFP_KERNEL);
+ if (!data->files)
+ goto out_free;
+
+ find_event_files(tr, system, event, data->files, nr_files);
+
+ data->event = kstrdup(event, GFP_KERNEL);
+ data->system = kstrdup(system, GFP_KERNEL);
+ if (!data->event || !data->system)
+ goto out_free;

if (!param)
goto out_reg;
@@ -2070,16 +2128,10 @@ event_enable_func(struct ftrace_hash *hash,
goto out_free;

out_reg:
- /* Don't let event modules unload while probe registered */
- ret = try_module_get(file->event_call->mod);
- if (!ret) {
- ret = -EBUSY;
+ ret = event_files_soft_mode(data->files, 1);
+ if (ret < 0)
goto out_free;
- }

- ret = __ftrace_event_enable_disable(file, 1, 1);
- if (ret < 0)
- goto out_put;
ret = register_ftrace_function_probe(glob, ops, data);
/*
* The above returns on success the # of functions enabled,
@@ -2098,10 +2150,11 @@ event_enable_func(struct ftrace_hash *hash,
return ret;

out_disable:
- __ftrace_event_enable_disable(file, 0, 1);
- out_put:
- module_put(file->event_call->mod);
+ event_files_soft_mode(data->files, 0);
out_free:
+ kfree(data->files);
+ kfree(data->event);
+ kfree(data->system);
kfree(data);
goto out;
}

--
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/