From a0fecd6c41a6f2c965352efb0df70a149b7b260c Mon Sep 17 00:00:00 2001 From: Erhan Kurubas Date: Fri, 14 Apr 2023 00:07:18 +0200 Subject: target/espressif: add system level tracing feature Produces traces compatible with SEGGER SystemView tool. Signed-off-by: Erhan Kurubas Change-Id: If1057309edbb91ed2cf1ebf9137c378d3deb9b88 Reviewed-on: https://review.openocd.org/c/openocd/+/7606 Tested-by: jenkins Reviewed-by: Antonio Borneo --- doc/openocd.texi | 34 +++ src/target/espressif/Makefile.am | 3 + src/target/espressif/esp32_apptrace.c | 297 ++++++++++++++++-- src/target/espressif/esp32_sysview.c | 554 ++++++++++++++++++++++++++++++++++ src/target/espressif/esp32_sysview.h | 38 +++ src/target/espressif/segger_sysview.h | 79 +++++ 6 files changed, 988 insertions(+), 17 deletions(-) create mode 100644 src/target/espressif/esp32_sysview.c create mode 100644 src/target/espressif/esp32_sysview.h create mode 100644 src/target/espressif/segger_sysview.h diff --git a/doc/openocd.texi b/doc/openocd.texi index d0bc3f9..9b485c5 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -11271,6 +11271,40 @@ w/o OpenOCD command and keeps only the latest data window which fit into the buf Data will be stored to specified destination. @end deffn +@deffn {Command} {esp sysview} (start file:// [file://] [ [ [ [ []]]]]) +Starts @uref{https://www.segger.com/products/development-tools/systemview/, SEGGER SystemView} +compatible tracing. Data will be stored to specified destination. +For dual-core chips traces from every core will be saved to separate files. +Resulting files can be open in "SEGGER SystemView" application. +@url{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#openocd-systemview-tracing-command-options} +The meaning of the arguments is identical to @command{esp apptrace start}. +@end deffn + +@deffn {Command} {esp sysview} (stop) +Stops SystremView compatible tracing started with above command. +@url{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#openocd-systemview-tracing-command-options} +@end deffn + +@deffn {Command} {esp sysview} (status) +Requests ongoing SystremView compatible tracing status. +@url{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#openocd-systemview-tracing-command-options} +@end deffn + +@deffn {Command} {esp sysview_mcore} (start file:// [ [ [ [ []]]]]) +This command is identical to @command{esp sysview start}, but uses Espressif multi-core extension to +@uref{https://www.segger.com/products/development-tools/systemview/, SEGGER SystemView} data format. +Data will be stored to specified destination. Tracing data from all cores are saved in the same file. +The meaning of the arguments is identical to @command{esp sysview start}. +@end deffn + +@deffn {Command} {esp sysview_mcore} (stop) +Stops Espressif multi-core SystremView tracing started with above command. +@end deffn + +@deffn {Command} {esp sysview_mcore} (status) +Requests ongoing Espressif multi-core SystremView tracing status. +@end deffn + @anchor{softwaredebugmessagesandtracing} @section Software Debug Messages and Tracing @cindex Linux-ARM DCC support diff --git a/src/target/espressif/Makefile.am b/src/target/espressif/Makefile.am index c1759ed..14625d4 100644 --- a/src/target/espressif/Makefile.am +++ b/src/target/espressif/Makefile.am @@ -15,5 +15,8 @@ noinst_LTLIBRARIES += %D%/libespressif.la %D%/esp32.c \ %D%/esp32s2.c \ %D%/esp32s3.c \ + %D%/esp32_sysview.c \ + %D%/esp32_sysview.h \ + %D%/segger_sysview.h \ %D%/esp_semihosting.c \ %D%/esp_semihosting.h diff --git a/src/target/espressif/esp32_apptrace.c b/src/target/espressif/esp32_apptrace.c index 291503e..0c510ff 100644 --- a/src/target/espressif/esp32_apptrace.c +++ b/src/target/espressif/esp32_apptrace.c @@ -32,6 +32,8 @@ #include "esp_xtensa_smp.h" #include "esp_xtensa_apptrace.h" #include "esp32_apptrace.h" +#include "esp32_sysview.h" +#include "segger_sysview.h" #define ESP32_APPTRACE_USER_BLOCK_CORE(_v_) ((_v_) >> 15) #define ESP32_APPTRACE_USER_BLOCK_LEN(_v_) ((_v_) & ~BIT(15)) @@ -82,6 +84,8 @@ static int esp32_apptrace_safe_halt_targets(struct esp32_apptrace_cmd_ctx *ctx, static struct esp32_apptrace_block *esp32_apptrace_free_block_get(struct esp32_apptrace_cmd_ctx *ctx); static int esp32_apptrace_handle_trace_block(struct esp32_apptrace_cmd_ctx *ctx, struct esp32_apptrace_block *block); +static int esp32_sysview_start(struct esp32_apptrace_cmd_ctx *ctx); +static int esp32_sysview_stop(struct esp32_apptrace_cmd_ctx *ctx); static const bool s_time_stats_enable = true; @@ -1118,10 +1122,7 @@ static int esp32_apptrace_poll(void *priv) return ERROR_FAIL; } } - res = - ctx->hw->data_read(ctx->cpus[fired_target_num], - target_state[fired_target_num].data_len, - block->data, + res = ctx->hw->data_read(ctx->cpus[fired_target_num], target_state[fired_target_num].data_len, block->data, target_state[fired_target_num].block_id, /* do not ack target data in sync mode, esp32_apptrace_handle_trace_block() can write response data and will do ack thereafter */ @@ -1215,6 +1216,11 @@ static int esp32_apptrace_poll(void *priv) return ERROR_OK; } +static inline bool is_sysview_mode(int mode) +{ + return mode == ESP_APPTRACE_CMD_MODE_SYSVIEW || mode == ESP_APPTRACE_CMD_MODE_SYSVIEW_MCORE; +} + static void esp32_apptrace_cmd_stop(struct esp32_apptrace_cmd_ctx *ctx) { if (duration_measure(&ctx->read_time) != 0) @@ -1222,7 +1228,12 @@ static void esp32_apptrace_cmd_stop(struct esp32_apptrace_cmd_ctx *ctx) int res = target_unregister_timer_callback(esp32_apptrace_poll, ctx); if (res != ERROR_OK) LOG_ERROR("Failed to unregister target timer handler (%d)!", res); - + if (is_sysview_mode(ctx->mode)) { + /* stop tracing */ + res = esp32_sysview_stop(ctx); + if (res != ERROR_OK) + LOG_ERROR("sysview: Failed to stop tracing!"); + } /* data processor is alive, so wait for all received blocks to be processed */ res = esp32_apptrace_wait_tracing_finished(ctx); if (res != ERROR_OK) @@ -1236,6 +1247,190 @@ static void esp32_apptrace_cmd_stop(struct esp32_apptrace_cmd_ctx *ctx) LOG_ERROR("Failed to cleanup cmd ctx (%d)!", res); } +/* this function must be called after connecting to targets */ +static int esp32_sysview_start(struct esp32_apptrace_cmd_ctx *ctx) +{ + uint8_t cmds[] = { SEGGER_SYSVIEW_COMMAND_ID_START }; + uint32_t fired_target_num = 0; + struct esp32_apptrace_target_state target_state[ESP32_APPTRACE_MAX_CORES_NUM]; + struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv; + + /* get current block id */ + int res = esp32_apptrace_get_data_info(ctx, target_state, &fired_target_num); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to read target data info!"); + return res; + } + if (fired_target_num == UINT32_MAX) { + /* it can happen that there is no pending target data, but block was switched + * in this case block_ids on both CPUs are equal, so select the first one */ + fired_target_num = 0; + } + /* start tracing */ + res = esp_apptrace_usr_block_write(ctx->hw, ctx->cpus[fired_target_num], target_state[fired_target_num].block_id, + cmds, sizeof(cmds)); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to start tracing!"); + return res; + } + cmd_data->sv_trace_running = 1; + return res; +} + +static int esp32_sysview_stop(struct esp32_apptrace_cmd_ctx *ctx) +{ + uint32_t old_block_id, fired_target_num = 0, empty_target_num = 0; + struct esp32_apptrace_target_state target_state[ESP32_APPTRACE_MAX_CORES_NUM]; + struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv; + uint8_t cmds[] = { SEGGER_SYSVIEW_COMMAND_ID_STOP }; + struct duration wait_time; + + struct esp32_apptrace_block *block = esp32_apptrace_free_block_get(ctx); + if (!block) { + LOG_ERROR("Failed to get free block for data on (%s)!", target_name(ctx->cpus[fired_target_num])); + return ERROR_FAIL; + } + + /* halt all CPUs (not only one), otherwise it can happen that there is no target data and + * while we are queueing commands another CPU switches tracing block */ + int res = esp32_apptrace_safe_halt_targets(ctx, target_state); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to halt targets (%d)!", res); + return res; + } + /* it can happen that there is no pending target data + * in this case block_ids on both CPUs are equal, so the first one will be selected */ + for (unsigned int k = 0; k < ctx->cores_num; k++) { + if (target_state[k].data_len) { + fired_target_num = k; + break; + } + } + if (target_state[fired_target_num].data_len) { + /* read pending data without ack, they will be acked when stop command is queued */ + res = ctx->hw->data_read(ctx->cpus[fired_target_num], target_state[fired_target_num].data_len, block->data, + target_state[fired_target_num].block_id, + false /*no ack target data*/); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to read data on (%s)!", target_name(ctx->cpus[fired_target_num])); + return res; + } + /* process data */ + block->data_len = target_state[fired_target_num].data_len; + res = esp32_apptrace_handle_trace_block(ctx, block); + if (res != ERROR_OK) { + LOG_ERROR("Failed to process trace block %" PRId32 " bytes!", block->data_len); + return res; + } + } + /* stop tracing and ack target data */ + res = esp_apptrace_usr_block_write(ctx->hw, ctx->cpus[fired_target_num], target_state[fired_target_num].block_id, + cmds, + sizeof(cmds)); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to stop tracing!"); + return res; + } + if (ctx->cores_num > 1) { + empty_target_num = fired_target_num ? 0 : 1; + /* ack target data on another CPU */ + res = ctx->hw->ctrl_reg_write(ctx->cpus[empty_target_num], target_state[fired_target_num].block_id, + 0 /*target data ack*/, + true /*host connected*/, + false /*no host data*/); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to ack data on target '%s' (%d)!", + target_name(ctx->cpus[empty_target_num]), res); + return res; + } + } + /* resume targets to allow command processing */ + LOG_INFO("Resume targets"); + bool smp_resumed = false; + for (unsigned int k = 0; k < ctx->cores_num; k++) { + if (smp_resumed && ctx->cpus[k]->smp) { + /* in SMP mode we need to call target_resume for one core only */ + continue; + } + res = target_resume(ctx->cpus[k], 1, 0, 1, 0); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to resume target '%s' (%d)!", target_name(ctx->cpus[k]), res); + return res; + } + if (ctx->cpus[k]->smp) + smp_resumed = true; + } + /* wait for block switch (command sent), so we can disconnect from targets */ + old_block_id = target_state[fired_target_num].block_id; + if (duration_start(&wait_time) != 0) { + LOG_ERROR("Failed to start trace stop timeout measurement!"); + return ERROR_FAIL; + } + /* we are waiting for the last data from tracing block and also there can be data in the pended + * data buffer */ + /* so we are expecting two TRX block switches at most or stopping due to timeout */ + while (cmd_data->sv_trace_running) { + res = esp32_apptrace_get_data_info(ctx, target_state, &fired_target_num); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to read targets data info!"); + return res; + } + if (fired_target_num == UINT32_MAX) { + /* it can happen that there is no pending (last) target data, but block was + * switched */ + /* in this case block_ids on both CPUs are equal, so select the first one */ + fired_target_num = 0; + } + if (target_state[fired_target_num].block_id != old_block_id) { + if (target_state[fired_target_num].data_len) { + /* read last data and ack them */ + res = ctx->hw->data_read(ctx->cpus[fired_target_num], + target_state[fired_target_num].data_len, + block->data, + target_state[fired_target_num].block_id, + true /*ack target data*/); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to read last data on (%s)!", target_name(ctx->cpus[fired_target_num])); + } else { + if (ctx->cores_num > 1) { + /* ack target data on another CPU */ + empty_target_num = fired_target_num ? 0 : 1; + res = ctx->hw->ctrl_reg_write(ctx->cpus[empty_target_num], + target_state[fired_target_num].block_id, + 0 /*all read*/, + true /*host connected*/, + false /*no host data*/); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to ack data on target '%s' (%d)!", + target_name(ctx->cpus[empty_target_num]), res); + return res; + } + } + /* process data */ + block->data_len = target_state[fired_target_num].data_len; + res = esp32_apptrace_handle_trace_block(ctx, block); + if (res != ERROR_OK) { + LOG_ERROR("Failed to process trace block %" PRId32 " bytes!", + block->data_len); + return res; + } + } + old_block_id = target_state[fired_target_num].block_id; + } + } + if (duration_measure(&wait_time) != 0) { + LOG_ERROR("Failed to start trace stop timeout measurement!"); + return ERROR_FAIL; + } + const float stop_tmo = LOG_LEVEL_IS(LOG_LVL_DEBUG) ? 30.0 : 0.5; + if (duration_elapsed(&wait_time) >= stop_tmo) { + LOG_INFO("Stop waiting for the last data due to timeout."); + break; + } + } + return res; +} + int esp32_cmd_apptrace_generic(struct command_invocation *cmd, int mode, const char **argv, int argc) { static struct esp32_apptrace_cmd_ctx s_at_cmd_ctx; @@ -1264,17 +1459,39 @@ int esp32_cmd_apptrace_generic(struct command_invocation *cmd, int mode, const c old_state = target->state; if (strcmp(argv[0], "start") == 0) { - res = esp32_apptrace_cmd_init(&s_at_cmd_ctx, - cmd, - mode, - &argv[1], - argc - 1); - if (res != ERROR_OK) { - command_print(cmd, "Failed to init cmd ctx (%d)!", res); - return res; + if (is_sysview_mode(mode)) { + /* init cmd context */ + res = esp32_sysview_cmd_init(&s_at_cmd_ctx, + cmd, + mode, + mode == ESP_APPTRACE_CMD_MODE_SYSVIEW_MCORE, + &argv[1], + argc - 1); + if (res != ERROR_OK) { + command_print(cmd, "Failed to init cmd ctx (%d)!", res); + return res; + } + cmd_data = s_at_cmd_ctx.cmd_priv; + if (cmd_data->skip_len != 0) { + s_at_cmd_ctx.running = 0; + esp32_sysview_cmd_cleanup(&s_at_cmd_ctx); + command_print(cmd, "Data skipping not supported!"); + return ERROR_FAIL; + } + s_at_cmd_ctx.process_data = esp32_sysview_process_data; + } else { + res = esp32_apptrace_cmd_init(&s_at_cmd_ctx, + cmd, + mode, + &argv[1], + argc - 1); + if (res != ERROR_OK) { + command_print(cmd, "Failed to init cmd ctx (%d)!", res); + return res; + } + cmd_data = s_at_cmd_ctx.cmd_priv; + s_at_cmd_ctx.process_data = esp32_apptrace_process_data; } - cmd_data = s_at_cmd_ctx.cmd_priv; - s_at_cmd_ctx.process_data = esp32_apptrace_process_data; s_at_cmd_ctx.auto_clean = esp32_apptrace_cmd_stop; if (cmd_data->wait4halt) { res = esp32_apptrace_wait4halt(&s_at_cmd_ctx, target); @@ -1288,6 +1505,17 @@ int esp32_cmd_apptrace_generic(struct command_invocation *cmd, int mode, const c command_print(cmd, "Failed to connect to targets (%d)!", res); goto _on_start_error; } + if (is_sysview_mode(mode)) { + /* start tracing */ + res = esp32_sysview_start(&s_at_cmd_ctx); + if (res != ERROR_OK) { + esp32_apptrace_connect_targets(&s_at_cmd_ctx, false, old_state == TARGET_RUNNING); + s_at_cmd_ctx.running = 0; + esp32_apptrace_cmd_cleanup(&s_at_cmd_ctx); + command_print(cmd, "sysview: Failed to start tracing!"); + return res; + } + } res = target_register_timer_callback(esp32_apptrace_poll, cmd_data->poll_period, TARGET_TIMER_TYPE_PERIODIC, @@ -1309,6 +1537,10 @@ int esp32_cmd_apptrace_generic(struct command_invocation *cmd, int mode, const c esp32_apptrace_print_stats(&s_at_cmd_ctx); return ERROR_OK; } else if (strcmp(argv[0], "dump") == 0) { + if (is_sysview_mode(mode)) { + command_print(cmd, "Not supported!"); + return ERROR_FAIL; + } /* [dump outfile] - post-mortem dump without connection to targets */ res = esp32_apptrace_cmd_init(&s_at_cmd_ctx, cmd, @@ -1349,7 +1581,10 @@ int esp32_cmd_apptrace_generic(struct command_invocation *cmd, int mode, const c _on_start_error: s_at_cmd_ctx.running = 0; - esp32_apptrace_cmd_cleanup(&s_at_cmd_ctx); + if (is_sysview_mode(mode)) + esp32_sysview_cmd_cleanup(&s_at_cmd_ctx); + else + esp32_apptrace_cmd_cleanup(&s_at_cmd_ctx); return res; } @@ -1358,6 +1593,16 @@ COMMAND_HANDLER(esp32_cmd_apptrace) return esp32_cmd_apptrace_generic(CMD, ESP_APPTRACE_CMD_MODE_GEN, CMD_ARGV, CMD_ARGC); } +COMMAND_HANDLER(esp32_cmd_sysview) +{ + return esp32_cmd_apptrace_generic(CMD, ESP_APPTRACE_CMD_MODE_SYSVIEW, CMD_ARGV, CMD_ARGC); +} + +COMMAND_HANDLER(esp32_cmd_sysview_mcore) +{ + return esp32_cmd_apptrace_generic(CMD, ESP_APPTRACE_CMD_MODE_SYSVIEW_MCORE, CMD_ARGV, CMD_ARGC); +} + const struct command_registration esp32_apptrace_command_handlers[] = { { .name = "apptrace", @@ -1366,7 +1611,25 @@ const struct command_registration esp32_apptrace_command_handlers[] = { .help = "App Tracing: application level trace control. Starts, stops or queries tracing process status.", .usage = - "[start [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]] | [stop] | [status] | [dump ]", + "(start [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]) | (stop) | (status) | (dump )", + }, + { + .name = "sysview", + .handler = esp32_cmd_sysview, + .mode = COMMAND_EXEC, + .help = + "App Tracing: SEGGER SystemView compatible trace control. Starts, stops or queries tracing process status.", + .usage = + "(start file:// [file://] [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]) | (stop) | (status)", + }, + { + .name = "sysview_mcore", + .handler = esp32_cmd_sysview_mcore, + .mode = COMMAND_EXEC, + .help = + "App Tracing: Espressif multi-core SystemView trace control. Starts, stops or queries tracing process status.", + .usage = + "(start file:// [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]) | (stop) | (status)", }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/espressif/esp32_sysview.c b/src/target/espressif/esp32_sysview.c new file mode 100644 index 0000000..2fe2157 --- /dev/null +++ b/src/target/espressif/esp32_sysview.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * ESP32 sysview tracing module * + * Copyright (C) 2020 Espressif Systems Ltd. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "esp32_apptrace.h" +#include "esp32_sysview.h" +#include "segger_sysview.h" + +/* in SystemView mode core ID is passed in event ID field */ +#define ESP32_SYSVIEW_USER_BLOCK_CORE(_v_) (0) /* not used */ +#define ESP32_SYSVIEW_USER_BLOCK_LEN(_v_) (_v_) +#define ESP32_SYSVIEW_USER_BLOCK_HDR_SZ 2 + +struct esp_sysview_target2host_hdr { + uint8_t block_sz; + uint8_t wr_sz; +}; +#define SYSVIEW_BLOCK_SIZE_OFFSET 0 +#define SYSVIEW_WR_SIZE_OFFSET 1 + +static int esp_sysview_trace_header_write(struct esp32_apptrace_cmd_ctx *ctx, bool mcore_format); +static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf); +static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len); + +int esp32_sysview_cmd_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, + struct command_invocation *cmd, + int mode, + bool mcore_format, + const char **argv, + int argc) +{ + struct esp32_sysview_cmd_data *cmd_data; + + if (argc < 1) { + command_print(cmd, "Not enough args! Need trace data destination!"); + return ERROR_FAIL; + } + + int res = esp32_apptrace_cmd_ctx_init(cmd_ctx, cmd, mode); + if (res != ERROR_OK) + return res; + + int core_num = cmd_ctx->cores_num; + + if (!mcore_format && argc < core_num) { + command_print(cmd, "Not enough args! Need %d trace data destinations!", core_num); + res = ERROR_FAIL; + goto on_error; + } + + cmd_data = calloc(1, sizeof(*cmd_data)); + if (!cmd_data) { + command_print(cmd, "No memory for command data!"); + res = ERROR_FAIL; + goto on_error; + } + cmd_ctx->cmd_priv = cmd_data; + cmd_data->mcore_format = mcore_format; + + /*outfile1 [outfile2] [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]] */ + int dests_num = esp32_apptrace_dest_init(cmd_data->data_dests, argv, !mcore_format ? core_num : 1); + if (!mcore_format && dests_num < core_num) { + command_print(cmd, "Not enough args! Need %d trace data destinations!", core_num); + free(cmd_data); + res = ERROR_FAIL; + goto on_error; + } + cmd_data->apptrace.max_len = UINT32_MAX; + cmd_data->apptrace.poll_period = 0 /*ms*/; + cmd_ctx->stop_tmo = -1.0; /* infinite */ + if (argc > dests_num) { + /* parse remaining args */ + esp32_apptrace_cmd_args_parse(cmd_ctx, + &cmd_data->apptrace, + &argv[dests_num], + argc - dests_num); + } + LOG_USER("App trace params: from %d cores, size %u bytes, stop_tmo %g s, " + "poll period %u ms, wait_rst %d, skip %u bytes", + cmd_ctx->cores_num, + cmd_data->apptrace.max_len, + cmd_ctx->stop_tmo, + cmd_data->apptrace.poll_period, + cmd_data->apptrace.wait4halt, + cmd_data->apptrace.skip_len); + + cmd_ctx->trace_format.hdr_sz = ESP32_SYSVIEW_USER_BLOCK_HDR_SZ; + cmd_ctx->trace_format.core_id_get = esp32_sysview_core_id_get; + cmd_ctx->trace_format.usr_block_len_get = esp32_sysview_usr_block_len_get; + + res = esp_sysview_trace_header_write(cmd_ctx, mcore_format); + if (res != ERROR_OK) { + command_print(cmd, "Failed to write trace header (%d)!", res); + esp32_apptrace_dest_cleanup(cmd_data->data_dests, core_num); + free(cmd_data); + return res; + } + return ERROR_OK; +on_error: + cmd_ctx->running = 0; + esp32_apptrace_cmd_ctx_cleanup(cmd_ctx); + return res; +} + +int esp32_sysview_cmd_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx) +{ + struct esp32_sysview_cmd_data *cmd_data = cmd_ctx->cmd_priv; + + esp32_apptrace_dest_cleanup(cmd_data->data_dests, cmd_ctx->cores_num); + free(cmd_data); + cmd_ctx->cmd_priv = NULL; + esp32_apptrace_cmd_ctx_cleanup(cmd_ctx); + return ERROR_OK; +} + +static int esp32_sysview_core_id_get(struct target *target, uint8_t *hdr_buf) +{ + /* for sysview compressed apptrace header is used, so core id is encoded in sysview packet */ + return 0; +} + +static uint32_t esp32_sysview_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len) +{ + *wr_len = ESP32_SYSVIEW_USER_BLOCK_LEN(hdr_buf[SYSVIEW_WR_SIZE_OFFSET]); + return ESP32_SYSVIEW_USER_BLOCK_LEN(hdr_buf[SYSVIEW_BLOCK_SIZE_OFFSET]); +} + +static int esp_sysview_trace_header_write(struct esp32_apptrace_cmd_ctx *ctx, bool mcore_format) +{ + struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv; + char *hdr_str; + int dests_num; + + if (!mcore_format) { + hdr_str = ";\n" + "; Version " SYSVIEW_MIN_VER_STRING "\n" + "; Author Espressif Inc\n" + ";\n"; + dests_num = ctx->cores_num; + } else { + hdr_str = ";\n" + "; Version " SYSVIEW_MIN_VER_STRING "\n" + "; Author Espressif Inc\n" + "; ESP_Extension\n" + ";\n"; + dests_num = 1; + } + + int hdr_len = strlen(hdr_str); + for (int i = 0; i < dests_num; i++) { + int res = cmd_data->data_dests[i].write(cmd_data->data_dests[i].priv, + (uint8_t *)hdr_str, + hdr_len); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", hdr_len, i); + return ERROR_FAIL; + } + } + return ERROR_OK; +} + +static void sysview_encode_u32(uint8_t **dest, uint32_t val) +{ + uint8_t *sv_ptr = *dest; + while (val > 0x7F) { + *sv_ptr++ = (uint8_t)(val | 0x80); + val >>= 7; + } + *sv_ptr++ = (uint8_t)val; + *dest = sv_ptr; +} + +static uint32_t esp_sysview_decode_u32(uint8_t **ptr) +{ + uint32_t val = 0; + for (int k = 0;; k++, (*ptr)++) { + if (**ptr & 0x80) { + val |= (uint32_t)(**ptr & ~0x80) << 7 * k; + } else { + val |= (uint32_t)**ptr << 7 * k; + (*ptr)++; + break; + } + } + return val; +} + +static uint16_t esp_sysview_decode_plen(uint8_t **ptr) +{ + uint16_t payload_len = 0; + uint8_t *p = *ptr; + /* here pkt points to encoded payload length */ + if (*p & 0x80) { + payload_len = *(p + 1); /* higher part */ + payload_len = (payload_len << 7) | (*p & ~0x80);/* lower 7 bits */ + p += 2; /* payload len (2 bytes) */ + } else { + payload_len = *p; + p++; /* payload len (1 byte) */ + } + *ptr = p; + + return payload_len; +} + +static uint16_t esp_sysview_get_predef_payload_len(uint16_t id, uint8_t *pkt) +{ + uint16_t len; + uint8_t *ptr = pkt; + + switch (id) { + case SYSVIEW_EVTID_OVERFLOW: + case SYSVIEW_EVTID_ISR_ENTER: + case SYSVIEW_EVTID_TASK_START_EXEC: + case SYSVIEW_EVTID_TASK_START_READY: + case SYSVIEW_EVTID_TASK_CREATE: + case SYSVIEW_EVTID_SYSTIME_CYCLES: + case SYSVIEW_EVTID_USER_START: + case SYSVIEW_EVTID_USER_STOP: + case SYSVIEW_EVTID_TIMER_ENTER: + /*ENCODE_U32 */ + esp_sysview_decode_u32(&ptr); + len = ptr - pkt; + break; + case SYSVIEW_EVTID_TASK_STOP_READY: + case SYSVIEW_EVTID_SYSTIME_US: + /*2*ENCODE_U32 */ + esp_sysview_decode_u32(&ptr); + esp_sysview_decode_u32(&ptr); + len = ptr - pkt; + break; + case SYSVIEW_EVTID_SYSDESC: + /*str(128 + 1) */ + len = *ptr + 1; + break; + case SYSVIEW_EVTID_TASK_INFO: + case SYSVIEW_EVTID_MODULEDESC: + /*2*ENCODE_U32 + str */ + esp_sysview_decode_u32(&ptr); + esp_sysview_decode_u32(&ptr); + /* TODO: add support for strings longer then 255 bytes */ + len = ptr - pkt + *ptr + 1; + break; + case SYSVIEW_EVTID_STACK_INFO: + /*4*ENCODE_U32 */ + esp_sysview_decode_u32(&ptr); + esp_sysview_decode_u32(&ptr); + esp_sysview_decode_u32(&ptr); + esp_sysview_decode_u32(&ptr); + len = ptr - pkt; + break; + case SYSVIEW_EVTID_ISR_EXIT: + case SYSVIEW_EVTID_TASK_STOP_EXEC: + case SYSVIEW_EVTID_TRACE_START: + case SYSVIEW_EVTID_TRACE_STOP: + case SYSVIEW_EVTID_IDLE: + case SYSVIEW_EVTID_ISR_TO_SCHEDULER: + case SYSVIEW_EVTID_TIMER_EXIT: + len = 0; + break; + + /*case SYSVIEW_EVTID_NOP: */ + default: + LOG_ERROR("sysview: Unsupported predef event %d!", id); + len = 0; + } + return len; +} + +static uint16_t esp_sysview_parse_packet(uint8_t *pkt_buf, + uint32_t *pkt_len, + unsigned int *pkt_core_id, + uint32_t *delta, + uint32_t *delta_len, + bool clear_core_bit) +{ + uint8_t *pkt = pkt_buf; + uint16_t event_id = 0, payload_len = 0; + + *pkt_core_id = 0; + *pkt_len = 0; + /* 1-2 byte of message type, 0-2 byte of payload length, payload, 1-5 bytes of timestamp. */ + if (*pkt & 0x80) { + if (*(pkt + 1) & (1 << 6)) { + if (clear_core_bit) + *(pkt + 1) &= ~(1 << 6); /* clear core_id bit */ + *pkt_core_id = 1; + } + event_id = *(pkt + 1) & ~(1 << 6); /* higher part */ + event_id = (event_id << 7) | (*pkt & ~0x80); /* lower 7 bits */ + pkt += 2; /* event_id (2 bytes) */ + /* here pkt points to encoded payload length */ + payload_len = esp_sysview_decode_plen(&pkt); + } else { + if (*pkt & (1 << 6)) { + if (clear_core_bit) + *pkt &= ~(1 << 6); /* clear core_id bit */ + *pkt_core_id = 1; + } + /* event_id (1 byte) */ + event_id = *pkt & ~(1 << 6); + pkt++; + if (event_id < 24) + payload_len = esp_sysview_get_predef_payload_len(event_id, pkt); + else + payload_len = esp_sysview_decode_plen(&pkt); + } + pkt += payload_len; + uint8_t *delta_start = pkt; + *delta = esp_sysview_decode_u32(&pkt); + *delta_len = pkt - delta_start; + *pkt_len = pkt - pkt_buf; + LOG_DEBUG("sysview: evt %d len %d plen %d dlen %d", + event_id, + *pkt_len, + payload_len, + *delta_len); + return event_id; +} + +static int esp32_sysview_write_packet(struct esp32_sysview_cmd_data *cmd_data, + int pkt_core_id, uint32_t pkt_len, uint8_t *pkt_buf, uint32_t delta_len, uint8_t *delta_buf) +{ + if (!cmd_data->data_dests[pkt_core_id].write) + return ERROR_FAIL; + + int res = cmd_data->data_dests[pkt_core_id].write(cmd_data->data_dests[pkt_core_id].priv, pkt_buf, pkt_len); + + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", pkt_len, pkt_core_id); + return res; + } + if (delta_len) { + /* write packet with modified delta */ + res = cmd_data->data_dests[pkt_core_id].write(cmd_data->data_dests[pkt_core_id].priv, delta_buf, delta_len); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to write %u bytes of delta to dest %d!", delta_len, pkt_core_id); + return res; + } + } + return ERROR_OK; +} + +static int esp32_sysview_process_packet(struct esp32_apptrace_cmd_ctx *ctx, + unsigned int pkt_core_id, uint16_t event_id, uint32_t delta, uint32_t delta_len, + uint32_t pkt_len, uint8_t *pkt_buf) +{ + struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv; + int pkt_core_changed = 0; + uint32_t new_delta_len = 0; + uint8_t new_delta_buf[10]; + uint32_t wr_len = pkt_len; + + if (ctx->cores_num > 1) { + if (cmd_data->sv_last_core_id == pkt_core_id) { + /* if this packet is for the same core as the prev one acc delta and write packet unmodified */ + cmd_data->sv_acc_time_delta += delta; + } else { + /* if this packet is for another core then prev one set acc delta to the packet's delta */ + uint8_t *delta_ptr = new_delta_buf; + sysview_encode_u32(&delta_ptr, delta + cmd_data->sv_acc_time_delta); + cmd_data->sv_acc_time_delta = delta; + wr_len -= delta_len; + new_delta_len = delta_ptr - new_delta_buf; + pkt_core_changed = 1; + } + cmd_data->sv_last_core_id = pkt_core_id; + } + if (pkt_core_id >= ctx->cores_num) { + LOG_WARNING("sysview: invalid core ID in packet %d, must be less then %d! Event id %d", + pkt_core_id, + ctx->cores_num, + event_id); + return ERROR_FAIL; + } + int res = esp32_sysview_write_packet(cmd_data, + pkt_core_id, + wr_len, + pkt_buf, + new_delta_len, + new_delta_buf); + if (res != ERROR_OK) + return res; + for (unsigned int i = 0; i < ctx->cores_num; i++) { + if (pkt_core_id == i) + continue; + switch (event_id) { + /* messages below should be sent to trace destinations for all cores */ + case SYSVIEW_EVTID_TRACE_START: + case SYSVIEW_EVTID_TRACE_STOP: + case SYSVIEW_EVTID_SYSTIME_CYCLES: + case SYSVIEW_EVTID_SYSTIME_US: + case SYSVIEW_EVTID_SYSDESC: + case SYSVIEW_EVTID_TASK_INFO: + case SYSVIEW_EVTID_STACK_INFO: + case SYSVIEW_EVTID_MODULEDESC: + case SYSVIEW_EVTID_INIT: + case SYSVIEW_EVTID_NUMMODULES: + case SYSVIEW_EVTID_OVERFLOW: + case SYSVIEW_EVTID_TASK_START_READY: + /* if packet's source core has changed */ + wr_len = pkt_len; + if (pkt_core_changed) { + /* clone packet with unmodified delta */ + new_delta_len = 0; + } else { + /* clone packet with modified delta */ + uint8_t *delta_ptr = new_delta_buf; + sysview_encode_u32(&delta_ptr, cmd_data->sv_acc_time_delta /*delta has been accumulated above*/); + wr_len -= delta_len; + new_delta_len = delta_ptr - new_delta_buf; + } + LOG_DEBUG("sysview: Redirect %d bytes of event %d to dest %d", wr_len, event_id, i); + res = esp32_sysview_write_packet(cmd_data, + i, + wr_len, + pkt_buf, + new_delta_len, + new_delta_buf); + if (res != ERROR_OK) + return res; + /* messages above are cloned to trace files for both cores, + * so reset acc time delta, both files have actual delta + * info */ + cmd_data->sv_acc_time_delta = 0; + break; + default: + break; + } + } + return ERROR_OK; +} + +int esp32_sysview_process_data(struct esp32_apptrace_cmd_ctx *ctx, + unsigned int core_id, + uint8_t *data, + uint32_t data_len) +{ + struct esp32_sysview_cmd_data *cmd_data = ctx->cmd_priv; + + LOG_DEBUG("sysview: Read from target %d bytes [%x %x %x %x]", + data_len, + data[0], + data[1], + data[2], + data[3]); + int res; + uint32_t processed = 0; + if (core_id >= ctx->cores_num) { + LOG_ERROR("sysview: Invalid core id %d in user block!", core_id); + return ERROR_FAIL; + } + if (cmd_data->mcore_format) + core_id = 0; + if (ctx->tot_len == 0) { + /* handle sync seq */ + if (data_len < SYSVIEW_SYNC_LEN) { + LOG_ERROR("sysview: Invalid init seq len %d!", data_len); + return ERROR_FAIL; + } + LOG_DEBUG("sysview: Process %d sync bytes", SYSVIEW_SYNC_LEN); + uint8_t sync_seq[SYSVIEW_SYNC_LEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + if (memcmp(data, sync_seq, SYSVIEW_SYNC_LEN) != 0) { + LOG_ERROR("sysview: Invalid init seq [%x %x %x %x %x %x %x %x %x %x]", + data[0], data[1], data[2], data[3], data[4], data[5], data[6], + data[7], data[8], data[9]); + return ERROR_FAIL; + } + res = cmd_data->data_dests[core_id].write(cmd_data->data_dests[core_id].priv, + data, + SYSVIEW_SYNC_LEN); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to write %u sync bytes to dest %d!", + SYSVIEW_SYNC_LEN, + core_id); + return res; + } + if (!cmd_data->mcore_format) { + for (unsigned int i = 0; i < ctx->cores_num; i++) { + if (core_id == i) + continue; + res = + cmd_data->data_dests[i].write(cmd_data->data_dests[i].priv, + data, + SYSVIEW_SYNC_LEN); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to write %u sync bytes to dest %d!", SYSVIEW_SYNC_LEN, core_id ? 0 : 1); + return res; + } + } + } + ctx->tot_len += SYSVIEW_SYNC_LEN; + processed += SYSVIEW_SYNC_LEN; + } + while (processed < data_len) { + unsigned int pkt_core_id; + uint32_t delta_len = 0; + uint32_t pkt_len = 0, delta = 0; + uint16_t event_id = esp_sysview_parse_packet(data + processed, + &pkt_len, + &pkt_core_id, + &delta, + &delta_len, + !cmd_data->mcore_format); + LOG_DEBUG("sysview: Process packet: core %d, %d id, %d bytes [%x %x %x %x]", + pkt_core_id, + event_id, + pkt_len, + data[processed + 0], + data[processed + 1], + data[processed + 2], + data[processed + 3]); + if (!cmd_data->mcore_format) { + res = esp32_sysview_process_packet(ctx, + pkt_core_id, + event_id, + delta, + delta_len, + pkt_len, + data + processed); + if (res != ERROR_OK) + return res; + } else { + res = cmd_data->data_dests[0].write(cmd_data->data_dests[0].priv, data + processed, pkt_len); + if (res != ERROR_OK) { + LOG_ERROR("sysview: Failed to write %u bytes to dest %d!", pkt_len, 0); + return res; + } + } + if (event_id == SYSVIEW_EVTID_TRACE_STOP) + cmd_data->sv_trace_running = 0; + ctx->tot_len += pkt_len; + processed += pkt_len; + } + LOG_USER("%u ", ctx->tot_len); + /* check for stop condition */ + if (ctx->tot_len > cmd_data->apptrace.skip_len && + (ctx->tot_len - cmd_data->apptrace.skip_len >= cmd_data->apptrace.max_len)) { + ctx->running = 0; + if (duration_measure(&ctx->read_time) != 0) { + LOG_ERROR("Failed to stop trace read time measure!"); + return ERROR_FAIL; + } + } + return ERROR_OK; +} diff --git a/src/target/espressif/esp32_sysview.h b/src/target/espressif/esp32_sysview.h new file mode 100644 index 0000000..230ce46 --- /dev/null +++ b/src/target/espressif/esp32_sysview.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * ESP32 sysview tracing module * + * Copyright (C) 2020 Espressif Systems Ltd. * + ***************************************************************************/ + +#ifndef OPENOCD_TARGET_ESP32_SYSVIEW_H +#define OPENOCD_TARGET_ESP32_SYSVIEW_H + +#include +#include "esp32_apptrace.h" + +struct esp32_sysview_cmd_data { + /* Should be the first field. Generic apptrace command handling code accesses it */ + struct esp32_apptrace_cmd_data apptrace; + struct esp32_apptrace_dest data_dests[ESP32_APPTRACE_MAX_CORES_NUM]; + bool mcore_format; + uint32_t sv_acc_time_delta; + unsigned int sv_last_core_id; + int sv_trace_running; +}; + +struct esp32_apptrace_cmd_ctx; + +int esp32_sysview_cmd_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, + struct command_invocation *cmd, + int mode, + bool mcore_format, + const char **argv, + int argc); +int esp32_sysview_cmd_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx); +int esp32_sysview_process_data(struct esp32_apptrace_cmd_ctx *ctx, + unsigned int core_id, + uint8_t *data, + uint32_t data_len); + +#endif /* OPENOCD_TARGET_ESP32_SYSVIEW_H */ diff --git a/src/target/espressif/segger_sysview.h b/src/target/espressif/segger_sysview.h new file mode 100644 index 0000000..d149cab --- /dev/null +++ b/src/target/espressif/segger_sysview.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-1-Clause */ +/* SPDX-FileCopyrightText: (c) 1995-2021 SEGGER Microcontroller GmbH. All rights reserved. */ +/* SPDX-FileContributor: 2023 Espressif Systems (Shanghai) CO LTD */ + +/* +* The contend below is extracted from files SEGGER_SYSVIEW.h and SEGGER_SYSVIEW_Int.h in: +* https://www.segger.com/downloads/systemview/systemview_target_src +* SystemView version: 3.42 +*/ + +#ifndef OPENOCD_TARGET_SEGGER_SYSVIEW_H +#define OPENOCD_TARGET_SEGGER_SYSVIEW_H + +#define SYSVIEW_EVTID_NOP 0 /* Dummy packet. */ +#define SYSVIEW_EVTID_OVERFLOW 1 +#define SYSVIEW_EVTID_ISR_ENTER 2 +#define SYSVIEW_EVTID_ISR_EXIT 3 +#define SYSVIEW_EVTID_TASK_START_EXEC 4 +#define SYSVIEW_EVTID_TASK_STOP_EXEC 5 +#define SYSVIEW_EVTID_TASK_START_READY 6 +#define SYSVIEW_EVTID_TASK_STOP_READY 7 +#define SYSVIEW_EVTID_TASK_CREATE 8 +#define SYSVIEW_EVTID_TASK_INFO 9 +#define SYSVIEW_EVTID_TRACE_START 10 +#define SYSVIEW_EVTID_TRACE_STOP 11 +#define SYSVIEW_EVTID_SYSTIME_CYCLES 12 +#define SYSVIEW_EVTID_SYSTIME_US 13 +#define SYSVIEW_EVTID_SYSDESC 14 +#define SYSVIEW_EVTID_USER_START 15 +#define SYSVIEW_EVTID_USER_STOP 16 +#define SYSVIEW_EVTID_IDLE 17 +#define SYSVIEW_EVTID_ISR_TO_SCHEDULER 18 +#define SYSVIEW_EVTID_TIMER_ENTER 19 +#define SYSVIEW_EVTID_TIMER_EXIT 20 +#define SYSVIEW_EVTID_STACK_INFO 21 +#define SYSVIEW_EVTID_MODULEDESC 22 + +#define SYSVIEW_EVTID_INIT 24 +#define SYSVIEW_EVTID_NAME_RESOURCE 25 +#define SYSVIEW_EVTID_PRINT_FORMATTED 26 +#define SYSVIEW_EVTID_NUMMODULES 27 +#define SYSVIEW_EVTID_END_CALL 28 +#define SYSVIEW_EVTID_TASK_TERMINATE 29 + +#define SYSVIEW_EVTID_EX 31 +// +// SystemView extended events. Sent with ID 31. +// +#define SYSVIEW_EVTID_EX_MARK 0 +#define SYSVIEW_EVTID_EX_NAME_MARKER 1 +#define SYSVIEW_EVTID_EX_HEAP_DEFINE 2 +#define SYSVIEW_EVTID_EX_HEAP_ALLOC 3 +#define SYSVIEW_EVTID_EX_HEAP_ALLOC_EX 4 +#define SYSVIEW_EVTID_EX_HEAP_FREE 5 + +#define SYSVIEW_SYNC_LEN 10 + +#define SYSVIEW_EVENT_ID_MAX (200) + +// +// Commands that Host can send to target +// +enum { + SEGGER_SYSVIEW_COMMAND_ID_START = 1, + SEGGER_SYSVIEW_COMMAND_ID_STOP, + SEGGER_SYSVIEW_COMMAND_ID_GET_SYSTIME, + SEGGER_SYSVIEW_COMMAND_ID_GET_TASKLIST, + SEGGER_SYSVIEW_COMMAND_ID_GET_SYSDESC, + SEGGER_SYSVIEW_COMMAND_ID_GET_NUMMODULES, + SEGGER_SYSVIEW_COMMAND_ID_GET_MODULEDESC, + SEGGER_SYSVIEW_COMMAND_ID_HEARTBEAT = 127, + // Extended commands: Commands >= 128 have a second parameter + SEGGER_SYSVIEW_COMMAND_ID_GET_MODULE = 128 +}; + +/* Minimum compatible SEGGER SystemView tool version */ +#define SYSVIEW_MIN_VER_STRING "SEGGER SystemViewer V2.42" + +#endif -- cgit v1.1