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:
parent
17c873ec28
commit
6d723736e4
@ -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;
|
||||||
|
@ -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? */
|
||||||
|
@ -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.
|
||||||
|
@ -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, \
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user