]> sigrok.org Git - sigrok-cli.git/commitdiff
decode: add support for Google Trace Event output (JSON)
authorGerhard Sittig <redacted>
Sat, 25 Jul 2020 07:28:40 +0000 (09:28 +0200)
committerGerhard Sittig <redacted>
Sat, 25 Jul 2020 11:14:10 +0000 (13:14 +0200)
Introduce the --protocol-decoder-jsontrace command line option, which
outputs protocol decoder annotations in the Google Trace Event format.
Resulting files can be viewed by means of the chrome://tracing URL in
the Chrome browser (optionally zipped).

Example use:
  $ sigrok-cli -i $F -P uart -A uart=rx-data --protocol-decoder-jsontrace

The initial implementation was
Submitted-By: Dave Craig <redacted>
This commit introduces generic prepare and close support, should other
output formats need to flush previously accumulated data, or defer the
creation of a header and make sure to add a footer around the sequence
of annotations.

decode.c
main.c
options.c
sigrok-cli.h

index d27703929fe7d34a7e972627b87a5e0fd7d723e9..06400f36e87516caa56a97d2cd14e5e9bf88d9fb 100644 (file)
--- a/decode.c
+++ b/decode.c
@@ -480,6 +480,116 @@ int setup_pd_binary(char *opt_pd_binary)
        return 0;
 }
 
+/*
+ * Balance JSON object and array parentheses, and separate array items.
+ * Somewhat convoluted API to re-use the routine for individual items as
+ * well as the surrounding array and object, including deferred start of
+ * the output and late flush (and to keep the state strictly local to the
+ * routine). Some additional complexity due to JSON's inability to handle
+ * a trailing comma at the last item. Code phrased such that text literals
+ * are kept in their order of appearance in the output text.
+ */
+static void jsontrace_open_close(gboolean is_close_req)
+{
+       static gboolean is_opened;
+
+       if (!is_close_req) {
+               if (!is_opened) {
+                       printf("{\"traceEvents\": [\n");
+                       is_opened = TRUE;
+               } else {
+                       printf(",\n");
+               }
+       } else {
+               if (is_opened) {
+                       printf("\n");
+                       printf("]}\n");
+                       fflush(stdout);
+               }
+               is_opened = FALSE;
+       }
+}
+
+/* Convert uint64 sample number to double timestamp in microseconds. */
+static double jsontrace_ts_usec(uint64_t snum)
+{
+       double ts_usec;
+
+       ts_usec = snum;
+       ts_usec *= 1e6;
+       ts_usec /= pd_samplerate;
+       return ts_usec;
+}
+
+/* Emit two Google Trace Events (JSON) for one PD annotation (ss, es). */
+static void jsontrace_annotation(struct srd_decoder *dec,
+       struct srd_proto_data_annotation *pda, struct srd_proto_data *pdata)
+{
+       char *row_text;
+       GSList *lrow, *lcls;
+       struct srd_decoder_annotation_row *row;
+       int cls;
+       char **ann_descr;
+
+       /*
+        * Search for an annotation row for this index, or use the
+        * annotation's descriptor.
+        */
+       row_text = NULL;
+       if (dec->annotation_rows) {
+               for (lrow = dec->annotation_rows; lrow; lrow = lrow->next) {
+                       row = lrow->data;
+                       for (lcls = row->ann_classes; lcls; lcls = lcls->next) {
+                               cls = GPOINTER_TO_INT(lcls->data);
+                               if (cls == pda->ann_class) {
+                                       row_text = row->desc;
+                                       break;
+                               }
+                       }
+                       if (row_text)
+                               break;
+               }
+       }
+       if (!row_text) {
+               ann_descr = g_slist_nth_data(dec->annotations, pda->ann_class);
+               row_text = ann_descr[0];
+       }
+
+       /*
+        * Emit two Google Trace Events for the start and end times.
+        * Set the 'pid' (process ID) to the decoder name to group a
+        * decoder's annotations. Set the 'tid' (thread ID) to the
+        * annotation row's description. The 'ts' (timestamp) is in
+        * microseconds. Set 'name' to the longest annotation text.
+        *
+        * BEWARE of the unfortunate JSON format comma limitation. And
+        * some of the output formatting is motivated by the desire to
+        * further reduce text size, by eliminating some of the spaces.
+        *
+        * This implementation is strictly compatible to the initial
+        * implementation. Which might change in the future to increase
+        * readability of the output to humans, by generating a layout
+        * which is closer to other output modes.
+        */
+       jsontrace_open_close(FALSE);
+       printf("{");
+       printf("\"%s\": \"%s\"", "name", pda->ann_text[0]);
+       printf(", \"%s\": \"%s\"", "ph", "B");
+       printf(", \"%s\": \"%s\"", "pid", pdata->pdo->proto_id);
+       printf(", \"%s\": \"%s\"", "tid", row_text);
+       printf(", \"%s\": %lf", "ts", jsontrace_ts_usec(pdata->start_sample));
+       printf("}");
+
+       jsontrace_open_close(FALSE);
+       printf("{");
+       printf("\"%s\": \"%s\"", "name", pda->ann_text[0]);
+       printf(", \"%s\": \"%s\"", "ph", "E");
+       printf(", \"%s\": \"%s\"", "pid", pdata->pdo->proto_id);
+       printf(", \"%s\": \"%s\"", "tid", row_text);
+       printf(", \"%s\": %lf", "ts", jsontrace_ts_usec(pdata->end_sample));
+       printf("}");
+}
+
 void show_pd_annotations(struct srd_proto_data *pdata, void *cb_data)
 {
        struct srd_decoder *dec;
@@ -514,6 +624,12 @@ void show_pd_annotations(struct srd_proto_data *pdata, void *cb_data)
        if (!show_ann)
                return;
 
+       /* Google Trace Events are rather special. Use a separate code path. */
+       if (opt_pd_jsontrace) {
+               jsontrace_annotation(dec, pda, pdata);
+               return;
+       }
+
        /*
         * Determine which fields of the annotation to display. Inspect
         * user specified options as well as the verbosity of the log level:
@@ -601,4 +717,16 @@ void show_pd_binary(struct srd_proto_data *pdata, void *cb_data)
        fwrite(pdb->data, pdb->size, 1, stdout);
        fflush(stdout);
 }
+
+void show_pd_prepare(void)
+{
+       if (opt_pd_jsontrace)
+               jsontrace_open_close(TRUE);
+}
+
+void show_pd_close(void)
+{
+       if (opt_pd_jsontrace)
+               jsontrace_open_close(TRUE);
+}
 #endif
diff --git a/main.c b/main.c
index c2f39d84ff601fec51236cf261f723016bd2e552..aa4f1229888fa91f961cce2d88dd32e1cd93c997 100644 (file)
--- a/main.c
+++ b/main.c
@@ -261,6 +261,7 @@ int main(int argc, char **argv)
                                        show_pd_annotations, NULL) != SRD_OK)
                                goto done;
                }
+               show_pd_prepare();
        }
 #endif
 
@@ -300,6 +301,8 @@ int main(int argc, char **argv)
                show_help();
 
 #ifdef HAVE_SRD
+       if (opt_pds)
+               show_pd_close();
        if (opt_pds)
                srd_exit();
 #endif
index 5a6f9487769f640bd4e04a71c61832bcf6f0191e..ebd620d597b37417dcd65338da4b7202b274dc16 100644 (file)
--- a/options.c
+++ b/options.c
@@ -41,6 +41,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;
@@ -142,6 +143,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 Trace Event format (JSON)", NULL},
 #endif
        {"scan", 0, 0, G_OPTION_ARG_NONE, &opt_scan_devs,
                        "Scan for devices", NULL},
index 10e74deafc8224f826c65bf3d62bc8c8456e2791..79a637aa3717fd5b0b68430c1df766ad160d12bb 100644 (file)
@@ -95,6 +95,8 @@ int setup_pd_binary(char *opt_pd_binary);
 void show_pd_annotations(struct srd_proto_data *pdata, void *cb_data);
 void show_pd_meta(struct srd_proto_data *pdata, void *cb_data);
 void show_pd_binary(struct srd_proto_data *pdata, void *cb_data);
+void show_pd_prepare(void);
+void show_pd_close(void);
 void map_pd_channels(struct sr_dev_inst *sdi);
 #endif
 
@@ -133,6 +135,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;