trace-cmd: Add trace-cmd report --version to extract version info

Now that the trace.dat file can save the version of the executable that was
used to create it, save that information. This will be useful if a user has
a problem with a trace.dat file. It can let the developers know what version
was used to create it. We need a way to extract this information. Adding a
new "--version" to trace-cmd report will display the saved version in the
trace.dat file if it exists.

Link: http://lore.kernel.org/linux-trace-devel/20190610212706.2505bffe@oasis.local.home

Reviewed-by: Slavomir Kaslev <kaslevs@vmware.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
diff --git a/Documentation/trace-cmd-report.1.txt b/Documentation/trace-cmd-report.1.txt
index 20d6b7a..87f4d7a 100644
--- a/Documentation/trace-cmd-report.1.txt
+++ b/Documentation/trace-cmd-report.1.txt
@@ -248,6 +248,10 @@
     If the trace.dat file recorded uname during the run, this will retrieve that
     information.
 
+*--version*::
+    If the trace.dat file recorded the version of the executable used to create
+    it, report that version.
+
 *--ts-offset* offset::
     Add (or subtract if negative) an offset for all timestamps of the previous
     data file specified with *-i*. This is useful to merge sort multiple trace.dat
diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h
index 8b92210..6f62ab9 100644
--- a/include/trace-cmd/trace-cmd.h
+++ b/include/trace-cmd/trace-cmd.h
@@ -138,6 +138,7 @@
 
 void tracecmd_print_stats(struct tracecmd_input *handle);
 void tracecmd_print_uname(struct tracecmd_input *handle);
+void tracecmd_print_version(struct tracecmd_input *handle);
 
 struct tep_record *
 tracecmd_read_page_record(struct tep_handle *pevent, void *page, int size,
diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c
index 264e3c3..61566ba 100644
--- a/lib/trace-cmd/trace-input.c
+++ b/lib/trace-cmd/trace-input.c
@@ -94,6 +94,7 @@
 	double			ts2secs;
 	char *			cpustats;
 	char *			uname;
+	char *			version;
 	struct input_buffer_instance	*buffers;
 	int			parsing_failures;
 
@@ -2220,6 +2221,9 @@
 		case TRACECMD_OPTION_UNAME:
 			handle->uname = strdup(buf);
 			break;
+		case TRACECMD_OPTION_VERSION:
+			handle->version = strdup(buf);
+			break;
 		case TRACECMD_OPTION_HOOK:
 			hook = tracecmd_create_event_hook(buf);
 			hook->next = handle->hooks;
@@ -2599,6 +2603,21 @@
 }
 
 /**
+ * tracecmd_print_uname - prints the recorded uname if it was recorded
+ * @handle: input handle for the trace.dat file
+ *
+ * Looks for the option TRACECMD_OPTION_VERSION and prints out what's
+ * stored there, if it is found. Otherwise it prints that none were found.
+ */
+void tracecmd_print_version(struct tracecmd_input *handle)
+{
+	if (handle->version)
+		printf("%s\n", handle->version);
+	else
+		printf(" version was not recorded in this file\n");
+}
+
+/**
  * tracecmd_hooks - return the event hooks that were used in record
  * @handle: input handle for the trace.dat file
  *
diff --git a/tracecmd/trace-read.c b/tracecmd/trace-read.c
index dbfb3a5..d22c723 100644
--- a/tracecmd/trace-read.c
+++ b/tracecmd/trace-read.c
@@ -1112,6 +1112,7 @@
 	OUTPUT_NORMAL,
 	OUTPUT_STAT_ONLY,
 	OUTPUT_UNAME_ONLY,
+	OUTPUT_VERSION_ONLY,
 };
 
 static void read_data_info(struct list_head *handle_list, enum output_type otype,
@@ -1160,6 +1161,8 @@
 			continue;
 		case OUTPUT_UNAME_ONLY:
 			tracecmd_print_uname(handles->handle);
+		case OUTPUT_VERSION_ONLY:
+			tracecmd_print_version(handles->handle);
 			continue;
 		}
 
@@ -1386,6 +1389,7 @@
 }
 
 enum {
+	OPT_version	= 238,
 	OPT_tsdiff	= 239,
 	OPT_ts2secs	= 240,
 	OPT_tsoffset	= 241,
@@ -1427,6 +1431,7 @@
 	int show_page_size = 0;
 	int show_printk = 0;
 	int show_uname = 0;
+	int show_version = 0;
 	int latency_format = 0;
 	int show_events = 0;
 	int print_events = 0;
@@ -1468,6 +1473,7 @@
 			{"debug", no_argument, NULL, OPT_debug},
 			{"profile", no_argument, NULL, OPT_profile},
 			{"uname", no_argument, NULL, OPT_uname},
+			{"version", no_argument, NULL, OPT_version},
 			{"by-comm", no_argument, NULL, OPT_bycomm},
 			{"ts-offset", required_argument, NULL, OPT_tsoffset},
 			{"ts2secs", required_argument, NULL, OPT_ts2secs},
@@ -1618,6 +1624,9 @@
 		case OPT_uname:
 			show_uname = 1;
 			break;
+		case OPT_version:
+			show_version = 1;
+			break;
 		case OPT_bycomm:
 			trace_profile_set_merge_like_comms();
 			break;
@@ -1767,6 +1776,9 @@
 	/* yeah yeah, uname overrides stat */
 	if (show_uname)
 		otype = OUTPUT_UNAME_ONLY;
+	/* and version overrides uname! */
+	if (show_version)
+		otype = OUTPUT_VERSION_ONLY;
 	read_data_info(&handle_list, otype, global);
 
 	list_for_each_entry(handles, &handle_list, list) {
diff --git a/tracecmd/trace-usage.c b/tracecmd/trace-usage.c
index d2a1089..0012729 100644
--- a/tracecmd/trace-usage.c
+++ b/tracecmd/trace-usage.c
@@ -166,6 +166,7 @@
 		"          --check-events return whether all event formats can be parsed\n"
 		"          --stat - show the buffer stats that were reported at the end of the record.\n"
 		"          --uname - show uname of the record, if it was saved\n"
+		"          --version - show version used to build the trace-cmd exec that created the file\n"
 		"          --profile report stats on where tasks are blocked and such\n"
 		"          -G when profiling, set soft and hard irqs as global\n"
 		"          -H Allows users to hook two events together for timings\n"