From: Gerhard Sittig Date: Sat, 25 Jul 2020 07:28:40 +0000 (+0200) Subject: decode: add support for Google Trace Event output (JSON) X-Git-Url: http://sigrok.org/gitweb/?p=sigrok-cli.git;a=commitdiff_plain;h=54614916a903af4afc2582db793eb306e39ddd0a decode: add support for Google Trace Event output (JSON) 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 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. --- diff --git a/decode.c b/decode.c index d277039..06400f3 100644 --- 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 c2f39d8..aa4f122 100644 --- 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 diff --git a/options.c b/options.c index 5a6f948..ebd620d 100644 --- 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}, diff --git a/sigrok-cli.h b/sigrok-cli.h index 10e74de..79a637a 100644 --- a/sigrok-cli.h +++ b/sigrok-cli.h @@ -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;