This is an automated email from Gerrit. Austin Morton (austinpmor...@gmail.com) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/2588
-- gerrit commit a8c7dd5dff5ea1f778e024893102563066e38976 Author: Austin Morton <austinpmor...@gmail.com> Date: Mon Mar 9 05:34:52 2015 -0400 server: tcl_trace command Implements async target trace output to the tcl server Change-Id: I0178f6404447337d523782a1d2c317457030da40 Signed-off-by: Austin Morton <austinpmor...@gmail.com> diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index d6828f1..f152a17 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -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; } @@ -247,6 +277,23 @@ COMMAND_HANDLER(handle_tcl_notifications_command) } } +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 is"); + } else { + LOG_ERROR("%s: can only be called from the tcl server", CMD_NAME); + return ERROR_COMMAND_SYNTAX_ERROR; + } +} + static const struct command_registration tcl_command_handlers[] = { { .name = "tcl_port", @@ -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 }; diff --git a/src/target/armv7m_trace.c b/src/target/armv7m_trace.c index eb07a6e..3592bad 100644 --- a/src/target/armv7m_trace.c +++ b/src/target/armv7m_trace.c @@ -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++; diff --git a/src/target/target.c b/src/target/target.c index a8d3cba..87996b4 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -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[] = { @@ -1358,6 +1359,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; @@ -1435,6 +1458,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) @@ -1488,6 +1530,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) { diff --git a/src/target/target.h b/src/target/target.h index 9382720..18c1402 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -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 -- ------------------------------------------------------------------------------ Dive into the World of Parallel Programming The Go Parallel Website, sponsored by Intel and developed in partnership with Slashdot Media, is your hub for all things parallel software development, from weekly thought leadership blogs to news, videos, case studies, tutorials and more. Take a look and join the conversation now. http://goparallel.sourceforge.net/ _______________________________________________ OpenOCD-devel mailing list OpenOCD-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openocd-devel