armv7m: add a TCP channel to stream captured trace 45/5345/9
authorTarek BOCHKATI <tarek.bouchkati@gmail.com>
Mon, 8 Jun 2020 22:47:46 +0000 (23:47 +0100)
committerAntonio Borneo <borneo.antonio@gmail.com>
Sat, 7 Nov 2020 20:49:57 +0000 (20:49 +0000)
When trace capturing the trace is enabled using 'tpiu_config internal'
(via the internal mode), OpenOCD can collect the trace buffers then append
it to a specified file or named pipe and propagate the trace to 'tcl_trace'
command.
This change is allowing OpenOCD to stream the captured trace over TCP.

When using this configuration OpenOCD acts like a server and multiple
clients can connect and receive the captured trace.

Example on STM32F7 running at 216MHz:
  itm port 0 on
  tpiu config internal :3344 uart off 216000000

Change-Id: Idea43e7e26e87b98a33da7fb9acf7ea50fe3b345
Signed-off-by: Tarek BOCHKATI <tarek.bouchkati@gmail.com>
Reviewed-on: http://openocd.zylin.com/5345
Tested-by: jenkins
Reviewed-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
doc/openocd.texi
src/target/armv7m_trace.c
src/target/armv7m_trace.h

index e6a14673f732bc93415a65a5f5f09340020d79d6..b01e5a6c1bf64bb2296f0f490f127605653a2218 100644 (file)
@@ -9206,7 +9206,7 @@ Selects whether interrupts will be processed when single stepping
 @cindex ITM
 @cindex ETM
 
-@deffn Command {tpiu config} (@option{disable} | ((@option{external} | @option{internal (@var{filename} | -)}) @
+@deffn Command {tpiu config} (@option{disable} | ((@option{external} | @option{internal (@var{filename} | @var{:port} | -)}) @
                (@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
                @var{TRACECLKIN_freq} [@var{trace_freq}]))
 
@@ -9226,23 +9226,28 @@ Command options:
 @itemize @minus
 @item @option{disable} disable TPIU handling;
 @item @option{external} configure TPIU to let user capture trace
-output externally (with an additional UART or logic analyzer hardware);
-@item @option{internal @var{filename}} configure TPIU and debug adapter to
-gather trace data and append it to @var{filename} (which can be
-either a regular file or a named pipe);
-@item @option{internal -} configure TPIU and debug adapter to
-gather trace data, but not write to any file. Useful in conjunction with the @command{tcl_trace} command;
+output externally (with an additional UART or logic analyzer hardware).
+@item @option{internal (@var{filename} | @var{:port} | -)} configure TPIU and debug adapter to
+gather trace data then:
+
+@itemize @minus
+@item append it to a regular file or a named pipe if @var{filename} is specified.
+@item listen to a TCP/IP port if @var{:port} is specified, then broadcast the trace data over this port.
+@item if '-' is specified, OpenOCD will forward trace data to @command{tcl_trace} command.
+@*@b{Note:} while broadcasting to file or TCP, the forwarding to @command{tcl_trace} will remain active.
+@end itemize
+
 @item @option{sync @var{port_width}} use synchronous parallel trace output
-mode, and set port width to @var{port_width};
+mode, and set port width to @var{port_width}.
 @item @option{manchester} use asynchronous SWO mode with Manchester
-coding;
+coding.
 @item @option{uart} use asynchronous SWO mode with NRZ (same as
-regular UART 8N1) coding;
+regular UART 8N1) coding.
 @item @var{formatter_enable} is @option{on} or @option{off} to enable
 or disable TPIU formatter which needs to be used when both ITM and ETM
-data is to be output via SWO;
+data is to be output via SWO.
 @item @var{TRACECLKIN_freq} this should be specified to match target's
-current TRACECLKIN frequency (usually the same as HCLK);
+current TRACECLKIN frequency (usually the same as HCLK).
 @item @var{trace_freq} trace port frequency. Can be omitted in
 internal mode to let the adapter driver select the maximum supported
 rate automatically.
index 6b368f7a09c4ce65372f9324e97fa58c69f9bced..916d1a1647866d8d8ad4f938966e32dffb725cb3 100644 (file)
@@ -40,13 +40,43 @@ static int armv7m_poll_trace(void *target)
 
        target_call_trace_callbacks(target, size, buf);
 
-       if (armv7m->trace_config.trace_file != NULL) {
-               if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size)
-                       fflush(armv7m->trace_config.trace_file);
-               else {
-                       LOG_ERROR("Error writing to the trace destination file");
-                       return ERROR_FAIL;
+       switch (armv7m->trace_config.internal_channel) {
+       case TRACE_INTERNAL_CHANNEL_FILE:
+               if (armv7m->trace_config.trace_file != NULL) {
+                       if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size)
+                               fflush(armv7m->trace_config.trace_file);
+                       else {
+                               LOG_ERROR("Error writing to the trace destination file");
+                               return ERROR_FAIL;
+                       }
+               }
+               break;
+       case TRACE_INTERNAL_CHANNEL_TCP:
+               if (armv7m->trace_config.trace_service != NULL) {
+                       /* broadcast to all service connections */
+                       struct connection *connection = armv7m->trace_config.trace_service->connections;
+                       retval = ERROR_OK;
+                       while (connection) {
+                               if (connection_write(connection, buf, size) != (int) size)
+                                       retval = ERROR_FAIL;
+
+                               connection = connection->next;
+                       }
+
+                       if (retval != ERROR_OK) {
+                               LOG_ERROR("Error streaming the trace to TCP/IP port");
+                               return ERROR_FAIL;
+                       }
                }
+               break;
+       case TRACE_INTERNAL_CHANNEL_TCL_ONLY:
+               /* nothing to do :
+                * the trace data is sent to TCL by calling the target_call_trace_callbacks
+                **/
+               break;
+       default:
+               LOG_ERROR("unsupported trace internal channel");
+               return ERROR_FAIL;
        }
 
        return ERROR_OK;
@@ -152,11 +182,56 @@ int armv7m_trace_itm_config(struct target *target)
        return ERROR_OK;
 }
 
-static void close_trace_file(struct armv7m_common *armv7m)
+static void close_trace_channel(struct armv7m_common *armv7m)
 {
-       if (armv7m->trace_config.trace_file)
-               fclose(armv7m->trace_config.trace_file);
-       armv7m->trace_config.trace_file = NULL;
+       switch (armv7m->trace_config.internal_channel) {
+       case TRACE_INTERNAL_CHANNEL_FILE:
+               if (armv7m->trace_config.trace_file)
+                       fclose(armv7m->trace_config.trace_file);
+               armv7m->trace_config.trace_file = NULL;
+               break;
+       case TRACE_INTERNAL_CHANNEL_TCP:
+               if (armv7m->trace_config.trace_service)
+                       remove_service(armv7m->trace_config.trace_service->name, armv7m->trace_config.trace_service->port);
+               armv7m->trace_config.trace_service = NULL;
+               break;
+       case TRACE_INTERNAL_CHANNEL_TCL_ONLY:
+               /* nothing to do:
+                * the trace polling is disabled in the beginning of armv7m_trace_tpiu_config
+                **/
+               break;
+       default:
+               LOG_ERROR("unsupported trace internal channel");
+       }
+}
+
+static int trace_new_connection(struct connection *connection)
+{
+       /* nothing to do */
+       return ERROR_OK;
+}
+
+static int trace_input(struct connection *connection)
+{
+       /* create a dummy buffer to check if the connection is still active */
+       const int buf_len = 100;
+       unsigned char buf[buf_len];
+       int bytes_read = connection_read(connection, buf, buf_len);
+
+       if (bytes_read == 0)
+               return ERROR_SERVER_REMOTE_CLOSED;
+       else if (bytes_read == -1) {
+               LOG_ERROR("error during read: %s", strerror(errno));
+               return ERROR_SERVER_REMOTE_CLOSED;
+       }
+
+       return ERROR_OK;
+}
+
+static int trace_connection_closed(struct connection *connection)
+{
+       /* nothing to do, no connection->priv to free */
+       return ERROR_OK;
 }
 
 COMMAND_HANDLER(handle_tpiu_config_command)
@@ -170,7 +245,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
                return ERROR_COMMAND_SYNTAX_ERROR;
        if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
                if (CMD_ARGC == cmd_idx + 1) {
-                       close_trace_file(armv7m);
+                       close_trace_channel(armv7m);
 
                        armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
                        if (CMD_CTX->mode == COMMAND_EXEC)
@@ -180,7 +255,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
                }
        } else if (!strcmp(CMD_ARGV[cmd_idx], "external") ||
                   !strcmp(CMD_ARGV[cmd_idx], "internal")) {
-               close_trace_file(armv7m);
+               close_trace_channel(armv7m);
 
                armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
                if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
@@ -189,12 +264,26 @@ COMMAND_HANDLER(handle_tpiu_config_command)
                                return ERROR_COMMAND_SYNTAX_ERROR;
 
                        armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL;
+                       armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCL_ONLY;
 
                        if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
-                               armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
-                               if (!armv7m->trace_config.trace_file) {
-                                       LOG_ERROR("Can't open trace destination file");
-                                       return ERROR_FAIL;
+                               if (CMD_ARGV[cmd_idx][0] == ':') {
+                                       armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCP;
+
+                                       int ret = add_service("armv7m_trace", &(CMD_ARGV[cmd_idx][1]),
+                                                       CONNECTION_LIMIT_UNLIMITED, trace_new_connection, trace_input,
+                                                       trace_connection_closed, NULL, &armv7m->trace_config.trace_service);
+                                       if (ret != ERROR_OK) {
+                                               LOG_ERROR("Can't configure trace TCP port");
+                                               return ERROR_FAIL;
+                                       }
+                               } else {
+                                       armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_FILE;
+                                       armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab");
+                                       if (!armv7m->trace_config.trace_file) {
+                                               LOG_ERROR("Can't open trace destination file");
+                                               return ERROR_FAIL;
+                                       }
                                }
                        }
                }
@@ -306,7 +395,7 @@ static const struct command_registration tpiu_command_handlers[] = {
                .mode = COMMAND_ANY,
                .help = "Configure TPIU features",
                .usage = "(disable | "
-               "((external | internal <filename>) "
+               "((external | internal (<filename> | <:port> | -)) "
                "(sync <port width> | ((manchester | uart) <formatter enable>)) "
                "<TRACECLKIN freq> [<trace freq>]))",
        },
index e5879fb082a6d67bd928a3bcccf597af502d6356..076f9d5829d119b4a2fddfe9c5cada03a7524008 100644 (file)
@@ -18,6 +18,7 @@
 #ifndef OPENOCD_TARGET_ARMV7M_TRACE_H
 #define OPENOCD_TARGET_ARMV7M_TRACE_H
 
+#include <server/server.h>
 #include <target/target.h>
 #include <command.h>
 
@@ -32,8 +33,14 @@ enum trace_config_type {
        TRACE_CONFIG_TYPE_INTERNAL      /**< trace output is handled by OpenOCD adapter driver */
 };
 
+enum trace_internal_channel {
+       TRACE_INTERNAL_CHANNEL_TCL_ONLY,        /** trace data is sent only to 'tcl_trace'  */
+       TRACE_INTERNAL_CHANNEL_FILE,            /** trace data is appended to a file */
+       TRACE_INTERNAL_CHANNEL_TCP                      /** trace data is appended to a TCP/IP port*/
+};
+
 enum tpiu_pin_protocol {
-       TPIU_PIN_PROTOCOL_SYNC,                 /**< synchronous trace output */
+       TPIU_PIN_PROTOCOL_SYNC,                         /**< synchronous trace output */
        TPIU_PIN_PROTOCOL_ASYNC_MANCHESTER,     /**< asynchronous output with Manchester coding */
        TPIU_PIN_PROTOCOL_ASYNC_UART            /**< asynchronous output with NRZ coding */
 };
@@ -49,6 +56,9 @@ struct armv7m_trace_config {
        /** Currently active trace capture mode */
        enum trace_config_type config_type;
 
+       /** The used channel when internal mode is selected */
+       enum trace_internal_channel internal_channel;
+
        /** Currently active trace output mode */
        enum tpiu_pin_protocol pin_protocol;
        /** TPIU formatter enable/disable (in async mode) */
@@ -73,8 +83,10 @@ struct armv7m_trace_config {
        unsigned int traceclkin_freq;
        /** Current frequency of trace port */
        unsigned int trace_freq;
-       /** Handle to output trace data in INTERNAL capture mode */
+       /** Handle to output trace data in INTERNAL capture mode via file */
        FILE *trace_file;
+       /** Handle to output trace data in INTERNAL capture mode via tcp */
+       struct service *trace_service;
 };
 
 extern const struct command_registration armv7m_trace_command_handlers[];

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)