server: tcl_trace command

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>
This commit is contained in:
Austin Morton 2015-03-09 05:34:52 -04:00 committed by Paul Fertser
parent 85903156d7
commit d28ab08cfa
5 changed files with 165 additions and 12 deletions

View File

@ -7570,7 +7570,7 @@ Defaulting to 0.
@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}]))
@ -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 -} 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
@ -8699,6 +8701,28 @@ Defaults to off.
@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

View File

@ -24,6 +24,7 @@
#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)
@ -35,6 +36,7 @@ struct tcl_connection {
int tc_outerror;/* flag an output error */
enum target_state tc_laststate;
bool tc_notify;
bool tc_trace;
};
static char *tcl_port;
@ -87,6 +89,32 @@ static int tcl_target_callback_reset_handler(struct target *target,
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
@ -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_trace_callback(tcl_target_callback_trace_handler, connection);
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';
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)
@ -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_trace_callback(tcl_target_callback_trace_handler, connection);
return ERROR_OK;
}
@ -240,7 +270,24 @@ COMMAND_HANDLER(handle_tcl_notifications_command)
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;
@ -264,6 +311,13 @@ static const struct command_registration tcl_command_handlers[] = {
.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
};

View File

@ -35,11 +35,15 @@ static int armv7m_poll_trace(void *target)
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;
@ -183,10 +187,13 @@ COMMAND_HANDLER(handle_tpiu_config_command)
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++;

View 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);
LIST_HEAD(target_trace_callback_list);
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;
}
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;
@ -1427,6 +1450,25 @@ int target_unregister_reset_callback(int (*callback)(struct target *target,
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)
@ -1480,6 +1522,16 @@ int target_call_reset_callbacks(struct target *target, enum target_reset_mode re
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)
{

View File

@ -292,6 +292,12 @@ struct target_reset_callback {
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;
@ -323,6 +329,15 @@ int target_unregister_reset_callback(
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
@ -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_call_trace_callbacks(struct target *target, size_t len, uint8_t *data);
/**
* The period is very approximate, the callback can happen much more often