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 /kernel | |
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>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/module.c | 7 | ||||
-rw-r--r-- | kernel/trace/trace_events.c | 128 |
2 files changed, 103 insertions, 32 deletions
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); |