|
@@ -36,6 +36,19 @@ EXPORT_SYMBOL_GPL(event_storage);
|
|
|
LIST_HEAD(ftrace_events);
|
|
|
LIST_HEAD(ftrace_common_fields);
|
|
|
|
|
|
+/* Double loops, do not use break, only goto's work */
|
|
|
+#define do_for_each_event_file(tr, file) \
|
|
|
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
|
|
|
+ list_for_each_entry(file, &tr->events, list)
|
|
|
+
|
|
|
+#define do_for_each_event_file_safe(tr, file) \
|
|
|
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
|
|
|
+ struct ftrace_event_file *___n; \
|
|
|
+ list_for_each_entry_safe(file, ___n, &tr->events, list)
|
|
|
+
|
|
|
+#define while_for_each_event_file() \
|
|
|
+ }
|
|
|
+
|
|
|
struct list_head *
|
|
|
trace_get_fields(struct ftrace_event_call *event_call)
|
|
|
{
|
|
@@ -149,15 +162,17 @@ EXPORT_SYMBOL_GPL(trace_event_raw_init);
|
|
|
int ftrace_event_reg(struct ftrace_event_call *call,
|
|
|
enum trace_reg type, void *data)
|
|
|
{
|
|
|
+ struct ftrace_event_file *file = data;
|
|
|
+
|
|
|
switch (type) {
|
|
|
case TRACE_REG_REGISTER:
|
|
|
return tracepoint_probe_register(call->name,
|
|
|
call->class->probe,
|
|
|
- call);
|
|
|
+ file);
|
|
|
case TRACE_REG_UNREGISTER:
|
|
|
tracepoint_probe_unregister(call->name,
|
|
|
call->class->probe,
|
|
|
- call);
|
|
|
+ file);
|
|
|
return 0;
|
|
|
|
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
@@ -183,54 +198,57 @@ EXPORT_SYMBOL_GPL(ftrace_event_reg);
|
|
|
|
|
|
void trace_event_enable_cmd_record(bool enable)
|
|
|
{
|
|
|
- struct ftrace_event_call *call;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+ struct trace_array *tr;
|
|
|
|
|
|
mutex_lock(&event_mutex);
|
|
|
- list_for_each_entry(call, &ftrace_events, list) {
|
|
|
- if (!(call->flags & TRACE_EVENT_FL_ENABLED))
|
|
|
+ do_for_each_event_file(tr, file) {
|
|
|
+
|
|
|
+ if (!(file->flags & FTRACE_EVENT_FL_ENABLED))
|
|
|
continue;
|
|
|
|
|
|
if (enable) {
|
|
|
tracing_start_cmdline_record();
|
|
|
- call->flags |= TRACE_EVENT_FL_RECORDED_CMD;
|
|
|
+ file->flags |= FTRACE_EVENT_FL_RECORDED_CMD;
|
|
|
} else {
|
|
|
tracing_stop_cmdline_record();
|
|
|
- call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD;
|
|
|
+ file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD;
|
|
|
}
|
|
|
- }
|
|
|
+ } while_for_each_event_file();
|
|
|
mutex_unlock(&event_mutex);
|
|
|
}
|
|
|
|
|
|
-static int ftrace_event_enable_disable(struct ftrace_event_call *call,
|
|
|
- int enable)
|
|
|
+static int ftrace_event_enable_disable(struct ftrace_event_file *file,
|
|
|
+ int enable)
|
|
|
{
|
|
|
+ struct ftrace_event_call *call = file->event_call;
|
|
|
int ret = 0;
|
|
|
|
|
|
switch (enable) {
|
|
|
case 0:
|
|
|
- if (call->flags & TRACE_EVENT_FL_ENABLED) {
|
|
|
- call->flags &= ~TRACE_EVENT_FL_ENABLED;
|
|
|
- if (call->flags & TRACE_EVENT_FL_RECORDED_CMD) {
|
|
|
+ if (file->flags & FTRACE_EVENT_FL_ENABLED) {
|
|
|
+ file->flags &= ~FTRACE_EVENT_FL_ENABLED;
|
|
|
+ if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) {
|
|
|
tracing_stop_cmdline_record();
|
|
|
- call->flags &= ~TRACE_EVENT_FL_RECORDED_CMD;
|
|
|
+ file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD;
|
|
|
}
|
|
|
- call->class->reg(call, TRACE_REG_UNREGISTER, NULL);
|
|
|
+ call->class->reg(call, TRACE_REG_UNREGISTER, file);
|
|
|
}
|
|
|
break;
|
|
|
case 1:
|
|
|
- if (!(call->flags & TRACE_EVENT_FL_ENABLED)) {
|
|
|
+ if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) {
|
|
|
if (trace_flags & TRACE_ITER_RECORD_CMD) {
|
|
|
tracing_start_cmdline_record();
|
|
|
- call->flags |= TRACE_EVENT_FL_RECORDED_CMD;
|
|
|
+ file->flags |= FTRACE_EVENT_FL_RECORDED_CMD;
|
|
|
}
|
|
|
- ret = call->class->reg(call, TRACE_REG_REGISTER, NULL);
|
|
|
+ ret = call->class->reg(call, TRACE_REG_REGISTER, file);
|
|
|
if (ret) {
|
|
|
tracing_stop_cmdline_record();
|
|
|
pr_info("event trace: Could not enable event "
|
|
|
"%s\n", call->name);
|
|
|
break;
|
|
|
}
|
|
|
- call->flags |= TRACE_EVENT_FL_ENABLED;
|
|
|
+ file->flags |= FTRACE_EVENT_FL_ENABLED;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
@@ -238,13 +256,13 @@ static int ftrace_event_enable_disable(struct ftrace_event_call *call,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void ftrace_clear_events(void)
|
|
|
+static void ftrace_clear_events(struct trace_array *tr)
|
|
|
{
|
|
|
- struct ftrace_event_call *call;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
|
|
|
mutex_lock(&event_mutex);
|
|
|
- list_for_each_entry(call, &ftrace_events, list) {
|
|
|
- ftrace_event_enable_disable(call, 0);
|
|
|
+ list_for_each_entry(file, &tr->events, list) {
|
|
|
+ ftrace_event_enable_disable(file, 0);
|
|
|
}
|
|
|
mutex_unlock(&event_mutex);
|
|
|
}
|
|
@@ -257,6 +275,8 @@ static void __put_system(struct event_subsystem *system)
|
|
|
if (--system->ref_count)
|
|
|
return;
|
|
|
|
|
|
+ list_del(&system->list);
|
|
|
+
|
|
|
if (filter) {
|
|
|
kfree(filter->filter_string);
|
|
|
kfree(filter);
|
|
@@ -271,24 +291,45 @@ static void __get_system(struct event_subsystem *system)
|
|
|
system->ref_count++;
|
|
|
}
|
|
|
|
|
|
-static void put_system(struct event_subsystem *system)
|
|
|
+static void __get_system_dir(struct ftrace_subsystem_dir *dir)
|
|
|
+{
|
|
|
+ WARN_ON_ONCE(dir->ref_count == 0);
|
|
|
+ dir->ref_count++;
|
|
|
+ __get_system(dir->subsystem);
|
|
|
+}
|
|
|
+
|
|
|
+static void __put_system_dir(struct ftrace_subsystem_dir *dir)
|
|
|
+{
|
|
|
+ WARN_ON_ONCE(dir->ref_count == 0);
|
|
|
+ /* If the subsystem is about to be freed, the dir must be too */
|
|
|
+ WARN_ON_ONCE(dir->subsystem->ref_count == 1 && dir->ref_count != 1);
|
|
|
+
|
|
|
+ __put_system(dir->subsystem);
|
|
|
+ if (!--dir->ref_count)
|
|
|
+ kfree(dir);
|
|
|
+}
|
|
|
+
|
|
|
+static void put_system(struct ftrace_subsystem_dir *dir)
|
|
|
{
|
|
|
mutex_lock(&event_mutex);
|
|
|
- __put_system(system);
|
|
|
+ __put_system_dir(dir);
|
|
|
mutex_unlock(&event_mutex);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
|
|
|
*/
|
|
|
-static int __ftrace_set_clr_event(const char *match, const char *sub,
|
|
|
- const char *event, int set)
|
|
|
+static int __ftrace_set_clr_event(struct trace_array *tr, const char *match,
|
|
|
+ const char *sub, const char *event, int set)
|
|
|
{
|
|
|
+ struct ftrace_event_file *file;
|
|
|
struct ftrace_event_call *call;
|
|
|
int ret = -EINVAL;
|
|
|
|
|
|
mutex_lock(&event_mutex);
|
|
|
- list_for_each_entry(call, &ftrace_events, list) {
|
|
|
+ list_for_each_entry(file, &tr->events, list) {
|
|
|
+
|
|
|
+ call = file->event_call;
|
|
|
|
|
|
if (!call->name || !call->class || !call->class->reg)
|
|
|
continue;
|
|
@@ -307,7 +348,7 @@ static int __ftrace_set_clr_event(const char *match, const char *sub,
|
|
|
if (event && strcmp(event, call->name) != 0)
|
|
|
continue;
|
|
|
|
|
|
- ftrace_event_enable_disable(call, set);
|
|
|
+ ftrace_event_enable_disable(file, set);
|
|
|
|
|
|
ret = 0;
|
|
|
}
|
|
@@ -316,7 +357,7 @@ static int __ftrace_set_clr_event(const char *match, const char *sub,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int ftrace_set_clr_event(char *buf, int set)
|
|
|
+static int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
|
|
|
{
|
|
|
char *event = NULL, *sub = NULL, *match;
|
|
|
|
|
@@ -344,7 +385,7 @@ static int ftrace_set_clr_event(char *buf, int set)
|
|
|
event = NULL;
|
|
|
}
|
|
|
|
|
|
- return __ftrace_set_clr_event(match, sub, event, set);
|
|
|
+ return __ftrace_set_clr_event(tr, match, sub, event, set);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -361,7 +402,9 @@ static int ftrace_set_clr_event(char *buf, int set)
|
|
|
*/
|
|
|
int trace_set_clr_event(const char *system, const char *event, int set)
|
|
|
{
|
|
|
- return __ftrace_set_clr_event(NULL, system, event, set);
|
|
|
+ struct trace_array *tr = top_trace_array();
|
|
|
+
|
|
|
+ return __ftrace_set_clr_event(tr, NULL, system, event, set);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(trace_set_clr_event);
|
|
|
|
|
@@ -373,6 +416,8 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
|
|
|
size_t cnt, loff_t *ppos)
|
|
|
{
|
|
|
struct trace_parser parser;
|
|
|
+ struct seq_file *m = file->private_data;
|
|
|
+ struct trace_array *tr = m->private;
|
|
|
ssize_t read, ret;
|
|
|
|
|
|
if (!cnt)
|
|
@@ -395,7 +440,7 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
|
|
|
|
|
|
parser.buffer[parser.idx] = 0;
|
|
|
|
|
|
- ret = ftrace_set_clr_event(parser.buffer + !set, set);
|
|
|
+ ret = ftrace_set_clr_event(tr, parser.buffer + !set, set);
|
|
|
if (ret)
|
|
|
goto out_put;
|
|
|
}
|
|
@@ -411,17 +456,20 @@ ftrace_event_write(struct file *file, const char __user *ubuf,
|
|
|
static void *
|
|
|
t_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
{
|
|
|
- struct ftrace_event_call *call = v;
|
|
|
+ struct ftrace_event_file *file = v;
|
|
|
+ struct ftrace_event_call *call;
|
|
|
+ struct trace_array *tr = m->private;
|
|
|
|
|
|
(*pos)++;
|
|
|
|
|
|
- list_for_each_entry_continue(call, &ftrace_events, list) {
|
|
|
+ list_for_each_entry_continue(file, &tr->events, list) {
|
|
|
+ call = file->event_call;
|
|
|
/*
|
|
|
* The ftrace subsystem is for showing formats only.
|
|
|
* They can not be enabled or disabled via the event files.
|
|
|
*/
|
|
|
if (call->class && call->class->reg)
|
|
|
- return call;
|
|
|
+ return file;
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
@@ -429,30 +477,32 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
|
|
|
static void *t_start(struct seq_file *m, loff_t *pos)
|
|
|
{
|
|
|
- struct ftrace_event_call *call;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+ struct trace_array *tr = m->private;
|
|
|
loff_t l;
|
|
|
|
|
|
mutex_lock(&event_mutex);
|
|
|
|
|
|
- call = list_entry(&ftrace_events, struct ftrace_event_call, list);
|
|
|
+ file = list_entry(&tr->events, struct ftrace_event_file, list);
|
|
|
for (l = 0; l <= *pos; ) {
|
|
|
- call = t_next(m, call, &l);
|
|
|
- if (!call)
|
|
|
+ file = t_next(m, file, &l);
|
|
|
+ if (!file)
|
|
|
break;
|
|
|
}
|
|
|
- return call;
|
|
|
+ return file;
|
|
|
}
|
|
|
|
|
|
static void *
|
|
|
s_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
{
|
|
|
- struct ftrace_event_call *call = v;
|
|
|
+ struct ftrace_event_file *file = v;
|
|
|
+ struct trace_array *tr = m->private;
|
|
|
|
|
|
(*pos)++;
|
|
|
|
|
|
- list_for_each_entry_continue(call, &ftrace_events, list) {
|
|
|
- if (call->flags & TRACE_EVENT_FL_ENABLED)
|
|
|
- return call;
|
|
|
+ list_for_each_entry_continue(file, &tr->events, list) {
|
|
|
+ if (file->flags & FTRACE_EVENT_FL_ENABLED)
|
|
|
+ return file;
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
@@ -460,23 +510,25 @@ s_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
|
|
|
static void *s_start(struct seq_file *m, loff_t *pos)
|
|
|
{
|
|
|
- struct ftrace_event_call *call;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+ struct trace_array *tr = m->private;
|
|
|
loff_t l;
|
|
|
|
|
|
mutex_lock(&event_mutex);
|
|
|
|
|
|
- call = list_entry(&ftrace_events, struct ftrace_event_call, list);
|
|
|
+ file = list_entry(&tr->events, struct ftrace_event_file, list);
|
|
|
for (l = 0; l <= *pos; ) {
|
|
|
- call = s_next(m, call, &l);
|
|
|
- if (!call)
|
|
|
+ file = s_next(m, file, &l);
|
|
|
+ if (!file)
|
|
|
break;
|
|
|
}
|
|
|
- return call;
|
|
|
+ return file;
|
|
|
}
|
|
|
|
|
|
static int t_show(struct seq_file *m, void *v)
|
|
|
{
|
|
|
- struct ftrace_event_call *call = v;
|
|
|
+ struct ftrace_event_file *file = v;
|
|
|
+ struct ftrace_event_call *call = file->event_call;
|
|
|
|
|
|
if (strcmp(call->class->system, TRACE_SYSTEM) != 0)
|
|
|
seq_printf(m, "%s:", call->class->system);
|
|
@@ -494,10 +546,10 @@ static ssize_t
|
|
|
event_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
|
|
|
loff_t *ppos)
|
|
|
{
|
|
|
- struct ftrace_event_call *call = filp->private_data;
|
|
|
+ struct ftrace_event_file *file = filp->private_data;
|
|
|
char *buf;
|
|
|
|
|
|
- if (call->flags & TRACE_EVENT_FL_ENABLED)
|
|
|
+ if (file->flags & FTRACE_EVENT_FL_ENABLED)
|
|
|
buf = "1\n";
|
|
|
else
|
|
|
buf = "0\n";
|
|
@@ -509,10 +561,13 @@ static ssize_t
|
|
|
event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|
|
loff_t *ppos)
|
|
|
{
|
|
|
- struct ftrace_event_call *call = filp->private_data;
|
|
|
+ struct ftrace_event_file *file = filp->private_data;
|
|
|
unsigned long val;
|
|
|
int ret;
|
|
|
|
|
|
+ if (!file)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
|
|
|
if (ret)
|
|
|
return ret;
|
|
@@ -525,7 +580,7 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|
|
case 0:
|
|
|
case 1:
|
|
|
mutex_lock(&event_mutex);
|
|
|
- ret = ftrace_event_enable_disable(call, val);
|
|
|
+ ret = ftrace_event_enable_disable(file, val);
|
|
|
mutex_unlock(&event_mutex);
|
|
|
break;
|
|
|
|
|
@@ -543,14 +598,18 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
|
|
|
loff_t *ppos)
|
|
|
{
|
|
|
const char set_to_char[4] = { '?', '0', '1', 'X' };
|
|
|
- struct event_subsystem *system = filp->private_data;
|
|
|
+ struct ftrace_subsystem_dir *dir = filp->private_data;
|
|
|
+ struct event_subsystem *system = dir->subsystem;
|
|
|
struct ftrace_event_call *call;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+ struct trace_array *tr = dir->tr;
|
|
|
char buf[2];
|
|
|
int set = 0;
|
|
|
int ret;
|
|
|
|
|
|
mutex_lock(&event_mutex);
|
|
|
- list_for_each_entry(call, &ftrace_events, list) {
|
|
|
+ list_for_each_entry(file, &tr->events, list) {
|
|
|
+ call = file->event_call;
|
|
|
if (!call->name || !call->class || !call->class->reg)
|
|
|
continue;
|
|
|
|
|
@@ -562,7 +621,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt,
|
|
|
* or if all events or cleared, or if we have
|
|
|
* a mixture.
|
|
|
*/
|
|
|
- set |= (1 << !!(call->flags & TRACE_EVENT_FL_ENABLED));
|
|
|
+ set |= (1 << !!(file->flags & FTRACE_EVENT_FL_ENABLED));
|
|
|
|
|
|
/*
|
|
|
* If we have a mixture, no need to look further.
|
|
@@ -584,7 +643,8 @@ static ssize_t
|
|
|
system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|
|
loff_t *ppos)
|
|
|
{
|
|
|
- struct event_subsystem *system = filp->private_data;
|
|
|
+ struct ftrace_subsystem_dir *dir = filp->private_data;
|
|
|
+ struct event_subsystem *system = dir->subsystem;
|
|
|
const char *name = NULL;
|
|
|
unsigned long val;
|
|
|
ssize_t ret;
|
|
@@ -607,7 +667,7 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|
|
if (system)
|
|
|
name = system->name;
|
|
|
|
|
|
- ret = __ftrace_set_clr_event(NULL, name, NULL, val);
|
|
|
+ ret = __ftrace_set_clr_event(dir->tr, NULL, name, NULL, val);
|
|
|
if (ret)
|
|
|
goto out;
|
|
|
|
|
@@ -845,43 +905,75 @@ static LIST_HEAD(event_subsystems);
|
|
|
static int subsystem_open(struct inode *inode, struct file *filp)
|
|
|
{
|
|
|
struct event_subsystem *system = NULL;
|
|
|
+ struct ftrace_subsystem_dir *dir = NULL; /* Initialize for gcc */
|
|
|
+ struct trace_array *tr;
|
|
|
int ret;
|
|
|
|
|
|
- if (!inode->i_private)
|
|
|
- goto skip_search;
|
|
|
-
|
|
|
/* Make sure the system still exists */
|
|
|
mutex_lock(&event_mutex);
|
|
|
- list_for_each_entry(system, &event_subsystems, list) {
|
|
|
- if (system == inode->i_private) {
|
|
|
- /* Don't open systems with no events */
|
|
|
- if (!system->nr_events) {
|
|
|
- system = NULL;
|
|
|
- break;
|
|
|
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
|
|
|
+ list_for_each_entry(dir, &tr->systems, list) {
|
|
|
+ if (dir == inode->i_private) {
|
|
|
+ /* Don't open systems with no events */
|
|
|
+ if (dir->nr_events) {
|
|
|
+ __get_system_dir(dir);
|
|
|
+ system = dir->subsystem;
|
|
|
+ }
|
|
|
+ goto exit_loop;
|
|
|
}
|
|
|
- __get_system(system);
|
|
|
- break;
|
|
|
}
|
|
|
}
|
|
|
+ exit_loop:
|
|
|
mutex_unlock(&event_mutex);
|
|
|
|
|
|
- if (system != inode->i_private)
|
|
|
+ if (!system)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- skip_search:
|
|
|
+ /* Some versions of gcc think dir can be uninitialized here */
|
|
|
+ WARN_ON(!dir);
|
|
|
+
|
|
|
ret = tracing_open_generic(inode, filp);
|
|
|
- if (ret < 0 && system)
|
|
|
- put_system(system);
|
|
|
+ if (ret < 0)
|
|
|
+ put_system(dir);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int system_tr_open(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ struct ftrace_subsystem_dir *dir;
|
|
|
+ struct trace_array *tr = inode->i_private;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Make a temporary dir that has no system but points to tr */
|
|
|
+ dir = kzalloc(sizeof(*dir), GFP_KERNEL);
|
|
|
+ if (!dir)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ dir->tr = tr;
|
|
|
+
|
|
|
+ ret = tracing_open_generic(inode, filp);
|
|
|
+ if (ret < 0)
|
|
|
+ kfree(dir);
|
|
|
+
|
|
|
+ filp->private_data = dir;
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
static int subsystem_release(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
- struct event_subsystem *system = inode->i_private;
|
|
|
+ struct ftrace_subsystem_dir *dir = file->private_data;
|
|
|
|
|
|
- if (system)
|
|
|
- put_system(system);
|
|
|
+ /*
|
|
|
+ * If dir->subsystem is NULL, then this is a temporary
|
|
|
+ * descriptor that was made for a trace_array to enable
|
|
|
+ * all subsystems.
|
|
|
+ */
|
|
|
+ if (dir->subsystem)
|
|
|
+ put_system(dir);
|
|
|
+ else
|
|
|
+ kfree(dir);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -890,7 +982,8 @@ static ssize_t
|
|
|
subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt,
|
|
|
loff_t *ppos)
|
|
|
{
|
|
|
- struct event_subsystem *system = filp->private_data;
|
|
|
+ struct ftrace_subsystem_dir *dir = filp->private_data;
|
|
|
+ struct event_subsystem *system = dir->subsystem;
|
|
|
struct trace_seq *s;
|
|
|
int r;
|
|
|
|
|
@@ -915,7 +1008,7 @@ static ssize_t
|
|
|
subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|
|
loff_t *ppos)
|
|
|
{
|
|
|
- struct event_subsystem *system = filp->private_data;
|
|
|
+ struct ftrace_subsystem_dir *dir = filp->private_data;
|
|
|
char *buf;
|
|
|
int err;
|
|
|
|
|
@@ -932,7 +1025,7 @@ subsystem_filter_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|
|
}
|
|
|
buf[cnt] = '\0';
|
|
|
|
|
|
- err = apply_subsystem_event_filter(system, buf);
|
|
|
+ err = apply_subsystem_event_filter(dir, buf);
|
|
|
free_page((unsigned long) buf);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
@@ -1041,30 +1134,35 @@ static const struct file_operations ftrace_system_enable_fops = {
|
|
|
.release = subsystem_release,
|
|
|
};
|
|
|
|
|
|
+static const struct file_operations ftrace_tr_enable_fops = {
|
|
|
+ .open = system_tr_open,
|
|
|
+ .read = system_enable_read,
|
|
|
+ .write = system_enable_write,
|
|
|
+ .llseek = default_llseek,
|
|
|
+ .release = subsystem_release,
|
|
|
+};
|
|
|
+
|
|
|
static const struct file_operations ftrace_show_header_fops = {
|
|
|
.open = tracing_open_generic,
|
|
|
.read = show_header,
|
|
|
.llseek = default_llseek,
|
|
|
};
|
|
|
|
|
|
-static struct dentry *event_trace_events_dir(void)
|
|
|
+static int
|
|
|
+ftrace_event_open(struct inode *inode, struct file *file,
|
|
|
+ const struct seq_operations *seq_ops)
|
|
|
{
|
|
|
- static struct dentry *d_tracer;
|
|
|
- static struct dentry *d_events;
|
|
|
-
|
|
|
- if (d_events)
|
|
|
- return d_events;
|
|
|
-
|
|
|
- d_tracer = tracing_init_dentry();
|
|
|
- if (!d_tracer)
|
|
|
- return NULL;
|
|
|
+ struct seq_file *m;
|
|
|
+ int ret;
|
|
|
|
|
|
- d_events = debugfs_create_dir("events", d_tracer);
|
|
|
- if (!d_events)
|
|
|
- pr_warning("Could not create debugfs "
|
|
|
- "'events' directory\n");
|
|
|
+ ret = seq_open(file, seq_ops);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ m = file->private_data;
|
|
|
+ /* copy tr over to seq ops */
|
|
|
+ m->private = inode->i_private;
|
|
|
|
|
|
- return d_events;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static int
|
|
@@ -1072,117 +1170,169 @@ ftrace_event_avail_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
const struct seq_operations *seq_ops = &show_event_seq_ops;
|
|
|
|
|
|
- return seq_open(file, seq_ops);
|
|
|
+ return ftrace_event_open(inode, file, seq_ops);
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
ftrace_event_set_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
const struct seq_operations *seq_ops = &show_set_event_seq_ops;
|
|
|
+ struct trace_array *tr = inode->i_private;
|
|
|
|
|
|
if ((file->f_mode & FMODE_WRITE) &&
|
|
|
(file->f_flags & O_TRUNC))
|
|
|
- ftrace_clear_events();
|
|
|
+ ftrace_clear_events(tr);
|
|
|
|
|
|
- return seq_open(file, seq_ops);
|
|
|
+ return ftrace_event_open(inode, file, seq_ops);
|
|
|
+}
|
|
|
+
|
|
|
+static struct event_subsystem *
|
|
|
+create_new_subsystem(const char *name)
|
|
|
+{
|
|
|
+ struct event_subsystem *system;
|
|
|
+
|
|
|
+ /* need to create new entry */
|
|
|
+ system = kmalloc(sizeof(*system), GFP_KERNEL);
|
|
|
+ if (!system)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ system->ref_count = 1;
|
|
|
+ system->name = kstrdup(name, GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!system->name)
|
|
|
+ goto out_free;
|
|
|
+
|
|
|
+ system->filter = NULL;
|
|
|
+
|
|
|
+ system->filter = kzalloc(sizeof(struct event_filter), GFP_KERNEL);
|
|
|
+ if (!system->filter)
|
|
|
+ goto out_free;
|
|
|
+
|
|
|
+ list_add(&system->list, &event_subsystems);
|
|
|
+
|
|
|
+ return system;
|
|
|
+
|
|
|
+ out_free:
|
|
|
+ kfree(system->name);
|
|
|
+ kfree(system);
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static struct dentry *
|
|
|
-event_subsystem_dir(const char *name, struct dentry *d_events)
|
|
|
+event_subsystem_dir(struct trace_array *tr, const char *name,
|
|
|
+ struct ftrace_event_file *file, struct dentry *parent)
|
|
|
{
|
|
|
+ struct ftrace_subsystem_dir *dir;
|
|
|
struct event_subsystem *system;
|
|
|
struct dentry *entry;
|
|
|
|
|
|
/* First see if we did not already create this dir */
|
|
|
- list_for_each_entry(system, &event_subsystems, list) {
|
|
|
+ list_for_each_entry(dir, &tr->systems, list) {
|
|
|
+ system = dir->subsystem;
|
|
|
if (strcmp(system->name, name) == 0) {
|
|
|
- system->nr_events++;
|
|
|
- return system->entry;
|
|
|
+ dir->nr_events++;
|
|
|
+ file->system = dir;
|
|
|
+ return dir->entry;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* need to create new entry */
|
|
|
- system = kmalloc(sizeof(*system), GFP_KERNEL);
|
|
|
- if (!system) {
|
|
|
- pr_warning("No memory to create event subsystem %s\n",
|
|
|
- name);
|
|
|
- return d_events;
|
|
|
+ /* Now see if the system itself exists. */
|
|
|
+ list_for_each_entry(system, &event_subsystems, list) {
|
|
|
+ if (strcmp(system->name, name) == 0)
|
|
|
+ break;
|
|
|
}
|
|
|
+ /* Reset system variable when not found */
|
|
|
+ if (&system->list == &event_subsystems)
|
|
|
+ system = NULL;
|
|
|
|
|
|
- system->entry = debugfs_create_dir(name, d_events);
|
|
|
- if (!system->entry) {
|
|
|
- pr_warning("Could not create event subsystem %s\n",
|
|
|
- name);
|
|
|
- kfree(system);
|
|
|
- return d_events;
|
|
|
- }
|
|
|
+ dir = kmalloc(sizeof(*dir), GFP_KERNEL);
|
|
|
+ if (!dir)
|
|
|
+ goto out_fail;
|
|
|
|
|
|
- system->nr_events = 1;
|
|
|
- system->ref_count = 1;
|
|
|
- system->name = kstrdup(name, GFP_KERNEL);
|
|
|
- if (!system->name) {
|
|
|
- debugfs_remove(system->entry);
|
|
|
- kfree(system);
|
|
|
- return d_events;
|
|
|
+ if (!system) {
|
|
|
+ system = create_new_subsystem(name);
|
|
|
+ if (!system)
|
|
|
+ goto out_free;
|
|
|
+ } else
|
|
|
+ __get_system(system);
|
|
|
+
|
|
|
+ dir->entry = debugfs_create_dir(name, parent);
|
|
|
+ if (!dir->entry) {
|
|
|
+ pr_warning("Failed to create system directory %s\n", name);
|
|
|
+ __put_system(system);
|
|
|
+ goto out_free;
|
|
|
}
|
|
|
|
|
|
- list_add(&system->list, &event_subsystems);
|
|
|
-
|
|
|
- system->filter = NULL;
|
|
|
-
|
|
|
- system->filter = kzalloc(sizeof(struct event_filter), GFP_KERNEL);
|
|
|
- if (!system->filter) {
|
|
|
- pr_warning("Could not allocate filter for subsystem "
|
|
|
- "'%s'\n", name);
|
|
|
- return system->entry;
|
|
|
- }
|
|
|
+ dir->tr = tr;
|
|
|
+ dir->ref_count = 1;
|
|
|
+ dir->nr_events = 1;
|
|
|
+ dir->subsystem = system;
|
|
|
+ file->system = dir;
|
|
|
|
|
|
- entry = debugfs_create_file("filter", 0644, system->entry, system,
|
|
|
+ entry = debugfs_create_file("filter", 0644, dir->entry, dir,
|
|
|
&ftrace_subsystem_filter_fops);
|
|
|
if (!entry) {
|
|
|
kfree(system->filter);
|
|
|
system->filter = NULL;
|
|
|
- pr_warning("Could not create debugfs "
|
|
|
- "'%s/filter' entry\n", name);
|
|
|
+ pr_warning("Could not create debugfs '%s/filter' entry\n", name);
|
|
|
}
|
|
|
|
|
|
- trace_create_file("enable", 0644, system->entry, system,
|
|
|
+ trace_create_file("enable", 0644, dir->entry, dir,
|
|
|
&ftrace_system_enable_fops);
|
|
|
|
|
|
- return system->entry;
|
|
|
+ list_add(&dir->list, &tr->systems);
|
|
|
+
|
|
|
+ return dir->entry;
|
|
|
+
|
|
|
+ out_free:
|
|
|
+ kfree(dir);
|
|
|
+ out_fail:
|
|
|
+ /* Only print this message if failed on memory allocation */
|
|
|
+ if (!dir || !system)
|
|
|
+ pr_warning("No memory to create event subsystem %s\n",
|
|
|
+ name);
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
|
|
+event_create_dir(struct dentry *parent,
|
|
|
+ struct ftrace_event_file *file,
|
|
|
const struct file_operations *id,
|
|
|
const struct file_operations *enable,
|
|
|
const struct file_operations *filter,
|
|
|
const struct file_operations *format)
|
|
|
{
|
|
|
+ struct ftrace_event_call *call = file->event_call;
|
|
|
+ struct trace_array *tr = file->tr;
|
|
|
struct list_head *head;
|
|
|
+ struct dentry *d_events;
|
|
|
int ret;
|
|
|
|
|
|
/*
|
|
|
* If the trace point header did not define TRACE_SYSTEM
|
|
|
* then the system would be called "TRACE_SYSTEM".
|
|
|
*/
|
|
|
- if (strcmp(call->class->system, TRACE_SYSTEM) != 0)
|
|
|
- d_events = event_subsystem_dir(call->class->system, d_events);
|
|
|
-
|
|
|
- call->dir = debugfs_create_dir(call->name, d_events);
|
|
|
- if (!call->dir) {
|
|
|
- pr_warning("Could not create debugfs "
|
|
|
- "'%s' directory\n", call->name);
|
|
|
+ if (strcmp(call->class->system, TRACE_SYSTEM) != 0) {
|
|
|
+ d_events = event_subsystem_dir(tr, call->class->system, file, parent);
|
|
|
+ if (!d_events)
|
|
|
+ return -ENOMEM;
|
|
|
+ } else
|
|
|
+ d_events = parent;
|
|
|
+
|
|
|
+ file->dir = debugfs_create_dir(call->name, d_events);
|
|
|
+ if (!file->dir) {
|
|
|
+ pr_warning("Could not create debugfs '%s' directory\n",
|
|
|
+ call->name);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
if (call->class->reg && !(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE))
|
|
|
- trace_create_file("enable", 0644, call->dir, call,
|
|
|
+ trace_create_file("enable", 0644, file->dir, file,
|
|
|
enable);
|
|
|
|
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
|
if (call->event.type && call->class->reg)
|
|
|
- trace_create_file("id", 0444, call->dir, call,
|
|
|
+ trace_create_file("id", 0444, file->dir, call,
|
|
|
id);
|
|
|
#endif
|
|
|
|
|
@@ -1196,23 +1346,76 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
|
|
|
if (ret < 0) {
|
|
|
pr_warning("Could not initialize trace point"
|
|
|
" events/%s\n", call->name);
|
|
|
- return ret;
|
|
|
+ return -1;
|
|
|
}
|
|
|
}
|
|
|
- trace_create_file("filter", 0644, call->dir, call,
|
|
|
+ trace_create_file("filter", 0644, file->dir, call,
|
|
|
filter);
|
|
|
|
|
|
- trace_create_file("format", 0444, call->dir, call,
|
|
|
+ trace_create_file("format", 0444, file->dir, call,
|
|
|
format);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void remove_subsystem(struct ftrace_subsystem_dir *dir)
|
|
|
+{
|
|
|
+ if (!dir)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!--dir->nr_events) {
|
|
|
+ debugfs_remove_recursive(dir->entry);
|
|
|
+ list_del(&dir->list);
|
|
|
+ __put_system_dir(dir);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void remove_event_from_tracers(struct ftrace_event_call *call)
|
|
|
+{
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+ struct trace_array *tr;
|
|
|
+
|
|
|
+ do_for_each_event_file_safe(tr, file) {
|
|
|
+
|
|
|
+ if (file->event_call != call)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ list_del(&file->list);
|
|
|
+ debugfs_remove_recursive(file->dir);
|
|
|
+ remove_subsystem(file->system);
|
|
|
+ kfree(file);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The do_for_each_event_file_safe() is
|
|
|
+ * a double loop. After finding the call for this
|
|
|
+ * trace_array, we use break to jump to the next
|
|
|
+ * trace_array.
|
|
|
+ */
|
|
|
+ break;
|
|
|
+ } while_for_each_event_file();
|
|
|
+}
|
|
|
+
|
|
|
static void event_remove(struct ftrace_event_call *call)
|
|
|
{
|
|
|
- ftrace_event_enable_disable(call, 0);
|
|
|
+ struct trace_array *tr;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+
|
|
|
+ do_for_each_event_file(tr, file) {
|
|
|
+ if (file->event_call != call)
|
|
|
+ continue;
|
|
|
+ ftrace_event_enable_disable(file, 0);
|
|
|
+ /*
|
|
|
+ * The do_for_each_event_file() is
|
|
|
+ * a double loop. After finding the call for this
|
|
|
+ * trace_array, we use break to jump to the next
|
|
|
+ * trace_array.
|
|
|
+ */
|
|
|
+ break;
|
|
|
+ } while_for_each_event_file();
|
|
|
+
|
|
|
if (call->event.funcs)
|
|
|
__unregister_ftrace_event(&call->event);
|
|
|
+ remove_event_from_tracers(call);
|
|
|
list_del(&call->list);
|
|
|
}
|
|
|
|
|
@@ -1234,61 +1437,58 @@ static int event_init(struct ftrace_event_call *call)
|
|
|
}
|
|
|
|
|
|
static int
|
|
|
-__trace_add_event_call(struct ftrace_event_call *call, struct module *mod,
|
|
|
- const struct file_operations *id,
|
|
|
- const struct file_operations *enable,
|
|
|
- const struct file_operations *filter,
|
|
|
- const struct file_operations *format)
|
|
|
+__register_event(struct ftrace_event_call *call, struct module *mod)
|
|
|
{
|
|
|
- struct dentry *d_events;
|
|
|
int ret;
|
|
|
|
|
|
ret = event_init(call);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- d_events = event_trace_events_dir();
|
|
|
- if (!d_events)
|
|
|
- return -ENOENT;
|
|
|
-
|
|
|
- ret = event_create_dir(call, d_events, id, enable, filter, format);
|
|
|
- if (!ret)
|
|
|
- list_add(&call->list, &ftrace_events);
|
|
|
+ list_add(&call->list, &ftrace_events);
|
|
|
call->mod = mod;
|
|
|
|
|
|
- return ret;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
+/* Add an event to a trace directory */
|
|
|
+static int
|
|
|
+__trace_add_new_event(struct ftrace_event_call *call,
|
|
|
+ struct trace_array *tr,
|
|
|
+ const struct file_operations *id,
|
|
|
+ const struct file_operations *enable,
|
|
|
+ const struct file_operations *filter,
|
|
|
+ const struct file_operations *format)
|
|
|
+{
|
|
|
+ struct ftrace_event_file *file;
|
|
|
+
|
|
|
+ file = kzalloc(sizeof(*file), GFP_KERNEL);
|
|
|
+ if (!file)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ file->event_call = call;
|
|
|
+ file->tr = tr;
|
|
|
+ list_add(&file->list, &tr->events);
|
|
|
+
|
|
|
+ return event_create_dir(tr->event_dir, file, id, enable, filter, format);
|
|
|
+}
|
|
|
+
|
|
|
+struct ftrace_module_file_ops;
|
|
|
+static void __add_event_to_tracers(struct ftrace_event_call *call,
|
|
|
+ struct ftrace_module_file_ops *file_ops);
|
|
|
+
|
|
|
/* Add an additional event_call dynamically */
|
|
|
int trace_add_event_call(struct ftrace_event_call *call)
|
|
|
{
|
|
|
int ret;
|
|
|
mutex_lock(&event_mutex);
|
|
|
- ret = __trace_add_event_call(call, NULL, &ftrace_event_id_fops,
|
|
|
- &ftrace_enable_fops,
|
|
|
- &ftrace_event_filter_fops,
|
|
|
- &ftrace_event_format_fops);
|
|
|
- mutex_unlock(&event_mutex);
|
|
|
- return ret;
|
|
|
-}
|
|
|
|
|
|
-static void remove_subsystem_dir(const char *name)
|
|
|
-{
|
|
|
- struct event_subsystem *system;
|
|
|
+ ret = __register_event(call, NULL);
|
|
|
+ if (ret >= 0)
|
|
|
+ __add_event_to_tracers(call, NULL);
|
|
|
|
|
|
- if (strcmp(name, TRACE_SYSTEM) == 0)
|
|
|
- return;
|
|
|
-
|
|
|
- list_for_each_entry(system, &event_subsystems, list) {
|
|
|
- if (strcmp(system->name, name) == 0) {
|
|
|
- if (!--system->nr_events) {
|
|
|
- debugfs_remove_recursive(system->entry);
|
|
|
- list_del(&system->list);
|
|
|
- __put_system(system);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ mutex_unlock(&event_mutex);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1299,8 +1499,6 @@ static void __trace_remove_event_call(struct ftrace_event_call *call)
|
|
|
event_remove(call);
|
|
|
trace_destroy_fields(call);
|
|
|
destroy_preds(call);
|
|
|
- debugfs_remove_recursive(call->dir);
|
|
|
- remove_subsystem_dir(call->class->system);
|
|
|
}
|
|
|
|
|
|
/* Remove an event_call */
|
|
@@ -1335,6 +1533,17 @@ struct ftrace_module_file_ops {
|
|
|
struct file_operations filter;
|
|
|
};
|
|
|
|
|
|
+static struct ftrace_module_file_ops *find_ftrace_file_ops(struct module *mod)
|
|
|
+{
|
|
|
+ struct ftrace_module_file_ops *file_ops;
|
|
|
+
|
|
|
+ list_for_each_entry(file_ops, &ftrace_module_file_list, list) {
|
|
|
+ if (file_ops->mod == mod)
|
|
|
+ return file_ops;
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
static struct ftrace_module_file_ops *
|
|
|
trace_create_file_ops(struct module *mod)
|
|
|
{
|
|
@@ -1386,9 +1595,8 @@ static void trace_module_add_events(struct module *mod)
|
|
|
return;
|
|
|
|
|
|
for_each_event(call, start, end) {
|
|
|
- __trace_add_event_call(*call, mod,
|
|
|
- &file_ops->id, &file_ops->enable,
|
|
|
- &file_ops->filter, &file_ops->format);
|
|
|
+ __register_event(*call, mod);
|
|
|
+ __add_event_to_tracers(*call, file_ops);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1444,6 +1652,10 @@ static int trace_module_notify(struct notifier_block *self,
|
|
|
return 0;
|
|
|
}
|
|
|
#else
|
|
|
+static struct ftrace_module_file_ops *find_ftrace_file_ops(struct module *mod)
|
|
|
+{
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
static int trace_module_notify(struct notifier_block *self,
|
|
|
unsigned long val, void *data)
|
|
|
{
|
|
@@ -1451,6 +1663,72 @@ static int trace_module_notify(struct notifier_block *self,
|
|
|
}
|
|
|
#endif /* CONFIG_MODULES */
|
|
|
|
|
|
+/* Create a new event directory structure for a trace directory. */
|
|
|
+static void
|
|
|
+__trace_add_event_dirs(struct trace_array *tr)
|
|
|
+{
|
|
|
+ struct ftrace_module_file_ops *file_ops = NULL;
|
|
|
+ struct ftrace_event_call *call;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ list_for_each_entry(call, &ftrace_events, list) {
|
|
|
+ if (call->mod) {
|
|
|
+ /*
|
|
|
+ * Directories for events by modules need to
|
|
|
+ * keep module ref counts when opened (as we don't
|
|
|
+ * want the module to disappear when reading one
|
|
|
+ * of these files). The file_ops keep account of
|
|
|
+ * the module ref count.
|
|
|
+ *
|
|
|
+ * As event_calls are added in groups by module,
|
|
|
+ * when we find one file_ops, we don't need to search for
|
|
|
+ * each call in that module, as the rest should be the
|
|
|
+ * same. Only search for a new one if the last one did
|
|
|
+ * not match.
|
|
|
+ */
|
|
|
+ if (!file_ops || call->mod != file_ops->mod)
|
|
|
+ file_ops = find_ftrace_file_ops(call->mod);
|
|
|
+ if (!file_ops)
|
|
|
+ continue; /* Warn? */
|
|
|
+ ret = __trace_add_new_event(call, tr,
|
|
|
+ &file_ops->id, &file_ops->enable,
|
|
|
+ &file_ops->filter, &file_ops->format);
|
|
|
+ if (ret < 0)
|
|
|
+ pr_warning("Could not create directory for event %s\n",
|
|
|
+ call->name);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ ret = __trace_add_new_event(call, tr,
|
|
|
+ &ftrace_event_id_fops,
|
|
|
+ &ftrace_enable_fops,
|
|
|
+ &ftrace_event_filter_fops,
|
|
|
+ &ftrace_event_format_fops);
|
|
|
+ if (ret < 0)
|
|
|
+ pr_warning("Could not create directory for event %s\n",
|
|
|
+ call->name);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+__add_event_to_tracers(struct ftrace_event_call *call,
|
|
|
+ struct ftrace_module_file_ops *file_ops)
|
|
|
+{
|
|
|
+ struct trace_array *tr;
|
|
|
+
|
|
|
+ list_for_each_entry(tr, &ftrace_trace_arrays, list) {
|
|
|
+ if (file_ops)
|
|
|
+ __trace_add_new_event(call, tr,
|
|
|
+ &file_ops->id, &file_ops->enable,
|
|
|
+ &file_ops->filter, &file_ops->format);
|
|
|
+ else
|
|
|
+ __trace_add_new_event(call, tr,
|
|
|
+ &ftrace_event_id_fops,
|
|
|
+ &ftrace_enable_fops,
|
|
|
+ &ftrace_event_filter_fops,
|
|
|
+ &ftrace_event_format_fops);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static struct notifier_block trace_module_nb = {
|
|
|
.notifier_call = trace_module_notify,
|
|
|
.priority = 0,
|
|
@@ -1471,8 +1749,43 @@ static __init int setup_trace_event(char *str)
|
|
|
}
|
|
|
__setup("trace_event=", setup_trace_event);
|
|
|
|
|
|
+int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
|
|
|
+{
|
|
|
+ struct dentry *d_events;
|
|
|
+ struct dentry *entry;
|
|
|
+
|
|
|
+ entry = debugfs_create_file("set_event", 0644, parent,
|
|
|
+ tr, &ftrace_set_event_fops);
|
|
|
+ if (!entry) {
|
|
|
+ pr_warning("Could not create debugfs 'set_event' entry\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ d_events = debugfs_create_dir("events", parent);
|
|
|
+ if (!d_events)
|
|
|
+ pr_warning("Could not create debugfs 'events' directory\n");
|
|
|
+
|
|
|
+ /* ring buffer internal formats */
|
|
|
+ trace_create_file("header_page", 0444, d_events,
|
|
|
+ ring_buffer_print_page_header,
|
|
|
+ &ftrace_show_header_fops);
|
|
|
+
|
|
|
+ trace_create_file("header_event", 0444, d_events,
|
|
|
+ ring_buffer_print_entry_header,
|
|
|
+ &ftrace_show_header_fops);
|
|
|
+
|
|
|
+ trace_create_file("enable", 0644, d_events,
|
|
|
+ tr, &ftrace_tr_enable_fops);
|
|
|
+
|
|
|
+ tr->event_dir = d_events;
|
|
|
+ __trace_add_event_dirs(tr);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static __init int event_trace_enable(void)
|
|
|
{
|
|
|
+ struct trace_array *tr = top_trace_array();
|
|
|
struct ftrace_event_call **iter, *call;
|
|
|
char *buf = bootup_event_buf;
|
|
|
char *token;
|
|
@@ -1494,7 +1807,7 @@ static __init int event_trace_enable(void)
|
|
|
if (!*token)
|
|
|
continue;
|
|
|
|
|
|
- ret = ftrace_set_clr_event(token, 1);
|
|
|
+ ret = ftrace_set_clr_event(tr, token, 1);
|
|
|
if (ret)
|
|
|
pr_warn("Failed to enable trace event: %s\n", token);
|
|
|
}
|
|
@@ -1506,61 +1819,29 @@ static __init int event_trace_enable(void)
|
|
|
|
|
|
static __init int event_trace_init(void)
|
|
|
{
|
|
|
- struct ftrace_event_call *call;
|
|
|
+ struct trace_array *tr;
|
|
|
struct dentry *d_tracer;
|
|
|
struct dentry *entry;
|
|
|
- struct dentry *d_events;
|
|
|
int ret;
|
|
|
|
|
|
+ tr = top_trace_array();
|
|
|
+
|
|
|
d_tracer = tracing_init_dentry();
|
|
|
if (!d_tracer)
|
|
|
return 0;
|
|
|
|
|
|
entry = debugfs_create_file("available_events", 0444, d_tracer,
|
|
|
- NULL, &ftrace_avail_fops);
|
|
|
+ tr, &ftrace_avail_fops);
|
|
|
if (!entry)
|
|
|
pr_warning("Could not create debugfs "
|
|
|
"'available_events' entry\n");
|
|
|
|
|
|
- entry = debugfs_create_file("set_event", 0644, d_tracer,
|
|
|
- NULL, &ftrace_set_event_fops);
|
|
|
- if (!entry)
|
|
|
- pr_warning("Could not create debugfs "
|
|
|
- "'set_event' entry\n");
|
|
|
-
|
|
|
- d_events = event_trace_events_dir();
|
|
|
- if (!d_events)
|
|
|
- return 0;
|
|
|
-
|
|
|
- /* ring buffer internal formats */
|
|
|
- trace_create_file("header_page", 0444, d_events,
|
|
|
- ring_buffer_print_page_header,
|
|
|
- &ftrace_show_header_fops);
|
|
|
-
|
|
|
- trace_create_file("header_event", 0444, d_events,
|
|
|
- ring_buffer_print_entry_header,
|
|
|
- &ftrace_show_header_fops);
|
|
|
-
|
|
|
- trace_create_file("enable", 0644, d_events,
|
|
|
- NULL, &ftrace_system_enable_fops);
|
|
|
-
|
|
|
if (trace_define_common_fields())
|
|
|
pr_warning("tracing: Failed to allocate common fields");
|
|
|
|
|
|
- /*
|
|
|
- * Early initialization already enabled ftrace event.
|
|
|
- * Now it's only necessary to create the event directory.
|
|
|
- */
|
|
|
- list_for_each_entry(call, &ftrace_events, list) {
|
|
|
-
|
|
|
- ret = event_create_dir(call, d_events,
|
|
|
- &ftrace_event_id_fops,
|
|
|
- &ftrace_enable_fops,
|
|
|
- &ftrace_event_filter_fops,
|
|
|
- &ftrace_event_format_fops);
|
|
|
- if (ret < 0)
|
|
|
- event_remove(call);
|
|
|
- }
|
|
|
+ ret = event_trace_add_tracer(d_tracer, tr);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
ret = register_module_notifier(&trace_module_nb);
|
|
|
if (ret)
|
|
@@ -1627,13 +1908,20 @@ static __init void event_test_stuff(void)
|
|
|
*/
|
|
|
static __init void event_trace_self_tests(void)
|
|
|
{
|
|
|
+ struct ftrace_subsystem_dir *dir;
|
|
|
+ struct ftrace_event_file *file;
|
|
|
struct ftrace_event_call *call;
|
|
|
struct event_subsystem *system;
|
|
|
+ struct trace_array *tr;
|
|
|
int ret;
|
|
|
|
|
|
+ tr = top_trace_array();
|
|
|
+
|
|
|
pr_info("Running tests on trace events:\n");
|
|
|
|
|
|
- list_for_each_entry(call, &ftrace_events, list) {
|
|
|
+ list_for_each_entry(file, &tr->events, list) {
|
|
|
+
|
|
|
+ call = file->event_call;
|
|
|
|
|
|
/* Only test those that have a probe */
|
|
|
if (!call->class || !call->class->probe)
|
|
@@ -1657,15 +1945,15 @@ static __init void event_trace_self_tests(void)
|
|
|
* If an event is already enabled, someone is using
|
|
|
* it and the self test should not be on.
|
|
|
*/
|
|
|
- if (call->flags & TRACE_EVENT_FL_ENABLED) {
|
|
|
+ if (file->flags & FTRACE_EVENT_FL_ENABLED) {
|
|
|
pr_warning("Enabled event during self test!\n");
|
|
|
WARN_ON_ONCE(1);
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- ftrace_event_enable_disable(call, 1);
|
|
|
+ ftrace_event_enable_disable(file, 1);
|
|
|
event_test_stuff();
|
|
|
- ftrace_event_enable_disable(call, 0);
|
|
|
+ ftrace_event_enable_disable(file, 0);
|
|
|
|
|
|
pr_cont("OK\n");
|
|
|
}
|
|
@@ -1674,7 +1962,9 @@ static __init void event_trace_self_tests(void)
|
|
|
|
|
|
pr_info("Running tests on trace event systems:\n");
|
|
|
|
|
|
- list_for_each_entry(system, &event_subsystems, list) {
|
|
|
+ list_for_each_entry(dir, &tr->systems, list) {
|
|
|
+
|
|
|
+ system = dir->subsystem;
|
|
|
|
|
|
/* the ftrace system is special, skip it */
|
|
|
if (strcmp(system->name, "ftrace") == 0)
|
|
@@ -1682,7 +1972,7 @@ static __init void event_trace_self_tests(void)
|
|
|
|
|
|
pr_info("Testing event system %s: ", system->name);
|
|
|
|
|
|
- ret = __ftrace_set_clr_event(NULL, system->name, NULL, 1);
|
|
|
+ ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 1);
|
|
|
if (WARN_ON_ONCE(ret)) {
|
|
|
pr_warning("error enabling system %s\n",
|
|
|
system->name);
|
|
@@ -1691,7 +1981,7 @@ static __init void event_trace_self_tests(void)
|
|
|
|
|
|
event_test_stuff();
|
|
|
|
|
|
- ret = __ftrace_set_clr_event(NULL, system->name, NULL, 0);
|
|
|
+ ret = __ftrace_set_clr_event(tr, NULL, system->name, NULL, 0);
|
|
|
if (WARN_ON_ONCE(ret)) {
|
|
|
pr_warning("error disabling system %s\n",
|
|
|
system->name);
|
|
@@ -1706,7 +1996,7 @@ static __init void event_trace_self_tests(void)
|
|
|
pr_info("Running tests on all trace events:\n");
|
|
|
pr_info("Testing all events: ");
|
|
|
|
|
|
- ret = __ftrace_set_clr_event(NULL, NULL, NULL, 1);
|
|
|
+ ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 1);
|
|
|
if (WARN_ON_ONCE(ret)) {
|
|
|
pr_warning("error enabling all events\n");
|
|
|
return;
|
|
@@ -1715,7 +2005,7 @@ static __init void event_trace_self_tests(void)
|
|
|
event_test_stuff();
|
|
|
|
|
|
/* reset sysname */
|
|
|
- ret = __ftrace_set_clr_event(NULL, NULL, NULL, 0);
|
|
|
+ ret = __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0);
|
|
|
if (WARN_ON_ONCE(ret)) {
|
|
|
pr_warning("error disabling all events\n");
|
|
|
return;
|