diff options
Diffstat (limited to 'kernel/trace/trace.c')
-rw-r--r-- | kernel/trace/trace.c | 304 |
1 files changed, 303 insertions, 1 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index bcfa2add6dda..91eecaaa43e0 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -125,6 +125,42 @@ enum ftrace_dump_mode ftrace_dump_on_oops; | |||
125 | /* When set, tracing will stop when a WARN*() is hit */ | 125 | /* When set, tracing will stop when a WARN*() is hit */ |
126 | int __disable_trace_on_warning; | 126 | int __disable_trace_on_warning; |
127 | 127 | ||
128 | #ifdef CONFIG_TRACE_ENUM_MAP_FILE | ||
129 | /* Map of enums to their values, for "enum_map" file */ | ||
130 | struct trace_enum_map_head { | ||
131 | struct module *mod; | ||
132 | unsigned long length; | ||
133 | }; | ||
134 | |||
135 | union trace_enum_map_item; | ||
136 | |||
137 | struct trace_enum_map_tail { | ||
138 | /* | ||
139 | * "end" is first and points to NULL as it must be different | ||
140 | * than "mod" or "enum_string" | ||
141 | */ | ||
142 | union trace_enum_map_item *next; | ||
143 | const char *end; /* points to NULL */ | ||
144 | }; | ||
145 | |||
146 | static DEFINE_MUTEX(trace_enum_mutex); | ||
147 | |||
148 | /* | ||
149 | * The trace_enum_maps are saved in an array with two extra elements, | ||
150 | * one at the beginning, and one at the end. The beginning item contains | ||
151 | * the count of the saved maps (head.length), and the module they | ||
152 | * belong to if not built in (head.mod). The ending item contains a | ||
153 | * pointer to the next array of saved enum_map items. | ||
154 | */ | ||
155 | union trace_enum_map_item { | ||
156 | struct trace_enum_map map; | ||
157 | struct trace_enum_map_head head; | ||
158 | struct trace_enum_map_tail tail; | ||
159 | }; | ||
160 | |||
161 | static union trace_enum_map_item *trace_enum_maps; | ||
162 | #endif /* CONFIG_TRACE_ENUM_MAP_FILE */ | ||
163 | |||
128 | static int tracing_set_tracer(struct trace_array *tr, const char *buf); | 164 | static int tracing_set_tracer(struct trace_array *tr, const char *buf); |
129 | 165 | ||
130 | #define MAX_TRACER_SIZE 100 | 166 | #define MAX_TRACER_SIZE 100 |
@@ -3910,6 +3946,182 @@ static const struct file_operations tracing_saved_cmdlines_size_fops = { | |||
3910 | .write = tracing_saved_cmdlines_size_write, | 3946 | .write = tracing_saved_cmdlines_size_write, |
3911 | }; | 3947 | }; |
3912 | 3948 | ||
3949 | #ifdef CONFIG_TRACE_ENUM_MAP_FILE | ||
3950 | static union trace_enum_map_item * | ||
3951 | update_enum_map(union trace_enum_map_item *ptr) | ||
3952 | { | ||
3953 | if (!ptr->map.enum_string) { | ||
3954 | if (ptr->tail.next) { | ||
3955 | ptr = ptr->tail.next; | ||
3956 | /* Set ptr to the next real item (skip head) */ | ||
3957 | ptr++; | ||
3958 | } else | ||
3959 | return NULL; | ||
3960 | } | ||
3961 | return ptr; | ||
3962 | } | ||
3963 | |||
3964 | static void *enum_map_next(struct seq_file *m, void *v, loff_t *pos) | ||
3965 | { | ||
3966 | union trace_enum_map_item *ptr = v; | ||
3967 | |||
3968 | /* | ||
3969 | * Paranoid! If ptr points to end, we don't want to increment past it. | ||
3970 | * This really should never happen. | ||
3971 | */ | ||
3972 | ptr = update_enum_map(ptr); | ||
3973 | if (WARN_ON_ONCE(!ptr)) | ||
3974 | return NULL; | ||
3975 | |||
3976 | ptr++; | ||
3977 | |||
3978 | (*pos)++; | ||
3979 | |||
3980 | ptr = update_enum_map(ptr); | ||
3981 | |||
3982 | return ptr; | ||
3983 | } | ||
3984 | |||
3985 | static void *enum_map_start(struct seq_file *m, loff_t *pos) | ||
3986 | { | ||
3987 | union trace_enum_map_item *v; | ||
3988 | loff_t l = 0; | ||
3989 | |||
3990 | mutex_lock(&trace_enum_mutex); | ||
3991 | |||
3992 | v = trace_enum_maps; | ||
3993 | if (v) | ||
3994 | v++; | ||
3995 | |||
3996 | while (v && l < *pos) { | ||
3997 | v = enum_map_next(m, v, &l); | ||
3998 | } | ||
3999 | |||
4000 | return v; | ||
4001 | } | ||
4002 | |||
4003 | static void enum_map_stop(struct seq_file *m, void *v) | ||
4004 | { | ||
4005 | mutex_unlock(&trace_enum_mutex); | ||
4006 | } | ||
4007 | |||
4008 | static int enum_map_show(struct seq_file *m, void *v) | ||
4009 | { | ||
4010 | union trace_enum_map_item *ptr = v; | ||
4011 | |||
4012 | seq_printf(m, "%s %ld (%s)\n", | ||
4013 | ptr->map.enum_string, ptr->map.enum_value, | ||
4014 | ptr->map.system); | ||
4015 | |||
4016 | return 0; | ||
4017 | } | ||
4018 | |||
4019 | static const struct seq_operations tracing_enum_map_seq_ops = { | ||
4020 | .start = enum_map_start, | ||
4021 | .next = enum_map_next, | ||
4022 | .stop = enum_map_stop, | ||
4023 | .show = enum_map_show, | ||
4024 | }; | ||
4025 | |||
4026 | static int tracing_enum_map_open(struct inode *inode, struct file *filp) | ||
4027 | { | ||
4028 | if (tracing_disabled) | ||
4029 | return -ENODEV; | ||
4030 | |||
4031 | return seq_open(filp, &tracing_enum_map_seq_ops); | ||
4032 | } | ||
4033 | |||
4034 | static const struct file_operations tracing_enum_map_fops = { | ||
4035 | .open = tracing_enum_map_open, | ||
4036 | .read = seq_read, | ||
4037 | .llseek = seq_lseek, | ||
4038 | .release = seq_release, | ||
4039 | }; | ||
4040 | |||
4041 | static inline union trace_enum_map_item * | ||
4042 | trace_enum_jmp_to_tail(union trace_enum_map_item *ptr) | ||
4043 | { | ||
4044 | /* Return tail of array given the head */ | ||
4045 | return ptr + ptr->head.length + 1; | ||
4046 | } | ||
4047 | |||
4048 | static void | ||
4049 | trace_insert_enum_map_file(struct module *mod, struct trace_enum_map **start, | ||
4050 | int len) | ||
4051 | { | ||
4052 | struct trace_enum_map **stop; | ||
4053 | struct trace_enum_map **map; | ||
4054 | union trace_enum_map_item *map_array; | ||
4055 | union trace_enum_map_item *ptr; | ||
4056 | |||
4057 | stop = start + len; | ||
4058 | |||
4059 | /* | ||
4060 | * The trace_enum_maps contains the map plus a head and tail item, | ||
4061 | * where the head holds the module and length of array, and the | ||
4062 | * tail holds a pointer to the next list. | ||
4063 | */ | ||
4064 | map_array = kmalloc(sizeof(*map_array) * (len + 2), GFP_KERNEL); | ||
4065 | if (!map_array) { | ||
4066 | pr_warning("Unable to allocate trace enum mapping\n"); | ||
4067 | return; | ||
4068 | } | ||
4069 | |||
4070 | mutex_lock(&trace_enum_mutex); | ||
4071 | |||
4072 | if (!trace_enum_maps) | ||
4073 | trace_enum_maps = map_array; | ||
4074 | else { | ||
4075 | ptr = trace_enum_maps; | ||
4076 | for (;;) { | ||
4077 | ptr = trace_enum_jmp_to_tail(ptr); | ||
4078 | if (!ptr->tail.next) | ||
4079 | break; | ||
4080 | ptr = ptr->tail.next; | ||
4081 | |||
4082 | } | ||
4083 | ptr->tail.next = map_array; | ||
4084 | } | ||
4085 | map_array->head.mod = mod; | ||
4086 | map_array->head.length = len; | ||
4087 | map_array++; | ||
4088 | |||
4089 | for (map = start; (unsigned long)map < (unsigned long)stop; map++) { | ||
4090 | map_array->map = **map; | ||
4091 | map_array++; | ||
4092 | } | ||
4093 | memset(map_array, 0, sizeof(*map_array)); | ||
4094 | |||
4095 | mutex_unlock(&trace_enum_mutex); | ||
4096 | } | ||
4097 | |||
4098 | static void trace_create_enum_file(struct dentry *d_tracer) | ||
4099 | { | ||
4100 | trace_create_file("enum_map", 0444, d_tracer, | ||
4101 | NULL, &tracing_enum_map_fops); | ||
4102 | } | ||
4103 | |||
4104 | #else /* CONFIG_TRACE_ENUM_MAP_FILE */ | ||
4105 | static inline void trace_create_enum_file(struct dentry *d_tracer) { } | ||
4106 | static inline void trace_insert_enum_map_file(struct module *mod, | ||
4107 | struct trace_enum_map **start, int len) { } | ||
4108 | #endif /* !CONFIG_TRACE_ENUM_MAP_FILE */ | ||
4109 | |||
4110 | static void trace_insert_enum_map(struct module *mod, | ||
4111 | struct trace_enum_map **start, int len) | ||
4112 | { | ||
4113 | struct trace_enum_map **map; | ||
4114 | |||
4115 | if (len <= 0) | ||
4116 | return; | ||
4117 | |||
4118 | map = start; | ||
4119 | |||
4120 | trace_event_enum_update(map, len); | ||
4121 | |||
4122 | trace_insert_enum_map_file(mod, start, len); | ||
4123 | } | ||
4124 | |||
3913 | static ssize_t | 4125 | static ssize_t |
3914 | tracing_set_trace_read(struct file *filp, char __user *ubuf, | 4126 | tracing_set_trace_read(struct file *filp, char __user *ubuf, |
3915 | size_t cnt, loff_t *ppos) | 4127 | size_t cnt, loff_t *ppos) |
@@ -6527,6 +6739,88 @@ struct dentry *tracing_init_dentry(void) | |||
6527 | return NULL; | 6739 | return NULL; |
6528 | } | 6740 | } |
6529 | 6741 | ||
6742 | extern struct trace_enum_map *__start_ftrace_enum_maps[]; | ||
6743 | extern struct trace_enum_map *__stop_ftrace_enum_maps[]; | ||
6744 | |||
6745 | static void __init trace_enum_init(void) | ||
6746 | { | ||
6747 | int len; | ||
6748 | |||
6749 | len = __stop_ftrace_enum_maps - __start_ftrace_enum_maps; | ||
6750 | trace_insert_enum_map(NULL, __start_ftrace_enum_maps, len); | ||
6751 | } | ||
6752 | |||
6753 | #ifdef CONFIG_MODULES | ||
6754 | static void trace_module_add_enums(struct module *mod) | ||
6755 | { | ||
6756 | if (!mod->num_trace_enums) | ||
6757 | return; | ||
6758 | |||
6759 | /* | ||
6760 | * Modules with bad taint do not have events created, do | ||
6761 | * not bother with enums either. | ||
6762 | */ | ||
6763 | if (trace_module_has_bad_taint(mod)) | ||
6764 | return; | ||
6765 | |||
6766 | trace_insert_enum_map(mod, mod->trace_enums, mod->num_trace_enums); | ||
6767 | } | ||
6768 | |||
6769 | #ifdef CONFIG_TRACE_ENUM_MAP_FILE | ||
6770 | static void trace_module_remove_enums(struct module *mod) | ||
6771 | { | ||
6772 | union trace_enum_map_item *map; | ||
6773 | union trace_enum_map_item **last = &trace_enum_maps; | ||
6774 | |||
6775 | if (!mod->num_trace_enums) | ||
6776 | return; | ||
6777 | |||
6778 | mutex_lock(&trace_enum_mutex); | ||
6779 | |||
6780 | map = trace_enum_maps; | ||
6781 | |||
6782 | while (map) { | ||
6783 | if (map->head.mod == mod) | ||
6784 | break; | ||
6785 | map = trace_enum_jmp_to_tail(map); | ||
6786 | last = &map->tail.next; | ||
6787 | map = map->tail.next; | ||
6788 | } | ||
6789 | if (!map) | ||
6790 | goto out; | ||
6791 | |||
6792 | *last = trace_enum_jmp_to_tail(map)->tail.next; | ||
6793 | kfree(map); | ||
6794 | out: | ||
6795 | mutex_unlock(&trace_enum_mutex); | ||
6796 | } | ||
6797 | #else | ||
6798 | static inline void trace_module_remove_enums(struct module *mod) { } | ||
6799 | #endif /* CONFIG_TRACE_ENUM_MAP_FILE */ | ||
6800 | |||
6801 | static int trace_module_notify(struct notifier_block *self, | ||
6802 | unsigned long val, void *data) | ||
6803 | { | ||
6804 | struct module *mod = data; | ||
6805 | |||
6806 | switch (val) { | ||
6807 | case MODULE_STATE_COMING: | ||
6808 | trace_module_add_enums(mod); | ||
6809 | break; | ||
6810 | case MODULE_STATE_GOING: | ||
6811 | trace_module_remove_enums(mod); | ||
6812 | break; | ||
6813 | } | ||
6814 | |||
6815 | return 0; | ||
6816 | } | ||
6817 | |||
6818 | static struct notifier_block trace_module_nb = { | ||
6819 | .notifier_call = trace_module_notify, | ||
6820 | .priority = 0, | ||
6821 | }; | ||
6822 | #endif /* CONFIG_MODULES */ | ||
6823 | |||
6530 | static __init int tracer_init_tracefs(void) | 6824 | static __init int tracer_init_tracefs(void) |
6531 | { | 6825 | { |
6532 | struct dentry *d_tracer; | 6826 | struct dentry *d_tracer; |
@@ -6551,6 +6845,14 @@ static __init int tracer_init_tracefs(void) | |||
6551 | trace_create_file("saved_cmdlines_size", 0644, d_tracer, | 6845 | trace_create_file("saved_cmdlines_size", 0644, d_tracer, |
6552 | NULL, &tracing_saved_cmdlines_size_fops); | 6846 | NULL, &tracing_saved_cmdlines_size_fops); |
6553 | 6847 | ||
6848 | trace_enum_init(); | ||
6849 | |||
6850 | trace_create_enum_file(d_tracer); | ||
6851 | |||
6852 | #ifdef CONFIG_MODULES | ||
6853 | register_module_notifier(&trace_module_nb); | ||
6854 | #endif | ||
6855 | |||
6554 | #ifdef CONFIG_DYNAMIC_FTRACE | 6856 | #ifdef CONFIG_DYNAMIC_FTRACE |
6555 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, | 6857 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, |
6556 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); | 6858 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); |
@@ -6877,7 +7179,7 @@ void __init trace_init(void) | |||
6877 | tracepoint_printk = 0; | 7179 | tracepoint_printk = 0; |
6878 | } | 7180 | } |
6879 | tracer_alloc_buffers(); | 7181 | tracer_alloc_buffers(); |
6880 | trace_event_init(); | 7182 | trace_event_init(); |
6881 | } | 7183 | } |
6882 | 7184 | ||
6883 | __init static int clear_boot_tracer(void) | 7185 | __init static int clear_boot_tracer(void) |