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 |
