[PATCH 18/18 v3] tracing: Add enum_map file to show enums that have been mapped

2015-04-02 Thread Steven Rostedt
From: "Steven Rostedt (Red Hat)" 

Add a enum_map file in the tracing directory to see what enums have been
saved to convert in the print fmt files.

As this requires the enum mapping to be persistent in memory, it is only
created if the new config option CONFIG_TRACE_ENUM_MAP_FILE is enabled.
This is for debugging and will increase the persistent memory footprint
of the kernel.

Signed-off-by: Steven Rostedt 
---
 kernel/trace/Kconfig |  28 ++
 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
 
 If unsure, say N
 
+config TRACE_ENUM_MAP_FILE
+   bool "Show enum mappings for trace events"
+   depends on TRACING
+   help
+The "print fmt" of the trace events will show the enum names instead
+   of their values. This can cause problems for user space tools that
+   use this string to parse the raw data as user space does not know
+   how to convert the string to its value.
+
+   To fix this, there's a special macro in the kernel that can be used
+   to convert the enum into its value. If this macro is used, then the
+   print fmt strings will have the enums converted to their values.
+
+   If something does not get converted properly, this option can be
+   used to show what enums the kernel tried to convert.
+
+   This option is for debugging the enum conversions. A file is created
+   in the tracing directory called "enum_map" that will show the enum
+   names matched with their values and what trace event system they
+   belong too.
+
+   Normally, the mapping of the strings to values will be freed after
+   boot up or module load. With this option, they will not be freed, as
+   they are needed for the "enum_map" file. Enabling this option will
+   increase the memory footprint of the running kernel.
+
+   If unsure, say N
+
 endif # FTRACE
 
 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;
 /* 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_SIZE100
@@ -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, 

[PATCH 18/18 v3] tracing: Add enum_map file to show enums that have been mapped

2015-04-02 Thread Steven Rostedt
From: Steven Rostedt (Red Hat) rost...@goodmis.org

Add a enum_map file in the tracing directory to see what enums have been
saved to convert in the print fmt files.

As this requires the enum mapping to be persistent in memory, it is only
created if the new config option CONFIG_TRACE_ENUM_MAP_FILE is enabled.
This is for debugging and will increase the persistent memory footprint
of the kernel.

Signed-off-by: Steven Rostedt rost...@goodmis.org
---
 kernel/trace/Kconfig |  28 ++
 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
 
 If unsure, say N
 
+config TRACE_ENUM_MAP_FILE
+   bool Show enum mappings for trace events
+   depends on TRACING
+   help
+The print fmt of the trace events will show the enum names instead
+   of their values. This can cause problems for user space tools that
+   use this string to parse the raw data as user space does not know
+   how to convert the string to its value.
+
+   To fix this, there's a special macro in the kernel that can be used
+   to convert the enum into its value. If this macro is used, then the
+   print fmt strings will have the enums converted to their values.
+
+   If something does not get converted properly, this option can be
+   used to show what enums the kernel tried to convert.
+
+   This option is for debugging the enum conversions. A file is created
+   in the tracing directory called enum_map that will show the enum
+   names matched with their values and what trace event system they
+   belong too.
+
+   Normally, the mapping of the strings to values will be freed after
+   boot up or module load. With this option, they will not be freed, as
+   they are needed for the enum_map file. Enabling this option will
+   increase the memory footprint of the running kernel.
+
+   If unsure, say N
+
 endif # FTRACE
 
 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;
 /* 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_SIZE100
@@ -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