armv7m: add a TCP channel to stream captured trace

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>
This commit is contained in:
Tarek BOCHKATI 2020-06-08 23:47:46 +01:00 committed by Antonio Borneo
parent 7e6556b3ca
commit 9cce6b3c76
3 changed files with 137 additions and 31 deletions

View File

@ -9206,7 +9206,7 @@ Selects whether interrupts will be processed when single stepping
@cindex ITM @cindex ITM
@cindex ETM @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})) @ (@option{sync @var{port_width}} | ((@option{manchester} | @option{uart}) @var{formatter_enable})) @
@var{TRACECLKIN_freq} [@var{trace_freq}])) @var{TRACECLKIN_freq} [@var{trace_freq}]))
@ -9226,23 +9226,28 @@ Command options:
@itemize @minus @itemize @minus
@item @option{disable} disable TPIU handling; @item @option{disable} disable TPIU handling;
@item @option{external} configure TPIU to let user capture trace @item @option{external} configure TPIU to let user capture trace
output externally (with an additional UART or logic analyzer hardware); output externally (with an additional UART or logic analyzer hardware).
@item @option{internal @var{filename}} configure TPIU and debug adapter to @item @option{internal (@var{filename} | @var{:port} | -)} configure TPIU and debug adapter to
gather trace data and append it to @var{filename} (which can be gather trace data then:
either a regular file or a named pipe);
@item @option{internal -} configure TPIU and debug adapter to @itemize @minus
gather trace data, but not write to any file. Useful in conjunction with the @command{tcl_trace} command; @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 @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 @item @option{manchester} use asynchronous SWO mode with Manchester
coding; coding.
@item @option{uart} use asynchronous SWO mode with NRZ (same as @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 @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 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 @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 @item @var{trace_freq} trace port frequency. Can be omitted in
internal mode to let the adapter driver select the maximum supported internal mode to let the adapter driver select the maximum supported
rate automatically. rate automatically.

View File

@ -40,13 +40,43 @@ static int armv7m_poll_trace(void *target)
target_call_trace_callbacks(target, size, buf); target_call_trace_callbacks(target, size, buf);
if (armv7m->trace_config.trace_file != NULL) { switch (armv7m->trace_config.internal_channel) {
if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size) case TRACE_INTERNAL_CHANNEL_FILE:
fflush(armv7m->trace_config.trace_file); if (armv7m->trace_config.trace_file != NULL) {
else { if (fwrite(buf, 1, size, armv7m->trace_config.trace_file) == size)
LOG_ERROR("Error writing to the trace destination file"); fflush(armv7m->trace_config.trace_file);
return ERROR_FAIL; 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; return ERROR_OK;
@ -152,11 +182,56 @@ int armv7m_trace_itm_config(struct target *target)
return ERROR_OK; 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) switch (armv7m->trace_config.internal_channel) {
fclose(armv7m->trace_config.trace_file); case TRACE_INTERNAL_CHANNEL_FILE:
armv7m->trace_config.trace_file = NULL; 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) COMMAND_HANDLER(handle_tpiu_config_command)
@ -170,7 +245,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
return ERROR_COMMAND_SYNTAX_ERROR; return ERROR_COMMAND_SYNTAX_ERROR;
if (!strcmp(CMD_ARGV[cmd_idx], "disable")) { if (!strcmp(CMD_ARGV[cmd_idx], "disable")) {
if (CMD_ARGC == cmd_idx + 1) { if (CMD_ARGC == cmd_idx + 1) {
close_trace_file(armv7m); close_trace_channel(armv7m);
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED; armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_DISABLED;
if (CMD_CTX->mode == COMMAND_EXEC) if (CMD_CTX->mode == COMMAND_EXEC)
@ -180,7 +255,7 @@ COMMAND_HANDLER(handle_tpiu_config_command)
} }
} else if (!strcmp(CMD_ARGV[cmd_idx], "external") || } else if (!strcmp(CMD_ARGV[cmd_idx], "external") ||
!strcmp(CMD_ARGV[cmd_idx], "internal")) { !strcmp(CMD_ARGV[cmd_idx], "internal")) {
close_trace_file(armv7m); close_trace_channel(armv7m);
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL; armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_EXTERNAL;
if (!strcmp(CMD_ARGV[cmd_idx], "internal")) { if (!strcmp(CMD_ARGV[cmd_idx], "internal")) {
@ -189,12 +264,26 @@ COMMAND_HANDLER(handle_tpiu_config_command)
return ERROR_COMMAND_SYNTAX_ERROR; return ERROR_COMMAND_SYNTAX_ERROR;
armv7m->trace_config.config_type = TRACE_CONFIG_TYPE_INTERNAL; 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) { if (strcmp(CMD_ARGV[cmd_idx], "-") != 0) {
armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab"); if (CMD_ARGV[cmd_idx][0] == ':') {
if (!armv7m->trace_config.trace_file) { armv7m->trace_config.internal_channel = TRACE_INTERNAL_CHANNEL_TCP;
LOG_ERROR("Can't open trace destination file");
return ERROR_FAIL; 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, .mode = COMMAND_ANY,
.help = "Configure TPIU features", .help = "Configure TPIU features",
.usage = "(disable | " .usage = "(disable | "
"((external | internal <filename>) " "((external | internal (<filename> | <:port> | -)) "
"(sync <port width> | ((manchester | uart) <formatter enable>)) " "(sync <port width> | ((manchester | uart) <formatter enable>)) "
"<TRACECLKIN freq> [<trace freq>]))", "<TRACECLKIN freq> [<trace freq>]))",
}, },

View File

@ -18,6 +18,7 @@
#ifndef OPENOCD_TARGET_ARMV7M_TRACE_H #ifndef OPENOCD_TARGET_ARMV7M_TRACE_H
#define OPENOCD_TARGET_ARMV7M_TRACE_H #define OPENOCD_TARGET_ARMV7M_TRACE_H
#include <server/server.h>
#include <target/target.h> #include <target/target.h>
#include <command.h> #include <command.h>
@ -32,8 +33,14 @@ enum trace_config_type {
TRACE_CONFIG_TYPE_INTERNAL /**< trace output is handled by OpenOCD adapter driver */ 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 { 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_MANCHESTER, /**< asynchronous output with Manchester coding */
TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */ TPIU_PIN_PROTOCOL_ASYNC_UART /**< asynchronous output with NRZ coding */
}; };
@ -49,6 +56,9 @@ struct armv7m_trace_config {
/** Currently active trace capture mode */ /** Currently active trace capture mode */
enum trace_config_type config_type; 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 */ /** Currently active trace output mode */
enum tpiu_pin_protocol pin_protocol; enum tpiu_pin_protocol pin_protocol;
/** TPIU formatter enable/disable (in async mode) */ /** TPIU formatter enable/disable (in async mode) */
@ -73,8 +83,10 @@ struct armv7m_trace_config {
unsigned int traceclkin_freq; unsigned int traceclkin_freq;
/** Current frequency of trace port */ /** Current frequency of trace port */
unsigned int trace_freq; 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; 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[]; extern const struct command_registration armv7m_trace_command_handlers[];