|
@@ -123,6 +123,42 @@ enum ftrace_dump_mode ftrace_dump_on_oops;
|
|
|
/* When set, tracing will stop when a WARN*() is hit */
|
|
|
int __disable_trace_on_warning;
|
|
|
|
|
|
+#ifdef CONFIG_TRACE_ENUM_MAP_FILE
|
|
|
+/* Map of enums to their values, for "enum_map" file */
|
|
|
+struct trace_enum_map_head {
|
|
|
+ struct module *mod;
|
|
|
+ unsigned long length;
|
|
|
+};
|
|
|
+
|
|
|
+union trace_enum_map_item;
|
|
|
+
|
|
|
+struct trace_enum_map_tail {
|
|
|
+ /*
|
|
|
+ * "end" is first and points to NULL as it must be different
|
|
|
+ * than "mod" or "enum_string"
|
|
|
+ */
|
|
|
+ union trace_enum_map_item *next;
|
|
|
+ const char *end; /* points to NULL */
|
|
|
+};
|
|
|
+
|
|
|
+static DEFINE_MUTEX(trace_enum_mutex);
|
|
|
+
|
|
|
+/*
|
|
|
+ * The trace_enum_maps are saved in an array with two extra elements,
|
|
|
+ * one at the beginning, and one at the end. The beginning item contains
|
|
|
+ * the count of the saved maps (head.length), and the module they
|
|
|
+ * belong to if not built in (head.mod). The ending item contains a
|
|
|
+ * pointer to the next array of saved enum_map items.
|
|
|
+ */
|
|
|
+union trace_enum_map_item {
|
|
|
+ struct trace_enum_map map;
|
|
|
+ struct trace_enum_map_head head;
|
|
|
+ struct trace_enum_map_tail tail;
|
|
|
+};
|
|
|
+
|
|
|
+static union trace_enum_map_item *trace_enum_maps;
|
|
|
+#endif /* CONFIG_TRACE_ENUM_MAP_FILE */
|
|
|
+
|
|
|
static int tracing_set_tracer(struct trace_array *tr, const char *buf);
|
|
|
|
|
|
#define MAX_TRACER_SIZE 100
|
|
@@ -3908,7 +3944,169 @@ static const struct file_operations tracing_saved_cmdlines_size_fops = {
|
|
|
.write = tracing_saved_cmdlines_size_write,
|
|
|
};
|
|
|
|
|
|
-static void trace_insert_enum_map(struct trace_enum_map **start, int len)
|
|
|
+#ifdef CONFIG_TRACE_ENUM_MAP_FILE
|
|
|
+static union trace_enum_map_item *
|
|
|
+update_enum_map(union trace_enum_map_item *ptr)
|
|
|
+{
|
|
|
+ if (!ptr->map.enum_string) {
|
|
|
+ if (ptr->tail.next) {
|
|
|
+ ptr = ptr->tail.next;
|
|
|
+ /* Set ptr to the next real item (skip head) */
|
|
|
+ ptr++;
|
|
|
+ } else
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+static void *enum_map_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
+{
|
|
|
+ union trace_enum_map_item *ptr = v;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Paranoid! If ptr points to end, we don't want to increment past it.
|
|
|
+ * This really should never happen.
|
|
|
+ */
|
|
|
+ ptr = update_enum_map(ptr);
|
|
|
+ if (WARN_ON_ONCE(!ptr))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ ptr++;
|
|
|
+
|
|
|
+ (*pos)++;
|
|
|
+
|
|
|
+ ptr = update_enum_map(ptr);
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+static void *enum_map_start(struct seq_file *m, loff_t *pos)
|
|
|
+{
|
|
|
+ union trace_enum_map_item *v;
|
|
|
+ loff_t l = 0;
|
|
|
+
|
|
|
+ mutex_lock(&trace_enum_mutex);
|
|
|
+
|
|
|
+ v = trace_enum_maps;
|
|
|
+ if (v)
|
|
|
+ v++;
|
|
|
+
|
|
|
+ while (v && l < *pos) {
|
|
|
+ v = enum_map_next(m, v, &l);
|
|
|
+ }
|
|
|
+
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+static void enum_map_stop(struct seq_file *m, void *v)
|
|
|
+{
|
|
|
+ mutex_unlock(&trace_enum_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+static int enum_map_show(struct seq_file *m, void *v)
|
|
|
+{
|
|
|
+ union trace_enum_map_item *ptr = v;
|
|
|
+
|
|
|
+ seq_printf(m, "%s %ld (%s)\n",
|
|
|
+ ptr->map.enum_string, ptr->map.enum_value,
|
|
|
+ ptr->map.system);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct seq_operations tracing_enum_map_seq_ops = {
|
|
|
+ .start = enum_map_start,
|
|
|
+ .next = enum_map_next,
|
|
|
+ .stop = enum_map_stop,
|
|
|
+ .show = enum_map_show,
|
|
|
+};
|
|
|
+
|
|
|
+static int tracing_enum_map_open(struct inode *inode, struct file *filp)
|
|
|
+{
|
|
|
+ if (tracing_disabled)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ return seq_open(filp, &tracing_enum_map_seq_ops);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations tracing_enum_map_fops = {
|
|
|
+ .open = tracing_enum_map_open,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = seq_release,
|
|
|
+};
|
|
|
+
|
|
|
+static inline union trace_enum_map_item *
|
|
|
+trace_enum_jmp_to_tail(union trace_enum_map_item *ptr)
|
|
|
+{
|
|
|
+ /* Return tail of array given the head */
|
|
|
+ return ptr + ptr->head.length + 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+trace_insert_enum_map_file(struct module *mod, struct trace_enum_map **start,
|
|
|
+ int len)
|
|
|
+{
|
|
|
+ struct trace_enum_map **stop;
|
|
|
+ struct trace_enum_map **map;
|
|
|
+ union trace_enum_map_item *map_array;
|
|
|
+ union trace_enum_map_item *ptr;
|
|
|
+
|
|
|
+ stop = start + len;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The trace_enum_maps contains the map plus a head and tail item,
|
|
|
+ * where the head holds the module and length of array, and the
|
|
|
+ * tail holds a pointer to the next list.
|
|
|
+ */
|
|
|
+ map_array = kmalloc(sizeof(*map_array) * (len + 2), GFP_KERNEL);
|
|
|
+ if (!map_array) {
|
|
|
+ pr_warning("Unable to allocate trace enum mapping\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&trace_enum_mutex);
|
|
|
+
|
|
|
+ if (!trace_enum_maps)
|
|
|
+ trace_enum_maps = map_array;
|
|
|
+ else {
|
|
|
+ ptr = trace_enum_maps;
|
|
|
+ for (;;) {
|
|
|
+ ptr = trace_enum_jmp_to_tail(ptr);
|
|
|
+ if (!ptr->tail.next)
|
|
|
+ break;
|
|
|
+ ptr = ptr->tail.next;
|
|
|
+
|
|
|
+ }
|
|
|
+ ptr->tail.next = map_array;
|
|
|
+ }
|
|
|
+ map_array->head.mod = mod;
|
|
|
+ map_array->head.length = len;
|
|
|
+ map_array++;
|
|
|
+
|
|
|
+ for (map = start; (unsigned long)map < (unsigned long)stop; map++) {
|
|
|
+ map_array->map = **map;
|
|
|
+ map_array++;
|
|
|
+ }
|
|
|
+ memset(map_array, 0, sizeof(*map_array));
|
|
|
+
|
|
|
+ mutex_unlock(&trace_enum_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+static void trace_create_enum_file(struct dentry *d_tracer)
|
|
|
+{
|
|
|
+ trace_create_file("enum_map", 0444, d_tracer,
|
|
|
+ NULL, &tracing_enum_map_fops);
|
|
|
+}
|
|
|
+
|
|
|
+#else /* CONFIG_TRACE_ENUM_MAP_FILE */
|
|
|
+static inline void trace_create_enum_file(struct dentry *d_tracer) { }
|
|
|
+static inline void trace_insert_enum_map_file(struct module *mod,
|
|
|
+ struct trace_enum_map **start, int len) { }
|
|
|
+#endif /* !CONFIG_TRACE_ENUM_MAP_FILE */
|
|
|
+
|
|
|
+static void trace_insert_enum_map(struct module *mod,
|
|
|
+ struct trace_enum_map **start, int len)
|
|
|
{
|
|
|
struct trace_enum_map **map;
|
|
|
|
|
@@ -3918,6 +4116,8 @@ static void trace_insert_enum_map(struct trace_enum_map **start, int len)
|
|
|
map = start;
|
|
|
|
|
|
trace_event_enum_update(map, len);
|
|
|
+
|
|
|
+ trace_insert_enum_map_file(mod, start, len);
|
|
|
}
|
|
|
|
|
|
static ssize_t
|
|
@@ -6562,7 +6762,7 @@ static void __init trace_enum_init(void)
|
|
|
int len;
|
|
|
|
|
|
len = __stop_ftrace_enum_maps - __start_ftrace_enum_maps;
|
|
|
- trace_insert_enum_map(__start_ftrace_enum_maps, len);
|
|
|
+ trace_insert_enum_map(NULL, __start_ftrace_enum_maps, len);
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_MODULES
|
|
@@ -6578,9 +6778,41 @@ static void trace_module_add_enums(struct module *mod)
|
|
|
if (trace_module_has_bad_taint(mod))
|
|
|
return;
|
|
|
|
|
|
- trace_insert_enum_map(mod->trace_enums, mod->num_trace_enums);
|
|
|
+ trace_insert_enum_map(mod, mod->trace_enums, mod->num_trace_enums);
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_TRACE_ENUM_MAP_FILE
|
|
|
+static void trace_module_remove_enums(struct module *mod)
|
|
|
+{
|
|
|
+ union trace_enum_map_item *map;
|
|
|
+ union trace_enum_map_item **last = &trace_enum_maps;
|
|
|
+
|
|
|
+ if (!mod->num_trace_enums)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mutex_lock(&trace_enum_mutex);
|
|
|
+
|
|
|
+ map = trace_enum_maps;
|
|
|
+
|
|
|
+ while (map) {
|
|
|
+ if (map->head.mod == mod)
|
|
|
+ break;
|
|
|
+ map = trace_enum_jmp_to_tail(map);
|
|
|
+ last = &map->tail.next;
|
|
|
+ map = map->tail.next;
|
|
|
+ }
|
|
|
+ if (!map)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ *last = trace_enum_jmp_to_tail(map)->tail.next;
|
|
|
+ kfree(map);
|
|
|
+ out:
|
|
|
+ mutex_unlock(&trace_enum_mutex);
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline void trace_module_remove_enums(struct module *mod) { }
|
|
|
+#endif /* CONFIG_TRACE_ENUM_MAP_FILE */
|
|
|
+
|
|
|
static int trace_module_notify(struct notifier_block *self,
|
|
|
unsigned long val, void *data)
|
|
|
{
|
|
@@ -6590,6 +6822,9 @@ static int trace_module_notify(struct notifier_block *self,
|
|
|
case MODULE_STATE_COMING:
|
|
|
trace_module_add_enums(mod);
|
|
|
break;
|
|
|
+ case MODULE_STATE_GOING:
|
|
|
+ trace_module_remove_enums(mod);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -6599,7 +6834,7 @@ static struct notifier_block trace_module_nb = {
|
|
|
.notifier_call = trace_module_notify,
|
|
|
.priority = 0,
|
|
|
};
|
|
|
-#endif
|
|
|
+#endif /* CONFIG_MODULES */
|
|
|
|
|
|
static __init int tracer_init_debugfs(void)
|
|
|
{
|
|
@@ -6627,6 +6862,8 @@ static __init int tracer_init_debugfs(void)
|
|
|
|
|
|
trace_enum_init();
|
|
|
|
|
|
+ trace_create_enum_file(d_tracer);
|
|
|
+
|
|
|
#ifdef CONFIG_MODULES
|
|
|
register_module_notifier(&trace_module_nb);
|
|
|
#endif
|