diff options
-rw-r--r-- | kernel/trace/Kconfig | 28 | ||||
-rw-r--r-- | kernel/trace/trace.c | 245 |
2 files changed, 269 insertions, 4 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index a5da09c899dd..fedbdd7d5d1e 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -599,6 +599,34 @@ config RING_BUFFER_STARTUP_TEST | |||
599 | 599 | ||
600 | If unsure, say N | 600 | If unsure, say N |
601 | 601 | ||
602 | config TRACE_ENUM_MAP_FILE | ||
603 | bool "Show enum mappings for trace events" | ||
604 | depends on TRACING | ||
605 | help | ||
606 | The "print fmt" of the trace events will show the enum names instead | ||
607 | of their values. This can cause problems for user space tools that | ||
608 | use this string to parse the raw data as user space does not know | ||
609 | how to convert the string to its value. | ||
610 | |||
611 | To fix this, there's a special macro in the kernel that can be used | ||
612 | to convert the enum into its value. If this macro is used, then the | ||
613 | print fmt strings will have the enums converted to their values. | ||
614 | |||
615 | If something does not get converted properly, this option can be | ||
616 | used to show what enums the kernel tried to convert. | ||
617 | |||
618 | This option is for debugging the enum conversions. A file is created | ||
619 | in the tracing directory called "enum_map" that will show the enum | ||
620 | names matched with their values and what trace event system they | ||
621 | belong too. | ||
622 | |||
623 | Normally, the mapping of the strings to values will be freed after | ||
624 | boot up or module load. With this option, they will not be freed, as | ||
625 | they are needed for the "enum_map" file. Enabling this option will | ||
626 | increase the memory footprint of the running kernel. | ||
627 | |||
628 | If unsure, say N | ||
629 | |||
602 | endif # FTRACE | 630 | endif # FTRACE |
603 | 631 | ||
604 | endif # TRACING_SUPPORT | 632 | endif # TRACING_SUPPORT |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 28e6654e640d..39e69568302e 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -123,6 +123,42 @@ enum ftrace_dump_mode ftrace_dump_on_oops; | |||
123 | /* When set, tracing will stop when a WARN*() is hit */ | 123 | /* When set, tracing will stop when a WARN*() is hit */ |
124 | int __disable_trace_on_warning; | 124 | int __disable_trace_on_warning; |
125 | 125 | ||
126 | #ifdef CONFIG_TRACE_ENUM_MAP_FILE | ||
127 | /* Map of enums to their values, for "enum_map" file */ | ||
128 | struct trace_enum_map_head { | ||
129 | struct module *mod; | ||
130 | unsigned long length; | ||
131 | }; | ||
132 | |||
133 | union trace_enum_map_item; | ||
134 | |||
135 | struct trace_enum_map_tail { | ||
136 | /* | ||
137 | * "end" is first and points to NULL as it must be different | ||
138 | * than "mod" or "enum_string" | ||
139 | */ | ||
140 | union trace_enum_map_item *next; | ||
141 | const char *end; /* points to NULL */ | ||
142 | }; | ||
143 | |||
144 | static DEFINE_MUTEX(trace_enum_mutex); | ||
145 | |||
146 | /* | ||
147 | * The trace_enum_maps are saved in an array with two extra elements, | ||
148 | * one at the beginning, and one at the end. The beginning item contains | ||
149 | * the count of the saved maps (head.length), and the module they | ||
150 | * belong to if not built in (head.mod). The ending item contains a | ||
151 | * pointer to the next array of saved enum_map items. | ||
152 | */ | ||
153 | union trace_enum_map_item { | ||
154 | struct trace_enum_map map; | ||
155 | struct trace_enum_map_head head; | ||
156 | struct trace_enum_map_tail tail; | ||
157 | }; | ||
158 | |||
159 | static union trace_enum_map_item *trace_enum_maps; | ||
160 | #endif /* CONFIG_TRACE_ENUM_MAP_FILE */ | ||
161 | |||
126 | static int tracing_set_tracer(struct trace_array *tr, const char *buf); | 162 | static int tracing_set_tracer(struct trace_array *tr, const char *buf); |
127 | 163 | ||
128 | #define MAX_TRACER_SIZE 100 | 164 | #define MAX_TRACER_SIZE 100 |
@@ -3908,7 +3944,169 @@ static const struct file_operations tracing_saved_cmdlines_size_fops = { | |||
3908 | .write = tracing_saved_cmdlines_size_write, | 3944 | .write = tracing_saved_cmdlines_size_write, |
3909 | }; | 3945 | }; |
3910 | 3946 | ||
3911 | static void trace_insert_enum_map(struct trace_enum_map **start, int len) | 3947 | #ifdef CONFIG_TRACE_ENUM_MAP_FILE |
3948 | static union trace_enum_map_item * | ||
3949 | update_enum_map(union trace_enum_map_item *ptr) | ||
3950 | { | ||
3951 | if (!ptr->map.enum_string) { | ||
3952 | if (ptr->tail.next) { | ||
3953 | ptr = ptr->tail.next; | ||
3954 | /* Set ptr to the next real item (skip head) */ | ||
3955 | ptr++; | ||
3956 | } else | ||
3957 | return NULL; | ||
3958 | } | ||
3959 | return ptr; | ||
3960 | } | ||
3961 | |||
3962 | static void *enum_map_next(struct seq_file *m, void *v, loff_t *pos) | ||
3963 | { | ||
3964 | union trace_enum_map_item *ptr = v; | ||
3965 | |||
3966 | /* | ||
3967 | * Paranoid! If ptr points to end, we don't want to increment past it. | ||
3968 | * This really should never happen. | ||
3969 | */ | ||
3970 | ptr = update_enum_map(ptr); | ||
3971 | if (WARN_ON_ONCE(!ptr)) | ||
3972 | return NULL; | ||
3973 | |||
3974 | ptr++; | ||
3975 | |||
3976 | (*pos)++; | ||
3977 | |||
3978 | ptr = update_enum_map(ptr); | ||
3979 | |||
3980 | return ptr; | ||
3981 | } | ||
3982 | |||
3983 | static void *enum_map_start(struct seq_file *m, loff_t *pos) | ||
3984 | { | ||
3985 | union trace_enum_map_item *v; | ||
3986 | loff_t l = 0; | ||
3987 | |||
3988 | mutex_lock(&trace_enum_mutex); | ||
3989 | |||
3990 | v = trace_enum_maps; | ||
3991 | if (v) | ||
3992 | v++; | ||
3993 | |||
3994 | while (v && l < *pos) { | ||
3995 | v = enum_map_next(m, v, &l); | ||
3996 | } | ||
3997 | |||
3998 | return v; | ||
3999 | } | ||
4000 | |||
4001 | static void enum_map_stop(struct seq_file *m, void *v) | ||
4002 | { | ||
4003 | mutex_unlock(&trace_enum_mutex); | ||
4004 | } | ||
4005 | |||
4006 | static int enum_map_show(struct seq_file *m, void *v) | ||
4007 | { | ||
4008 | union trace_enum_map_item *ptr = v; | ||
4009 | |||
4010 | seq_printf(m, "%s %ld (%s)\n", | ||
4011 | ptr->map.enum_string, ptr->map.enum_value, | ||
4012 | ptr->map.system); | ||
4013 | |||
4014 | return 0; | ||
4015 | } | ||
4016 | |||
4017 | static const struct seq_operations tracing_enum_map_seq_ops = { | ||
4018 | .start = enum_map_start, | ||
4019 | .next = enum_map_next, | ||
4020 | .stop = enum_map_stop, | ||
4021 | .show = enum_map_show, | ||
4022 | }; | ||
4023 | |||
4024 | static int tracing_enum_map_open(struct inode *inode, struct file *filp) | ||
4025 | { | ||
4026 | if (tracing_disabled) | ||
4027 | return -ENODEV; | ||
4028 | |||
4029 | return seq_open(filp, &tracing_enum_map_seq_ops); | ||
4030 | } | ||
4031 | |||
4032 | static const struct file_operations tracing_enum_map_fops = { | ||
4033 | .open = tracing_enum_map_open, | ||
4034 | .read = seq_read, | ||
4035 | .llseek = seq_lseek, | ||
4036 | .release = seq_release, | ||
4037 | }; | ||
4038 | |||
4039 | static inline union trace_enum_map_item * | ||
4040 | trace_enum_jmp_to_tail(union trace_enum_map_item *ptr) | ||
4041 | { | ||
4042 | /* Return tail of array given the head */ | ||
4043 | return ptr + ptr->head.length + 1; | ||
4044 | } | ||
4045 | |||
4046 | static void | ||
4047 | trace_insert_enum_map_file(struct module *mod, struct trace_enum_map **start, | ||
4048 | int len) | ||
4049 | { | ||
4050 | struct trace_enum_map **stop; | ||
4051 | struct trace_enum_map **map; | ||
4052 | union trace_enum_map_item *map_array; | ||
4053 | union trace_enum_map_item *ptr; | ||
4054 | |||
4055 | stop = start + len; | ||
4056 | |||
4057 | /* | ||
4058 | * The trace_enum_maps contains the map plus a head and tail item, | ||
4059 | * where the head holds the module and length of array, and the | ||
4060 | * tail holds a pointer to the next list. | ||
4061 | */ | ||
4062 | map_array = kmalloc(sizeof(*map_array) * (len + 2), GFP_KERNEL); | ||
4063 | if (!map_array) { | ||
4064 | pr_warning("Unable to allocate trace enum mapping\n"); | ||
4065 | return; | ||
4066 | } | ||
4067 | |||
4068 | mutex_lock(&trace_enum_mutex); | ||
4069 | |||
4070 | if (!trace_enum_maps) | ||
4071 | trace_enum_maps = map_array; | ||
4072 | else { | ||
4073 | ptr = trace_enum_maps; | ||
4074 | for (;;) { | ||
4075 | ptr = trace_enum_jmp_to_tail(ptr); | ||
4076 | if (!ptr->tail.next) | ||
4077 | break; | ||
4078 | ptr = ptr->tail.next; | ||
4079 | |||
4080 | } | ||
4081 | ptr->tail.next = map_array; | ||
4082 | } | ||
4083 | map_array->head.mod = mod; | ||
4084 | map_array->head.length = len; | ||
4085 | map_array++; | ||
4086 | |||
4087 | for (map = start; (unsigned long)map < (unsigned long)stop; map++) { | ||
4088 | map_array->map = **map; | ||
4089 | map_array++; | ||
4090 | } | ||
4091 | memset(map_array, 0, sizeof(*map_array)); | ||
4092 | |||
4093 | mutex_unlock(&trace_enum_mutex); | ||
4094 | } | ||
4095 | |||
4096 | static void trace_create_enum_file(struct dentry *d_tracer) | ||
4097 | { | ||
4098 | trace_create_file("enum_map", 0444, d_tracer, | ||
4099 | NULL, &tracing_enum_map_fops); | ||
4100 | } | ||
4101 | |||
4102 | #else /* CONFIG_TRACE_ENUM_MAP_FILE */ | ||
4103 | static inline void trace_create_enum_file(struct dentry *d_tracer) { } | ||
4104 | static inline void trace_insert_enum_map_file(struct module *mod, | ||
4105 | struct trace_enum_map **start, int len) { } | ||
4106 | #endif /* !CONFIG_TRACE_ENUM_MAP_FILE */ | ||
4107 | |||
4108 | static void trace_insert_enum_map(struct module *mod, | ||
4109 | struct trace_enum_map **start, int len) | ||
3912 | { | 4110 | { |
3913 | struct trace_enum_map **map; | 4111 | struct trace_enum_map **map; |
3914 | 4112 | ||
@@ -3918,6 +4116,8 @@ static void trace_insert_enum_map(struct trace_enum_map **start, int len) | |||
3918 | map = start; | 4116 | map = start; |
3919 | 4117 | ||
3920 | trace_event_enum_update(map, len); | 4118 | trace_event_enum_update(map, len); |
4119 | |||
4120 | trace_insert_enum_map_file(mod, start, len); | ||
3921 | } | 4121 | } |
3922 | 4122 | ||
3923 | static ssize_t | 4123 | static ssize_t |
@@ -6562,7 +6762,7 @@ static void __init trace_enum_init(void) | |||
6562 | int len; | 6762 | int len; |
6563 | 6763 | ||
6564 | len = __stop_ftrace_enum_maps - __start_ftrace_enum_maps; | 6764 | len = __stop_ftrace_enum_maps - __start_ftrace_enum_maps; |
6565 | trace_insert_enum_map(__start_ftrace_enum_maps, len); | 6765 | trace_insert_enum_map(NULL, __start_ftrace_enum_maps, len); |
6566 | } | 6766 | } |
6567 | 6767 | ||
6568 | #ifdef CONFIG_MODULES | 6768 | #ifdef CONFIG_MODULES |
@@ -6578,9 +6778,41 @@ static void trace_module_add_enums(struct module *mod) | |||
6578 | if (trace_module_has_bad_taint(mod)) | 6778 | if (trace_module_has_bad_taint(mod)) |
6579 | return; | 6779 | return; |
6580 | 6780 | ||
6581 | trace_insert_enum_map(mod->trace_enums, mod->num_trace_enums); | 6781 | trace_insert_enum_map(mod, mod->trace_enums, mod->num_trace_enums); |
6582 | } | 6782 | } |
6583 | 6783 | ||
6784 | #ifdef CONFIG_TRACE_ENUM_MAP_FILE | ||
6785 | static void trace_module_remove_enums(struct module *mod) | ||
6786 | { | ||
6787 | union trace_enum_map_item *map; | ||
6788 | union trace_enum_map_item **last = &trace_enum_maps; | ||
6789 | |||
6790 | if (!mod->num_trace_enums) | ||
6791 | return; | ||
6792 | |||
6793 | mutex_lock(&trace_enum_mutex); | ||
6794 | |||
6795 | map = trace_enum_maps; | ||
6796 | |||
6797 | while (map) { | ||
6798 | if (map->head.mod == mod) | ||
6799 | break; | ||
6800 | map = trace_enum_jmp_to_tail(map); | ||
6801 | last = &map->tail.next; | ||
6802 | map = map->tail.next; | ||
6803 | } | ||
6804 | if (!map) | ||
6805 | goto out; | ||
6806 | |||
6807 | *last = trace_enum_jmp_to_tail(map)->tail.next; | ||
6808 | kfree(map); | ||
6809 | out: | ||
6810 | mutex_unlock(&trace_enum_mutex); | ||
6811 | } | ||
6812 | #else | ||
6813 | static inline void trace_module_remove_enums(struct module *mod) { } | ||
6814 | #endif /* CONFIG_TRACE_ENUM_MAP_FILE */ | ||
6815 | |||
6584 | static int trace_module_notify(struct notifier_block *self, | 6816 | static int trace_module_notify(struct notifier_block *self, |
6585 | unsigned long val, void *data) | 6817 | unsigned long val, void *data) |
6586 | { | 6818 | { |
@@ -6590,6 +6822,9 @@ static int trace_module_notify(struct notifier_block *self, | |||
6590 | case MODULE_STATE_COMING: | 6822 | case MODULE_STATE_COMING: |
6591 | trace_module_add_enums(mod); | 6823 | trace_module_add_enums(mod); |
6592 | break; | 6824 | break; |
6825 | case MODULE_STATE_GOING: | ||
6826 | trace_module_remove_enums(mod); | ||
6827 | break; | ||
6593 | } | 6828 | } |
6594 | 6829 | ||
6595 | return 0; | 6830 | return 0; |
@@ -6599,7 +6834,7 @@ static struct notifier_block trace_module_nb = { | |||
6599 | .notifier_call = trace_module_notify, | 6834 | .notifier_call = trace_module_notify, |
6600 | .priority = 0, | 6835 | .priority = 0, |
6601 | }; | 6836 | }; |
6602 | #endif | 6837 | #endif /* CONFIG_MODULES */ |
6603 | 6838 | ||
6604 | static __init int tracer_init_debugfs(void) | 6839 | static __init int tracer_init_debugfs(void) |
6605 | { | 6840 | { |
@@ -6627,6 +6862,8 @@ static __init int tracer_init_debugfs(void) | |||
6627 | 6862 | ||
6628 | trace_enum_init(); | 6863 | trace_enum_init(); |
6629 | 6864 | ||
6865 | trace_create_enum_file(d_tracer); | ||
6866 | |||
6630 | #ifdef CONFIG_MODULES | 6867 | #ifdef CONFIG_MODULES |
6631 | register_module_notifier(&trace_module_nb); | 6868 | register_module_notifier(&trace_module_nb); |
6632 | #endif | 6869 | #endif |