tracing/events: add support for modules to TRACE_EVENT

Impact: allow modules to add TRACE_EVENTS on load

This patch adds the final hooks to allow modules to use the TRACE_EVENT
macro. A notifier and a data structure are used to link the TRACE_EVENTs
defined in the module to connect them with the ftrace event tracing system.

It also adds the necessary automated clean ups to the trace events when a
module is removed.

Cc: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
This commit is contained in:
Steven Rostedt 2009-04-10 14:53:50 -04:00 committed by Steven Rostedt
parent 17c873ec28
commit 6d723736e4
6 changed files with 113 additions and 32 deletions

View File

@ -7,6 +7,7 @@
struct trace_array; struct trace_array;
struct tracer; struct tracer;
struct dentry;
/* /*
* The trace entry - the most basic unit of tracing. This is what * The trace entry - the most basic unit of tracing. This is what
@ -87,6 +88,7 @@ struct ftrace_event_call {
char *name; char *name;
char *system; char *system;
struct dentry *dir; struct dentry *dir;
struct trace_event *event;
int enabled; int enabled;
int (*regfunc)(void); int (*regfunc)(void);
void (*unregfunc)(void); void (*unregfunc)(void);
@ -97,6 +99,7 @@ struct ftrace_event_call {
struct list_head fields; struct list_head fields;
int n_preds; int n_preds;
struct filter_pred **preds; struct filter_pred **preds;
void *mod;
#ifdef CONFIG_EVENT_PROFILE #ifdef CONFIG_EVENT_PROFILE
atomic_t profile_count; atomic_t profile_count;

View File

@ -337,6 +337,10 @@ struct module
const char **trace_bprintk_fmt_start; const char **trace_bprintk_fmt_start;
unsigned int num_trace_bprintk_fmt; unsigned int num_trace_bprintk_fmt;
#endif #endif
#ifdef CONFIG_EVENT_TRACING
struct ftrace_event_call *trace_events;
unsigned int num_trace_events;
#endif
#ifdef CONFIG_MODULE_UNLOAD #ifdef CONFIG_MODULE_UNLOAD
/* What modules depend on me? */ /* What modules depend on me? */

View File

@ -1,6 +1,8 @@
#ifndef _LINUX_TRACE_SEQ_H #ifndef _LINUX_TRACE_SEQ_H
#define _LINUX_TRACE_SEQ_H #define _LINUX_TRACE_SEQ_H
#include <linux/fs.h>
/* /*
* Trace sequences are used to allow a function to call several other functions * Trace sequences are used to allow a function to call several other functions
* to create a string of data to use (up to a max of PAGE_SIZE. * to create a string of data to use (up to a max of PAGE_SIZE.

View File

@ -477,6 +477,7 @@ __attribute__((__aligned__(4))) \
__attribute__((section("_ftrace_events"))) event_##call = { \ __attribute__((section("_ftrace_events"))) event_##call = { \
.name = #call, \ .name = #call, \
.system = __stringify(TRACE_SYSTEM), \ .system = __stringify(TRACE_SYSTEM), \
.event = &ftrace_event_type_##call, \
.raw_init = ftrace_raw_init_event_##call, \ .raw_init = ftrace_raw_init_event_##call, \
.regfunc = ftrace_raw_reg_event_##call, \ .regfunc = ftrace_raw_reg_event_##call, \
.unregfunc = ftrace_raw_unreg_event_##call, \ .unregfunc = ftrace_raw_unreg_event_##call, \

View File

@ -18,6 +18,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleloader.h> #include <linux/moduleloader.h>
#include <linux/ftrace_event.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/fs.h> #include <linux/fs.h>
@ -2172,6 +2173,12 @@ static noinline struct module *load_module(void __user *umod,
sizeof(*mod->tracepoints), sizeof(*mod->tracepoints),
&mod->num_tracepoints); &mod->num_tracepoints);
#endif #endif
#ifdef CONFIG_EVENT_TRACING
mod->trace_events = section_objs(hdr, sechdrs, secstrings,
"_ftrace_events",
sizeof(*mod->trace_events),
&mod->num_trace_events);
#endif
#ifdef CONFIG_MODVERSIONS #ifdef CONFIG_MODVERSIONS
if ((mod->num_syms && !mod->crcs) if ((mod->num_syms && !mod->crcs)

View File

@ -713,7 +713,13 @@ event_subsystem_dir(const char *name, struct dentry *d_events)
return d_events; return d_events;
} }
system->name = name; system->name = kstrdup(name, GFP_KERNEL);
if (!system->name) {
debugfs_remove(system->entry);
kfree(system);
return d_events;
}
list_add(&system->list, &event_subsystems); list_add(&system->list, &event_subsystems);
system->preds = NULL; system->preds = NULL;
@ -738,7 +744,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
* If the trace point header did not define TRACE_SYSTEM * If the trace point header did not define TRACE_SYSTEM
* then the system would be called "TRACE_SYSTEM". * then the system would be called "TRACE_SYSTEM".
*/ */
if (strcmp(call->system, "TRACE_SYSTEM") != 0) if (strcmp(call->system, TRACE_SYSTEM) != 0)
d_events = event_subsystem_dir(call->system, d_events); d_events = event_subsystem_dir(call->system, d_events);
if (call->raw_init) { if (call->raw_init) {
@ -757,21 +763,13 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
return -1; return -1;
} }
if (call->regfunc) { if (call->regfunc)
entry = debugfs_create_file("enable", 0644, call->dir, call, entry = trace_create_file("enable", 0644, call->dir, call,
&ftrace_enable_fops); &ftrace_enable_fops);
if (!entry)
pr_warning("Could not create debugfs "
"'%s/enable' entry\n", call->name);
}
if (call->id) { if (call->id)
entry = debugfs_create_file("id", 0444, call->dir, call, entry = trace_create_file("id", 0444, call->dir, call,
&ftrace_event_id_fops); &ftrace_event_id_fops);
if (!entry)
pr_warning("Could not create debugfs '%s/id' entry\n",
call->name);
}
if (call->define_fields) { if (call->define_fields) {
ret = call->define_fields(); ret = call->define_fields();
@ -780,40 +778,102 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events)
" events/%s\n", call->name); " events/%s\n", call->name);
return ret; return ret;
} }
entry = debugfs_create_file("filter", 0644, call->dir, call, entry = trace_create_file("filter", 0644, call->dir, call,
&ftrace_event_filter_fops); &ftrace_event_filter_fops);
if (!entry)
pr_warning("Could not create debugfs "
"'%s/filter' entry\n", call->name);
} }
/* A trace may not want to export its format */ /* A trace may not want to export its format */
if (!call->show_format) if (!call->show_format)
return 0; return 0;
entry = debugfs_create_file("format", 0444, call->dir, call, entry = trace_create_file("format", 0444, call->dir, call,
&ftrace_event_format_fops); &ftrace_event_format_fops);
if (!entry)
pr_warning("Could not create debugfs "
"'%s/format' entry\n", call->name);
return 0; return 0;
} }
#define for_each_event(event, start, end) \
for (event = start; \
(unsigned long)event < (unsigned long)end; \
event++)
static void trace_module_add_events(struct module *mod)
{
struct ftrace_event_call *call, *start, *end;
struct dentry *d_events;
start = mod->trace_events;
end = mod->trace_events + mod->num_trace_events;
if (start == end)
return;
d_events = event_trace_events_dir();
if (!d_events)
return;
for_each_event(call, start, end) {
/* The linker may leave blanks */
if (!call->name)
continue;
call->mod = mod;
list_add(&call->list, &ftrace_events);
event_create_dir(call, d_events);
}
}
static void trace_module_remove_events(struct module *mod)
{
struct ftrace_event_call *call, *p;
list_for_each_entry_safe(call, p, &ftrace_events, list) {
if (call->mod == mod) {
if (call->enabled) {
call->enabled = 0;
call->unregfunc();
}
if (call->event)
unregister_ftrace_event(call->event);
debugfs_remove_recursive(call->dir);
list_del(&call->list);
}
}
}
int trace_module_notify(struct notifier_block *self,
unsigned long val, void *data)
{
struct module *mod = data;
mutex_lock(&event_mutex);
switch (val) {
case MODULE_STATE_COMING:
trace_module_add_events(mod);
break;
case MODULE_STATE_GOING:
trace_module_remove_events(mod);
break;
}
mutex_unlock(&event_mutex);
return 0;
}
struct notifier_block trace_module_nb = {
.notifier_call = trace_module_notify,
.priority = 0,
};
extern struct ftrace_event_call __start_ftrace_events[]; extern struct ftrace_event_call __start_ftrace_events[];
extern struct ftrace_event_call __stop_ftrace_events[]; extern struct ftrace_event_call __stop_ftrace_events[];
#define for_each_event(event) \
for (event = __start_ftrace_events; \
(unsigned long)event < (unsigned long)__stop_ftrace_events; \
event++)
static __init int event_trace_init(void) static __init int event_trace_init(void)
{ {
struct ftrace_event_call *call; struct ftrace_event_call *call;
struct dentry *d_tracer; struct dentry *d_tracer;
struct dentry *entry; struct dentry *entry;
struct dentry *d_events; struct dentry *d_events;
int ret;
d_tracer = tracing_init_dentry(); d_tracer = tracing_init_dentry();
if (!d_tracer) if (!d_tracer)
@ -837,7 +897,7 @@ static __init int event_trace_init(void)
if (!d_events) if (!d_events)
return 0; return 0;
for_each_event(call) { for_each_event(call, __start_ftrace_events, __stop_ftrace_events) {
/* The linker may leave blanks */ /* The linker may leave blanks */
if (!call->name) if (!call->name)
continue; continue;
@ -845,6 +905,10 @@ static __init int event_trace_init(void)
event_create_dir(call, d_events); event_create_dir(call, d_events);
} }
ret = register_module_notifier(&trace_module_nb);
if (!ret)
pr_warning("Failed to register trace events module notifier\n");
return 0; return 0;
} }
fs_initcall(event_trace_init); fs_initcall(event_trace_init);