From 33db1ab2969ef817e92d48611376b956408ea5b1 Mon Sep 17 00:00:00 2001
From: Dave Craig <davecraig@unbalancedaudio.com>
Date: Mon, 31 Dec 2018 09:21:13 +0000
Subject: [PATCH] sigrok-cli: Add Google Trace Event output format (json)

The Chrome browser has a handy event viewer. It accepts JSON files
in the Google Trace Event format and views them in a similar way
to PulseView. The format is a convenient way to share decoded logic
captures. Note that being text based, the filesizes are large.

The patch adds a flag:

--protocol-decoder-jsontrace

which switches the decoder output format, and an alternative output
in show_pd_annotations which formats the samples appropriately.

An example command line might be:

sigrok-cli -P i2c:scl=D0:sda=D1 -P i2c:scl=D2:sda=D3 -i test.sr --protocol-decoder-jsontrace > test.json

This would take test.sr, decode two sets of I2C traffic and output as
JSON. Browsing to chrome://tracing from within Chrome, this json file
can then be loaded and viewed.

The patch is currently hard coded to 1MHz samplerate, I'm not
familiar enough with the sigrok code to figure out how to access
the samplerate from show_pd_annotations.
---
 decode.c     | 74 ++++++++++++++++++++++++++++++++++++++++++++++++------------
 main.c       |  6 +++++
 options.c    |  3 +++
 sigrok-cli.h |  1 +
 4 files changed, 70 insertions(+), 14 deletions(-)

diff --git a/decode.c b/decode.c
index 2b95f23..222507c 100644
--- a/decode.c
+++ b/decode.c
@@ -478,22 +478,68 @@ void show_pd_annotations(struct srd_proto_data *pdata, void *cb_data)
 	 * Display the annotation's fields after the layout was
 	 * determined above.
 	 */
-	if (show_snum) {
-		printf("%" PRIu64 "-%" PRIu64 " ",
-			pdata->start_sample, pdata->end_sample);
-	}
-	printf("%s: ", pdata->pdo->proto_id);
-	if (show_class) {
-		ann_descr = g_slist_nth_data(dec->annotations, pda->ann_class);
-		printf("%s: ", ann_descr[0]);
+	if(opt_pd_jsontrace)
+	{
+		char * row_text = 0;
+		static gboolean first_line = TRUE;
+
+		// TODO: Get this from the configured samplerate.
+		uint64_t samplerate = 1000000;
+
+		if(!first_line)
+			printf(",\n");
+
+		if (dec->annotation_rows) {
+			// Search for an annotation row for this index
+			for (l = dec->annotation_rows; l && !row_text; l = l->next) {
+				struct srd_decoder_annotation_row *r;
+				GSList *ll;
+
+				r = l->data;
+				for (ll = r->ann_classes; ll; ll = ll->next) {
+					if(GPOINTER_TO_INT(ll->data) == pda->ann_class)
+					{
+						row_text = r->desc;
+						break;
+					}
+				}
+			}
+		}
+
+		if(!row_text)
+		{
+			// Use the annotation descriptor
+			ann_descr = g_slist_nth_data(dec->annotations, pda->ann_class);
+			row_text = ann_descr[0];
+		}
+
+		// The name is set to contain the text of the annotation
+		// The pid (process id) takes the decoder name, this ensures that the annotations for each decoder are grouped together
+		// The tid (thread id) takes the annotation row descriptor
+		// The ts (timestamp) is in microseconds.
+		printf("{\"name\": \"%s\", \"ph\": \"B\", \"pid\": \"%s\", \"tid\": \"%s\", \"ts\": %"PRIu64"},\n", pda->ann_text[0], pdata->pdo->proto_id, row_text, (pdata->start_sample * 1000000) / samplerate);
+		printf("{\"name\": \"%s\", \"ph\": \"E\", \"pid\": \"%s\", \"tid\": \"%s\", \"ts\": %"PRIu64"}", pda->ann_text[0], pdata->pdo->proto_id, row_text, (pdata->end_sample * 1000000) / samplerate);
+		first_line = FALSE;
 	}
-	quote = show_quotes ? "\"" : "";
-	printf("%s%s%s", quote, pda->ann_text[0], quote);
-	if (show_abbrev) {
-		for (i = 1; pda->ann_text[i]; i++)
-			printf(" %s%s%s", quote, pda->ann_text[i], quote);
+	else
+	{
+		if (show_snum) {
+			printf("%" PRIu64 "-%" PRIu64 " ",
+				pdata->start_sample, pdata->end_sample);
+		}
+		printf("%s: ", pdata->pdo->proto_id);
+		if (show_class) {
+			ann_descr = g_slist_nth_data(dec->annotations, pda->ann_class);
+			printf("%s: ", ann_descr[0]);
+		}
+		quote = show_quotes ? "\"" : "";
+		printf("%s%s%s", quote, pda->ann_text[0], quote);
+		if (show_abbrev) {
+			for (i = 1; pda->ann_text[i]; i++)
+				printf(" %s%s%s", quote, pda->ann_text[i], quote);
+		}
+		printf("\n");
 	}
-	printf("\n");
 	fflush(stdout);
 }
 
diff --git a/main.c b/main.c
index c038b6f..0630e22 100644
--- a/main.c
+++ b/main.c
@@ -216,6 +216,9 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
+	if(opt_pd_jsontrace)
+		printf("{\"traceEvents\": [\n");
+
 	/* Set the loglevel (amount of messages to output) for libsigrok. */
 	if (sr_log_loglevel_set(opt_loglevel) != SR_OK)
 		goto done;
@@ -300,5 +303,8 @@ done:
 	if (sr_ctx)
 		sr_exit(sr_ctx);
 
+	if(opt_pd_jsontrace)
+		printf("\n]}\n");
+
 	return 0;
 }
diff --git a/options.c b/options.c
index b9e1494..b98d7c4 100644
--- a/options.c
+++ b/options.c
@@ -39,6 +39,7 @@ gchar *opt_pd_annotations = NULL;
 gchar *opt_pd_meta = NULL;
 gchar *opt_pd_binary = NULL;
 gboolean opt_pd_samplenum = FALSE;
+gboolean opt_pd_jsontrace = FALSE;
 #endif
 gchar *opt_input_format = NULL;
 gchar *opt_output_format = NULL;
@@ -137,6 +138,8 @@ static const GOptionEntry optargs[] = {
 			"Protocol decoder binary output to show", NULL},
 	{"protocol-decoder-samplenum", 0, 0, G_OPTION_ARG_NONE, &opt_pd_samplenum,
 			"Show sample numbers in decoder output", NULL},
+	{"protocol-decoder-jsontrace", 0, 0, G_OPTION_ARG_NONE, &opt_pd_jsontrace,
+			"Output in Google JSON trace format", NULL},
 #endif
 	{"scan", 0, 0, G_OPTION_ARG_NONE, &opt_scan_devs,
 			"Scan for devices", NULL},
diff --git a/sigrok-cli.h b/sigrok-cli.h
index f564448..5d54dde 100644
--- a/sigrok-cli.h
+++ b/sigrok-cli.h
@@ -111,6 +111,7 @@ extern gchar *opt_pd_annotations;
 extern gchar *opt_pd_meta;
 extern gchar *opt_pd_binary;
 extern gboolean opt_pd_samplenum;
+extern gboolean opt_pd_jsontrace;
 #endif
 extern gchar *opt_input_format;
 extern gchar *opt_output_format;
-- 
2.11.0

