|
From: openocd-gerrit <ope...@us...> - 2023-05-27 06:39:43
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Main OpenOCD repository".
The branch, master has been updated
via a0fecd6c41a6f2c965352efb0df70a149b7b260c (commit)
from 144d940a8b0276d2ecb97ee6615d8a78faf4117d (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit a0fecd6c41a6f2c965352efb0df70a149b7b260c
Author: Erhan Kurubas <erh...@es...>
Date: Fri Apr 14 00:07:18 2023 +0200
target/espressif: add system level tracing feature
Produces traces compatible with SEGGER SystemView tool.
Signed-off-by: Erhan Kurubas <erh...@es...>
Change-Id: If1057309edbb91ed2cf1ebf9137c378d3deb9b88
Reviewed-on: https://review.openocd.org/c/openocd/+/7606
Tested-by: jenkins
Reviewed-by: Antonio Borneo <bor...@gm...>
diff --git a/doc/openocd.texi b/doc/openocd.texi
index d0bc3f9f5..9b485c5e1 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://<outfile1> [file://<outfile2>] [<poll_period> [<trace_size> [<stop_tmo> [<wait4halt> [<skip_size>]]]]])
+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://<outfile> [<poll_period> [<trace_size> [<stop_tmo> [<wait4halt> [<skip_size>]]]]])
+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 c1759ed77..14625d4b3 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 291503e53..0c510ffbf 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 <destination> [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]] | [stop] | [status] | [dump <destination>]",
+ "(start <destination> [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]) | (stop) | (status) | (dump <destination>)",
+ },
+ {
+ .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://<outfile1> [file://<outfile2>] [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://<outfile> [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 000000000..2fe215778
--- /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 <helper/log.h>
+#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;
+ }
+ i...
[truncated message content] |