server: tcl_trace command 88/2588/7
authorAustin Morton <austinpmorton@gmail.com>
Mon, 9 Mar 2015 09:34:52 +0000 (05:34 -0400)
committerPaul Fertser <fercerpav@gmail.com>
Sat, 5 Sep 2015 08:19:26 +0000 (09:19 +0100)
Implements async target trace output to the tcl server

Change-Id: I0178f6404447337d523782a1d2c317457030da40
Signed-off-by: Austin Morton <austinpmorton@gmail.com>
Reviewed-on: http://openocd.zylin.com/2588
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>
doc/openocd.texi
src/server/tcl_server.c
src/target/armv7m_trace.c
src/target/target.c
src/target/target.h

index 63ab5deb5dc29bc00ea612adcdc1e97387891295..21141caae6b25723b4cfd90c6ae84925fd8315ba 100644 (file)
@@ -7570,7 +7570,7 @@ Defaulting to 0.
 @cindex ITM
 @cindex ETM
 
 @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} | -)}) @
                (@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
                @var{TRACECLKIN_freq} [@var{trace_freq}]))
 
                (@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
                @var{TRACECLKIN_freq} [@var{trace_freq}]))
 
@@ -7594,6 +7594,8 @@ 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 @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;
 @item @option{sync @var{port_width}} use synchronous parallel trace output
 mode, and set port width to @var{port_width};
 @item @option{manchester} use asynchronous SWO mode with Manchester
 @item @option{sync @var{port_width}} use synchronous parallel trace output
 mode, and set port width to @var{port_width};
 @item @option{manchester} use asynchronous SWO mode with Manchester
@@ -8699,6 +8701,28 @@ Defaults to off.
 
 @end deffn
 
 
 @end deffn
 
+@section Tcl RPC server trace output
+@cindex RPC trace output
+
+Trace data is sent asynchronously to other commands being executed over
+the RPC server, so the port must be polled continuously.
+
+Target trace data is emitted as a Tcl associative array in the following format.
+
+@verbatim
+type target_trace data [trace-data-hex-encoded]
+@end verbatim
+
+@deffn {Command} tcl_trace [on/off]
+Toggle output of target trace data to the current Tcl RPC server.
+Only available from the Tcl RPC server.
+Defaults to off.
+
+See an example application here:
+@url{https://github.com/apmorton/OpenOcdTraceUtil} [OpenOcdTraceUtil]
+
+@end deffn
+
 @node FAQ
 @chapter FAQ
 @cindex faq
 @node FAQ
 @chapter FAQ
 @cindex faq
index 65f71cc988f19f64c652b227bf0bb8c9b41e461d..409567c9d90e9e515cee078dea3b3048409daf4e 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "tcl_server.h"
 #include <target/target.h>
 
 #include "tcl_server.h"
 #include <target/target.h>
+#include <helper/binarybuffer.h>
 
 #define TCL_SERVER_VERSION             "TCL Server 0.1"
 #define TCL_MAX_LINE                   (4096)
 
 #define TCL_SERVER_VERSION             "TCL Server 0.1"
 #define TCL_MAX_LINE                   (4096)
@@ -35,6 +36,7 @@ struct tcl_connection {
        int tc_outerror;/* flag an output error */
        enum target_state tc_laststate;
        bool tc_notify;
        int tc_outerror;/* flag an output error */
        enum target_state tc_laststate;
        bool tc_notify;
+       bool tc_trace;
 };
 
 static char *tcl_port;
 };
 
 static char *tcl_port;
@@ -87,6 +89,32 @@ static int tcl_target_callback_reset_handler(struct target *target,
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+static int tcl_target_callback_trace_handler(struct target *target,
+               size_t len, uint8_t *data, void *priv)
+{
+       struct connection *connection = priv;
+       struct tcl_connection *tclc;
+       char *header = "type target_trace data ";
+       char *trailer = "\r\n\x1a";
+       size_t hex_len = len * 2 + 1;
+       size_t max_len = hex_len + strlen(header) + strlen(trailer);
+       char *buf, *hex;
+
+       tclc = connection->priv;
+
+       if (tclc->tc_trace) {
+               hex = malloc(hex_len);
+               buf = malloc(max_len);
+               hexify(hex, (const char *)data, len, hex_len);
+               snprintf(buf, max_len, "%s%s%s", header, hex, trailer);
+               tcl_output(connection, buf, strlen(buf));
+               free(hex);
+               free(buf);
+       }
+
+       return ERROR_OK;
+}
+
 /* write data out to a socket.
  *
  * this is a blocking write, so the return value must equal the length, if
 /* write data out to a socket.
  *
  * this is a blocking write, so the return value must equal the length, if
@@ -132,6 +160,7 @@ static int tcl_new_connection(struct connection *connection)
 
        target_register_event_callback(tcl_target_callback_event_handler, connection);
        target_register_reset_callback(tcl_target_callback_reset_handler, connection);
 
        target_register_event_callback(tcl_target_callback_event_handler, connection);
        target_register_reset_callback(tcl_target_callback_reset_handler, connection);
+       target_register_trace_callback(tcl_target_callback_trace_handler, connection);
 
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
@@ -183,7 +212,7 @@ static int tcl_input(struct connection *connection)
 #undef ESTR
                } else {
                        tclc->tc_line[tclc->tc_lineoffset-1] = '\0';
 #undef ESTR
                } else {
                        tclc->tc_line[tclc->tc_lineoffset-1] = '\0';
-                       retval = command_run_line(connection->cmd_ctx, tclc->tc_line);
+                       command_run_line(connection->cmd_ctx, tclc->tc_line);
                        result = Jim_GetString(Jim_GetResult(interp), &reslen);
                        retval = tcl_output(connection, result, reslen);
                        if (retval != ERROR_OK)
                        result = Jim_GetString(Jim_GetResult(interp), &reslen);
                        retval = tcl_output(connection, result, reslen);
                        if (retval != ERROR_OK)
@@ -209,6 +238,7 @@ static int tcl_closed(struct connection *connection)
 
        target_unregister_event_callback(tcl_target_callback_event_handler, connection);
        target_unregister_reset_callback(tcl_target_callback_reset_handler, connection);
 
        target_unregister_event_callback(tcl_target_callback_event_handler, connection);
        target_unregister_reset_callback(tcl_target_callback_reset_handler, connection);
+       target_unregister_trace_callback(tcl_target_callback_trace_handler, connection);
 
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
@@ -240,7 +270,24 @@ COMMAND_HANDLER(handle_tcl_notifications_command)
 
        if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
                tclc = connection->priv;
 
        if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
                tclc = connection->priv;
-               return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_notify, "Target Notification output is");
+               return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_notify, "Target Notification output ");
+       } else {
+               LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+}
+
+COMMAND_HANDLER(handle_tcl_trace_command)
+{
+       struct connection *connection = NULL;
+       struct tcl_connection *tclc = NULL;
+
+       if (CMD_CTX->output_handler_priv != NULL)
+               connection = CMD_CTX->output_handler_priv;
+
+       if (connection != NULL && !strcmp(connection->service->name, "tcl")) {
+               tclc = connection->priv;
+               return CALL_COMMAND_HANDLER(handle_command_parse_bool, &tclc->tc_trace, "Target trace output ");
        } else {
                LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
                return ERROR_COMMAND_SYNTAX_ERROR;
        } else {
                LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME);
                return ERROR_COMMAND_SYNTAX_ERROR;
@@ -264,6 +311,13 @@ static const struct command_registration tcl_command_handlers[] = {
                .help = "Target Notification output",
                .usage = "[on|off]",
        },
                .help = "Target Notification output",
                .usage = "[on|off]",
        },
+       {
+               .name = "tcl_trace",
+               .handler = handle_tcl_trace_command,
+               .mode = COMMAND_EXEC,
+               .help = "Target trace output",
+               .usage = "[on|off]",
+       },
        COMMAND_REGISTRATION_DONE
 };
 
        COMMAND_REGISTRATION_DONE
 };
 
index eb07a6e64056f9db57907b0b28c22496159c5069..3592bad0f64c2f59f8120b2e5787f80676130b12 100644 (file)
@@ -35,11 +35,15 @@ static int armv7m_poll_trace(void *target)
        if (retval != ERROR_OK || !size)
                return retval;
 
        if (retval != ERROR_OK || !size)
                return retval;
 
-       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;
+       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;
+               }
        }
 
        return ERROR_OK;
        }
 
        return ERROR_OK;
@@ -183,10 +187,13 @@ COMMAND_HANDLER(handle_tpiu_config_command)
                                return ERROR_COMMAND_SYNTAX_ERROR;
 
                        armv7m->trace_config.config_type = INTERNAL;
                                return ERROR_COMMAND_SYNTAX_ERROR;
 
                        armv7m->trace_config.config_type = INTERNAL;
-                       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 (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;
+                               }
                        }
                }
                cmd_idx++;
                        }
                }
                cmd_idx++;
index 89fee4aaa5fa7899fed3dfd96ee448bd734ab997..19e5d6515e9ba4f88a2ed06e77a975b12639d0b9 100644 (file)
@@ -140,6 +140,7 @@ struct target *all_targets;
 static struct target_event_callback *target_event_callbacks;
 static struct target_timer_callback *target_timer_callbacks;
 LIST_HEAD(target_reset_callback_list);
 static struct target_event_callback *target_event_callbacks;
 static struct target_timer_callback *target_timer_callbacks;
 LIST_HEAD(target_reset_callback_list);
+LIST_HEAD(target_trace_callback_list);
 static const int polling_interval = 100;
 
 static const Jim_Nvp nvp_assert[] = {
 static const int polling_interval = 100;
 
 static const Jim_Nvp nvp_assert[] = {
@@ -1350,6 +1351,28 @@ int target_register_reset_callback(int (*callback)(struct target *target,
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+int target_register_trace_callback(int (*callback)(struct target *target,
+               size_t len, uint8_t *data, void *priv), void *priv)
+{
+       struct target_trace_callback *entry;
+
+       if (callback == NULL)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       entry = malloc(sizeof(struct target_trace_callback));
+       if (entry == NULL) {
+               LOG_ERROR("error allocating buffer for trace callback entry");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       entry->callback = callback;
+       entry->priv = priv;
+       list_add(&entry->list, &target_trace_callback_list);
+
+
+       return ERROR_OK;
+}
+
 int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv)
 {
        struct target_timer_callback **callbacks_p = &target_timer_callbacks;
 int target_register_timer_callback(int (*callback)(void *priv), int time_ms, int periodic, void *priv)
 {
        struct target_timer_callback **callbacks_p = &target_timer_callbacks;
@@ -1427,6 +1450,25 @@ int target_unregister_reset_callback(int (*callback)(struct target *target,
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+int target_unregister_trace_callback(int (*callback)(struct target *target,
+               size_t len, uint8_t *data, void *priv), void *priv)
+{
+       struct target_trace_callback *entry;
+
+       if (callback == NULL)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       list_for_each_entry(entry, &target_trace_callback_list, list) {
+               if (entry->callback == callback && entry->priv == priv) {
+                       list_del(&entry->list);
+                       free(entry);
+                       break;
+               }
+       }
+
+       return ERROR_OK;
+}
+
 int target_unregister_timer_callback(int (*callback)(void *priv), void *priv)
 {
        if (callback == NULL)
 int target_unregister_timer_callback(int (*callback)(void *priv), void *priv)
 {
        if (callback == NULL)
@@ -1480,6 +1522,16 @@ int target_call_reset_callbacks(struct target *target, enum target_reset_mode re
        return ERROR_OK;
 }
 
        return ERROR_OK;
 }
 
+int target_call_trace_callbacks(struct target *target, size_t len, uint8_t *data)
+{
+       struct target_trace_callback *callback;
+
+       list_for_each_entry(callback, &target_trace_callback_list, list)
+               callback->callback(target, len, data, callback->priv);
+
+       return ERROR_OK;
+}
+
 static int target_timer_callback_periodic_restart(
                struct target_timer_callback *cb, struct timeval *now)
 {
 static int target_timer_callback_periodic_restart(
                struct target_timer_callback *cb, struct timeval *now)
 {
index 7471c1b330657b27e6e7313a9277f0ef344a8274..4faf3119f6a02efb9fb864eabbb8e8e7674743d6 100644 (file)
@@ -292,6 +292,12 @@ struct target_reset_callback {
        int (*callback)(struct target *target, enum target_reset_mode reset_mode, void *priv);
 };
 
        int (*callback)(struct target *target, enum target_reset_mode reset_mode, void *priv);
 };
 
+struct target_trace_callback {
+       struct list_head list;
+       void *priv;
+       int (*callback)(struct target *target, size_t len, uint8_t *data, void *priv);
+};
+
 struct target_timer_callback {
        int (*callback)(void *priv);
        int time_ms;
 struct target_timer_callback {
        int (*callback)(void *priv);
        int time_ms;
@@ -323,6 +329,15 @@ int target_unregister_reset_callback(
                enum target_reset_mode reset_mode, void *priv),
                void *priv);
 
                enum target_reset_mode reset_mode, void *priv),
                void *priv);
 
+int target_register_trace_callback(
+               int (*callback)(struct target *target,
+               size_t len, uint8_t *data, void *priv),
+               void *priv);
+int target_unregister_trace_callback(
+               int (*callback)(struct target *target,
+               size_t len, uint8_t *data, void *priv),
+               void *priv);
+
 /* Poll the status of the target, detect any error conditions and report them.
  *
  * Also note that this fn will clear such error conditions, so a subsequent
 /* Poll the status of the target, detect any error conditions and report them.
  *
  * Also note that this fn will clear such error conditions, so a subsequent
@@ -341,6 +356,7 @@ int target_resume(struct target *target, int current, uint32_t address,
 int target_halt(struct target *target);
 int target_call_event_callbacks(struct target *target, enum target_event event);
 int target_call_reset_callbacks(struct target *target, enum target_reset_mode reset_mode);
 int target_halt(struct target *target);
 int target_call_event_callbacks(struct target *target, enum target_event event);
 int target_call_reset_callbacks(struct target *target, enum target_reset_mode reset_mode);
+int target_call_trace_callbacks(struct target *target, size_t len, uint8_t *data);
 
 /**
  * The period is very approximate, the callback can happen much more often
 
 /**
  * The period is very approximate, the callback can happen much more often

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)