diff options
author | Steven Rostedt <srostedt@redhat.com> | 2009-04-10 14:53:50 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2009-04-14 12:58:03 -0400 |
commit | 6d723736e472f7a0cd5b62c84152fceead241328 (patch) | |
tree | 8df2f6c47ebdfdeb8979758c877a5abbd9c06aef | |
parent | 17c873ec280a03894bc718af817f7f24fa787ae1 (diff) |
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>
-rw-r--r-- | include/linux/ftrace_event.h | 3 | ||||
-rw-r--r-- | include/linux/module.h | 4 | ||||
-rw-r--r-- | include/linux/trace_seq.h | 2 | ||||
-rw-r--r-- | include/trace/ftrace.h | 1 | ||||
-rw-r--r-- | kernel/module.c | 7 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 128 |
6 files changed, 113 insertions, 32 deletions
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 17810853b4f8..75f3ac01a87c 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | struct trace_array; | 8 | struct trace_array; |
9 | struct tracer; | 9 | struct tracer; |
10 | struct dentry; | ||
10 | 11 | ||
11 | /* | 12 | /* |
12 | * The trace entry - the most basic unit of tracing. This is what | 13 | * The trace entry - the most basic unit of tracing. This is what |
@@ -87,6 +88,7 @@ struct ftrace_event_call { | |||
87 | char *name; | 88 | char *name; |
88 | char *system; | 89 | char *system; |
89 | struct dentry *dir; | 90 | struct dentry *dir; |
91 | struct trace_event *event; | ||
90 | int enabled; | 92 | int enabled; |
91 | int (*regfunc)(void); | 93 | int (*regfunc)(void); |
92 | void (*unregfunc)(void); | 94 | void (*unregfunc)(void); |
@@ -97,6 +99,7 @@ struct ftrace_event_call { | |||
97 | struct list_head fields; | 99 | struct list_head fields; |
98 | int n_preds; | 100 | int n_preds; |
99 | struct filter_pred **preds; | 101 | struct filter_pred **preds; |
102 | void *mod; | ||
100 | 103 | ||
101 | #ifdef CONFIG_EVENT_PROFILE | 104 | #ifdef CONFIG_EVENT_PROFILE |
102 | atomic_t profile_count; | 105 | atomic_t profile_count; |
diff --git a/include/linux/module.h b/include/linux/module.h index 627ac082e2a6..6155fa44168b 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
@@ -337,6 +337,10 @@ struct module | |||
337 | const char **trace_bprintk_fmt_start; | 337 | const char **trace_bprintk_fmt_start; |
338 | unsigned int num_trace_bprintk_fmt; | 338 | unsigned int num_trace_bprintk_fmt; |
339 | #endif | 339 | #endif |
340 | #ifdef CONFIG_EVENT_TRACING | ||
341 | struct ftrace_event_call *trace_events; | ||
342 | unsigned int num_trace_events; | ||
343 | #endif | ||
340 | 344 | ||
341 | #ifdef CONFIG_MODULE_UNLOAD | 345 | #ifdef CONFIG_MODULE_UNLOAD |
342 | /* What modules depend on me? */ | 346 | /* What modules depend on me? */ |
diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 28051da876dd..15ca2c71af13 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _LINUX_TRACE_SEQ_H | 1 | #ifndef _LINUX_TRACE_SEQ_H |
2 | #define _LINUX_TRACE_SEQ_H | 2 | #define _LINUX_TRACE_SEQ_H |
3 | 3 | ||
4 | #include <linux/fs.h> | ||
5 | |||
4 | /* | 6 | /* |
5 | * Trace sequences are used to allow a function to call several other functions | 7 | * Trace sequences are used to allow a function to call several other functions |
6 | * to create a string of data to use (up to a max of PAGE_SIZE. | 8 | * to create a string of data to use (up to a max of PAGE_SIZE. |
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 955b967acd74..60c5323bee64 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h | |||
@@ -477,6 +477,7 @@ __attribute__((__aligned__(4))) \ | |||
477 | __attribute__((section("_ftrace_events"))) event_##call = { \ | 477 | __attribute__((section("_ftrace_events"))) event_##call = { \ |
478 | .name = #call, \ | 478 | .name = #call, \ |
479 | .system = __stringify(TRACE_SYSTEM), \ | 479 | .system = __stringify(TRACE_SYSTEM), \ |
480 | .event = &ftrace_event_type_##call, \ | ||
480 | .raw_init = ftrace_raw_init_event_##call, \ | 481 | .raw_init = ftrace_raw_init_event_##call, \ |
481 | .regfunc = ftrace_raw_reg_event_##call, \ | 482 | .regfunc = ftrace_raw_reg_event_##call, \ |
482 | .unregfunc = ftrace_raw_unreg_event_##call, \ | 483 | .unregfunc = ftrace_raw_unreg_event_##call, \ |
diff --git a/kernel/module.c b/kernel/module.c index e797812a4d95..a0394706f10c 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -18,6 +18,7 @@ | |||
18 | */ | 18 | */ |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/moduleloader.h> | 20 | #include <linux/moduleloader.h> |
21 | #include <linux/ftrace_event.h> | ||
21 | #include <linux/init.h> | 22 | #include <linux/init.h> |
22 | #include <linux/kallsyms.h> | 23 | #include <linux/kallsyms.h> |
23 | #include <linux/fs.h> | 24 | #include <linux/fs.h> |
@@ -2172,6 +2173,12 @@ static noinline struct module *load_module(void __user *umod, | |||
2172 | sizeof(*mod->tracepoints), | 2173 | sizeof(*mod->tracepoints), |
2173 | &mod->num_tracepoints); | 2174 | &mod->num_tracepoints); |
2174 | #endif | 2175 | #endif |
2176 | #ifdef CONFIG_EVENT_TRACING | ||
2177 | mod->trace_events = section_objs(hdr, sechdrs, secstrings, | ||
2178 | "_ftrace_events", | ||
2179 | sizeof(*mod->trace_events), | ||
2180 | &mod->num_trace_events); | ||
2181 | #endif | ||
2175 | 2182 | ||
2176 | #ifdef CONFIG_MODVERSIONS | 2183 | #ifdef CONFIG_MODVERSIONS |
2177 | if ((mod->num_syms && !mod->crcs) | 2184 | if ((mod->num_syms && !mod->crcs) |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 8b9e621b80b4..a4b177720a6c 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -713,7 +713,13 @@ event_subsystem_dir(const char *name, struct dentry *d_events) | |||
713 | return d_events; | 713 | return d_events; |
714 | } | 714 | } |
715 | 715 | ||
716 | system->name = name; | 716 | system->name = kstrdup(name, GFP_KERNEL); |
717 | if (!system->name) { | ||
718 | debugfs_remove(system->entry); | ||
719 | kfree(system); | ||
720 | return d_events; | ||
721 | } | ||
722 | |||
717 | list_add(&system->list, &event_subsystems); | 723 | list_add(&system->list, &event_subsystems); |
718 | 724 | ||
719 | system->preds = NULL; | 725 | system->preds = NULL; |
@@ -738,7 +744,7 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
738 | * If the trace point header did not define TRACE_SYSTEM | 744 | * If the trace point header did not define TRACE_SYSTEM |
739 | * then the system would be called "TRACE_SYSTEM". | 745 | * then the system would be called "TRACE_SYSTEM". |
740 | */ | 746 | */ |
741 | if (strcmp(call->system, "TRACE_SYSTEM") != 0) | 747 | if (strcmp(call->system, TRACE_SYSTEM) != 0) |
742 | d_events = event_subsystem_dir(call->system, d_events); | 748 | d_events = event_subsystem_dir(call->system, d_events); |
743 | 749 | ||
744 | if (call->raw_init) { | 750 | if (call->raw_init) { |
@@ -757,21 +763,13 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
757 | return -1; | 763 | return -1; |
758 | } | 764 | } |
759 | 765 | ||
760 | if (call->regfunc) { | 766 | if (call->regfunc) |
761 | entry = debugfs_create_file("enable", 0644, call->dir, call, | 767 | entry = trace_create_file("enable", 0644, call->dir, call, |
762 | &ftrace_enable_fops); | 768 | &ftrace_enable_fops); |
763 | if (!entry) | ||
764 | pr_warning("Could not create debugfs " | ||
765 | "'%s/enable' entry\n", call->name); | ||
766 | } | ||
767 | 769 | ||
768 | if (call->id) { | 770 | if (call->id) |
769 | entry = debugfs_create_file("id", 0444, call->dir, call, | 771 | entry = trace_create_file("id", 0444, call->dir, call, |
770 | &ftrace_event_id_fops); | 772 | &ftrace_event_id_fops); |
771 | if (!entry) | ||
772 | pr_warning("Could not create debugfs '%s/id' entry\n", | ||
773 | call->name); | ||
774 | } | ||
775 | 773 | ||
776 | if (call->define_fields) { | 774 | if (call->define_fields) { |
777 | ret = call->define_fields(); | 775 | ret = call->define_fields(); |
@@ -780,40 +778,102 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events) | |||
780 | " events/%s\n", call->name); | 778 | " events/%s\n", call->name); |
781 | return ret; | 779 | return ret; |
782 | } | 780 | } |
783 | entry = debugfs_create_file("filter", 0644, call->dir, call, | 781 | entry = trace_create_file("filter", 0644, call->dir, call, |
784 | &ftrace_event_filter_fops); | 782 | &ftrace_event_filter_fops); |
785 | if (!entry) | ||
786 | pr_warning("Could not create debugfs " | ||
787 | "'%s/filter' entry\n", call->name); | ||
788 | } | 783 | } |
789 | 784 | ||
790 | /* A trace may not want to export its format */ | 785 | /* A trace may not want to export its format */ |
791 | if (!call->show_format) | 786 | if (!call->show_format) |
792 | return 0; | 787 | return 0; |
793 | 788 | ||
794 | entry = debugfs_create_file("format", 0444, call->dir, call, | 789 | entry = trace_create_file("format", 0444, call->dir, call, |
795 | &ftrace_event_format_fops); | 790 | &ftrace_event_format_fops); |
796 | if (!entry) | 791 | |
797 | pr_warning("Could not create debugfs " | 792 | return 0; |
798 | "'%s/format' entry\n", call->name); | 793 | } |
794 | |||
795 | #define for_each_event(event, start, end) \ | ||
796 | for (event = start; \ | ||
797 | (unsigned long)event < (unsigned long)end; \ | ||
798 | event++) | ||
799 | |||
800 | static void trace_module_add_events(struct module *mod) | ||
801 | { | ||
802 | struct ftrace_event_call *call, *start, *end; | ||
803 | struct dentry *d_events; | ||
804 | |||
805 | start = mod->trace_events; | ||
806 | end = mod->trace_events + mod->num_trace_events; | ||
807 | |||
808 | if (start == end) | ||
809 | return; | ||
810 | |||
811 | d_events = event_trace_events_dir(); | ||
812 | if (!d_events) | ||
813 | return; | ||
814 | |||
815 | for_each_event(call, start, end) { | ||
816 | /* The linker may leave blanks */ | ||
817 | if (!call->name) | ||
818 | continue; | ||
819 | call->mod = mod; | ||
820 | list_add(&call->list, &ftrace_events); | ||
821 | event_create_dir(call, d_events); | ||
822 | } | ||
823 | } | ||
824 | |||
825 | static void trace_module_remove_events(struct module *mod) | ||
826 | { | ||
827 | struct ftrace_event_call *call, *p; | ||
828 | |||
829 | list_for_each_entry_safe(call, p, &ftrace_events, list) { | ||
830 | if (call->mod == mod) { | ||
831 | if (call->enabled) { | ||
832 | call->enabled = 0; | ||
833 | call->unregfunc(); | ||
834 | } | ||
835 | if (call->event) | ||
836 | unregister_ftrace_event(call->event); | ||
837 | debugfs_remove_recursive(call->dir); | ||
838 | list_del(&call->list); | ||
839 | } | ||
840 | } | ||
841 | } | ||
842 | |||
843 | int trace_module_notify(struct notifier_block *self, | ||
844 | unsigned long val, void *data) | ||
845 | { | ||
846 | struct module *mod = data; | ||
847 | |||
848 | mutex_lock(&event_mutex); | ||
849 | switch (val) { | ||
850 | case MODULE_STATE_COMING: | ||
851 | trace_module_add_events(mod); | ||
852 | break; | ||
853 | case MODULE_STATE_GOING: | ||
854 | trace_module_remove_events(mod); | ||
855 | break; | ||
856 | } | ||
857 | mutex_unlock(&event_mutex); | ||
799 | 858 | ||
800 | return 0; | 859 | return 0; |
801 | } | 860 | } |
802 | 861 | ||
862 | struct notifier_block trace_module_nb = { | ||
863 | .notifier_call = trace_module_notify, | ||
864 | .priority = 0, | ||
865 | }; | ||
866 | |||
803 | extern struct ftrace_event_call __start_ftrace_events[]; | 867 | extern struct ftrace_event_call __start_ftrace_events[]; |
804 | extern struct ftrace_event_call __stop_ftrace_events[]; | 868 | extern struct ftrace_event_call __stop_ftrace_events[]; |
805 | 869 | ||
806 | #define for_each_event(event) \ | ||
807 | for (event = __start_ftrace_events; \ | ||
808 | (unsigned long)event < (unsigned long)__stop_ftrace_events; \ | ||
809 | event++) | ||
810 | |||
811 | static __init int event_trace_init(void) | 870 | static __init int event_trace_init(void) |
812 | { | 871 | { |
813 | struct ftrace_event_call *call; | 872 | struct ftrace_event_call *call; |
814 | struct dentry *d_tracer; | 873 | struct dentry *d_tracer; |
815 | struct dentry *entry; | 874 | struct dentry *entry; |
816 | struct dentry *d_events; | 875 | struct dentry *d_events; |
876 | int ret; | ||
817 | 877 | ||
818 | d_tracer = tracing_init_dentry(); | 878 | d_tracer = tracing_init_dentry(); |
819 | if (!d_tracer) | 879 | if (!d_tracer) |
@@ -837,7 +897,7 @@ static __init int event_trace_init(void) | |||
837 | if (!d_events) | 897 | if (!d_events) |
838 | return 0; | 898 | return 0; |
839 | 899 | ||
840 | for_each_event(call) { | 900 | for_each_event(call, __start_ftrace_events, __stop_ftrace_events) { |
841 | /* The linker may leave blanks */ | 901 | /* The linker may leave blanks */ |
842 | if (!call->name) | 902 | if (!call->name) |
843 | continue; | 903 | continue; |
@@ -845,6 +905,10 @@ static __init int event_trace_init(void) | |||
845 | event_create_dir(call, d_events); | 905 | event_create_dir(call, d_events); |
846 | } | 906 | } |
847 | 907 | ||
908 | ret = register_module_notifier(&trace_module_nb); | ||
909 | if (!ret) | ||
910 | pr_warning("Failed to register trace events module notifier\n"); | ||
911 | |||
848 | return 0; | 912 | return 0; |
849 | } | 913 | } |
850 | fs_initcall(event_trace_init); | 914 | fs_initcall(event_trace_init); |