target/espressif: add application tracing functionality over JTAG
[openocd.git] / src / target / espressif / esp32_apptrace.c
diff --git a/src/target/espressif/esp32_apptrace.c b/src/target/espressif/esp32_apptrace.c
new file mode 100644 (file)
index 0000000..dfeb794
--- /dev/null
@@ -0,0 +1,1376 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   ESP32xx application tracing module for OpenOCD                        *
+ *   Copyright (C) 2017 Espressif Systems Ltd.                             *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifndef _WIN32
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+#endif
+
+#include <helper/list.h>
+#include <helper/time_support.h>
+#include <target/target.h>
+#include <target/target_type.h>
+#include <target/smp.h>
+#include <server/server.h>
+#include "esp_xtensa.h"
+#include "esp_xtensa_smp.h"
+#include "esp_xtensa_apptrace.h"
+#include "esp32_apptrace.h"
+
+#define ESP32_APPTRACE_USER_BLOCK_CORE(_v_)     ((_v_) >> 15)
+#define ESP32_APPTRACE_USER_BLOCK_LEN(_v_)      ((_v_) & ~BIT(15))
+
+#define ESP32_APPTRACE_USER_BLOCK_HDR_SZ        4
+
+#define ESP_APPTRACE_CMD_MODE_GEN               0
+#define ESP_APPTRACE_CMD_MODE_SYSVIEW           1
+#define ESP_APPTRACE_CMD_MODE_SYSVIEW_MCORE     2
+#define ESP_APPTRACE_CMD_MODE_SYNC              3
+
+#define ESP32_APPTRACE_TGT_STATE_TMO            5000
+#define ESP_APPTRACE_BLOCKS_POOL_SZ             10
+
+struct esp32_apptrace_dest_file_data {
+       int fout;
+};
+
+struct esp32_apptrace_dest_tcp_data {
+       int sockfd;
+};
+
+struct esp32_apptrace_target_state {
+       int running;
+       uint32_t block_id;
+       uint32_t data_len;
+};
+
+struct esp_apptrace_target2host_hdr {
+       uint16_t block_sz;
+       uint16_t wr_sz;
+};
+#define APPTRACE_BLOCK_SIZE_OFFSET      0
+#define APPTRACE_WR_SIZE_OFFSET         2
+
+struct esp32_apptrace_block {
+       struct list_head node;
+       uint8_t *data;
+       uint32_t data_len;
+};
+
+static int esp32_apptrace_data_processor(void *priv);
+static int esp32_apptrace_get_data_info(struct esp32_apptrace_cmd_ctx *ctx,
+       struct esp32_apptrace_target_state *target_state,
+       uint32_t *fired_target_num);
+static int esp32_apptrace_safe_halt_targets(struct esp32_apptrace_cmd_ctx *ctx,
+       struct esp32_apptrace_target_state *targets);
+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 const bool s_time_stats_enable = true;
+
+/*********************************************************************
+*                       Trace destination API
+**********************************************************************/
+
+static int esp32_apptrace_file_dest_write(void *priv, uint8_t *data, int size)
+{
+       struct esp32_apptrace_dest_file_data *dest_data = (struct esp32_apptrace_dest_file_data *)priv;
+
+       int wr_sz = write(dest_data->fout, data, size);
+       if (wr_sz != size) {
+               LOG_ERROR("Failed to write %d bytes to out file (%d)! Written %d.", size, errno, wr_sz);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_file_dest_cleanup(void *priv)
+{
+       struct esp32_apptrace_dest_file_data *dest_data = (struct esp32_apptrace_dest_file_data *)priv;
+
+       if (dest_data->fout > 0)
+               close(dest_data->fout);
+       free(dest_data);
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_file_dest_init(struct esp32_apptrace_dest *dest, const char *dest_name)
+{
+       struct esp32_apptrace_dest_file_data *dest_data = calloc(1, sizeof(*dest_data));
+       if (!dest_data) {
+               LOG_ERROR("Failed to alloc mem for file dest!");
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("Open file %s", dest_name);
+       dest_data->fout = open(dest_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
+       if (dest_data->fout <= 0) {
+               LOG_ERROR("Failed to open file %s", dest_name);
+               free(dest_data);
+               return ERROR_FAIL;
+       }
+
+       dest->priv = dest_data;
+       dest->write = esp32_apptrace_file_dest_write;
+       dest->clean = esp32_apptrace_file_dest_cleanup;
+       dest->log_progress = true;
+
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_console_dest_write(void *priv, uint8_t *data, int size)
+{
+       LOG_USER_N("%.*s", size, data);
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_console_dest_cleanup(void *priv)
+{
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_console_dest_init(struct esp32_apptrace_dest *dest, const char *dest_name)
+{
+       dest->priv = NULL;
+       dest->write = esp32_apptrace_console_dest_write;
+       dest->clean = esp32_apptrace_console_dest_cleanup;
+       dest->log_progress = false;
+
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_tcp_dest_write(void *priv, uint8_t *data, int size)
+{
+       struct esp32_apptrace_dest_tcp_data *dest_data = (struct esp32_apptrace_dest_tcp_data *)priv;
+       int wr_sz = write_socket(dest_data->sockfd, data, size);
+       if (wr_sz != size) {
+               LOG_ERROR("Failed to write %u bytes to out socket (%d)! Written %d.", size, errno, wr_sz);
+               return ERROR_FAIL;
+       }
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_tcp_dest_cleanup(void *priv)
+{
+       struct esp32_apptrace_dest_tcp_data *dest_data = (struct esp32_apptrace_dest_tcp_data *)priv;
+
+       if (dest_data->sockfd > 0)
+               close_socket(dest_data->sockfd);
+       free(dest_data);
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_tcp_dest_init(struct esp32_apptrace_dest *dest, const char *dest_name)
+{
+       const char *port_sep = strchr(dest_name, ':');
+       /* separator not found, or was the first or the last character */
+       if (!port_sep || port_sep == dest_name || port_sep == dest_name + strlen(dest_name) - 1) {
+               LOG_ERROR("apptrace: Invalid connection URI, format should be tcp://host:port");
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+       size_t hostname_len = port_sep - dest_name;
+
+       char hostname[64] = { 0 };
+       if (hostname_len >= sizeof(hostname)) {
+               LOG_ERROR("apptrace: Hostname too long");
+               return ERROR_COMMAND_ARGUMENT_INVALID;
+       }
+       memcpy(hostname, dest_name, hostname_len);
+
+       const char *port_str = port_sep + 1;
+       struct addrinfo *ai;
+       int flags = 0;
+#ifdef AI_NUMERICSERV
+       flags |= AI_NUMERICSERV;
+#endif /* AI_NUMERICSERV */
+       struct addrinfo hint = {
+               .ai_family = AF_UNSPEC,
+               .ai_socktype = SOCK_STREAM,
+               .ai_protocol = 0,
+               .ai_flags = flags
+       };
+       int res = getaddrinfo(hostname, port_str, &hint, &ai);
+       if (res != 0) {
+               LOG_ERROR("apptrace: Failed to resolve host name: %s", hostname);
+               return ERROR_FAIL;
+       }
+       int sockfd = -1;
+       for (struct addrinfo *ai_it = ai; ai_it; ai_it = ai_it->ai_next) {
+               sockfd = socket(ai_it->ai_family, ai_it->ai_socktype, ai_it->ai_protocol);
+               if (sockfd < 0) {
+                       LOG_DEBUG("apptrace: Failed to create socket (%d, %d, %d) (%s)",
+                               ai_it->ai_family,
+                               ai_it->ai_socktype,
+                               ai_it->ai_protocol,
+                               strerror(errno));
+                       continue;
+               }
+
+               char cur_hostname[NI_MAXHOST];
+               char cur_portname[NI_MAXSERV];
+               res =
+                       getnameinfo(ai_it->ai_addr, ai_it->ai_addrlen, cur_hostname,
+                       sizeof(cur_hostname),
+                       cur_portname, sizeof(cur_portname),
+                       NI_NUMERICHOST | NI_NUMERICSERV);
+               if (res != 0)
+                       continue;
+
+               LOG_INFO("apptrace: Trying to connect to %s:%s", cur_hostname, cur_portname);
+               if (connect(sockfd, ai_it->ai_addr, ai_it->ai_addrlen) < 0) {
+                       close_socket(sockfd);
+                       sockfd = -1;
+                       LOG_WARNING("apptrace: Connection failed (%s)", strerror(errno));
+                       continue;
+               }
+               break;
+       }
+       freeaddrinfo(ai);
+       if (sockfd < 0) {
+               LOG_ERROR("apptrace: Could not connect to %s:%s", hostname, port_str);
+               return ERROR_FAIL;
+       }
+       LOG_INFO("apptrace: Connected!");
+
+       struct esp32_apptrace_dest_tcp_data *dest_data = calloc(1, sizeof(struct esp32_apptrace_dest_tcp_data));
+       if (!dest_data) {
+               LOG_ERROR("apptrace: Failed to alloc mem for tcp dest!");
+               close_socket(sockfd);
+               return ERROR_FAIL;
+       }
+
+       dest_data->sockfd = sockfd;
+       dest->priv = dest_data;
+       dest->write = esp32_apptrace_tcp_dest_write;
+       dest->clean = esp32_apptrace_tcp_dest_cleanup;
+       dest->log_progress = true;
+
+       return ERROR_OK;
+}
+
+int esp32_apptrace_dest_init(struct esp32_apptrace_dest dest[], const char *dest_paths[], unsigned int max_dests)
+{
+       int res;
+       unsigned int i;
+
+       for (i = 0; i < max_dests; i++) {
+               if (strncmp(dest_paths[i], "file://", 7) == 0)
+                       res = esp32_apptrace_file_dest_init(&dest[i], &dest_paths[i][7]);
+               else if (strncmp(dest_paths[i], "con:", 4) == 0)
+                       res = esp32_apptrace_console_dest_init(&dest[i], NULL);
+               else if (strncmp(dest_paths[i], "tcp://", 6) == 0)
+                       res = esp32_apptrace_tcp_dest_init(&dest[i], &dest_paths[i][6]);
+               else
+                       break;
+
+               if (res != ERROR_OK) {
+                       LOG_ERROR("apptrace: Failed to init trace data destination '%s'!", dest_paths[i]);
+                       return 0;
+               }
+       }
+
+       return i;
+}
+
+int esp32_apptrace_dest_cleanup(struct esp32_apptrace_dest dest[], unsigned int max_dests)
+{
+       for (unsigned int i = 0; i < max_dests; i++) {
+               if (dest[i].clean && dest[i].priv) {
+                       int res = dest[i].clean(dest[i].priv);
+                       dest[i].priv = NULL;
+                       return res;
+               }
+       }
+       return ERROR_OK;
+}
+
+/*********************************************************************
+*                 Trace data blocks management API
+**********************************************************************/
+static void esp32_apptrace_blocks_pool_cleanup(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       struct esp32_apptrace_block *cur;
+       struct list_head *head = &ctx->free_trace_blocks;
+       struct list_head *tmp, *pos;
+
+       list_for_each_safe(pos, tmp, head) {
+               cur = list_entry(pos, struct esp32_apptrace_block, node);
+               if (cur) {
+                       list_del(&cur->node);
+                       free(cur->data);
+                       free(cur);
+               }
+       }
+
+       head = &ctx->ready_trace_blocks;
+
+       list_for_each_safe(pos, tmp, head) {
+               cur = list_entry(pos, struct esp32_apptrace_block, node);
+               if (cur) {
+                       list_del(&cur->node);
+                       free(cur->data);
+                       free(cur);
+               }
+       }
+}
+
+struct esp32_apptrace_block *esp32_apptrace_free_block_get(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       struct esp32_apptrace_block *block = NULL;
+
+       if (!list_empty(&ctx->free_trace_blocks)) {
+               /*get first */
+               block = list_first_entry(&ctx->free_trace_blocks, struct esp32_apptrace_block, node);
+               list_del(&block->node);
+       }
+
+       return block;
+}
+
+static int esp32_apptrace_ready_block_put(struct esp32_apptrace_cmd_ctx *ctx, struct esp32_apptrace_block *block)
+{
+       LOG_DEBUG("esp32_apptrace_ready_block_put");
+       /* add to ready blocks list */
+       INIT_LIST_HEAD(&block->node);
+       list_add(&block->node, &ctx->ready_trace_blocks);
+
+       return ERROR_OK;
+}
+
+static struct esp32_apptrace_block *esp32_apptrace_ready_block_get(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       struct esp32_apptrace_block *block = NULL;
+
+       if (!list_empty(&ctx->ready_trace_blocks)) {
+               struct list_head *head = &ctx->ready_trace_blocks;
+               struct list_head *tmp, *pos;
+
+               list_for_each_safe(pos, tmp, head) {
+                       block = list_entry(pos, struct esp32_apptrace_block, node);
+               }
+               /* remove it from ready list */
+               list_del(&block->node);
+       }
+
+       return block;
+}
+
+static int esp32_apptrace_block_free(struct esp32_apptrace_cmd_ctx *ctx, struct esp32_apptrace_block *block)
+{
+       /* add to free blocks list */
+       INIT_LIST_HEAD(&block->node);
+       list_add(&block->node, &ctx->free_trace_blocks);
+
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_wait_tracing_finished(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       int64_t timeout = timeval_ms() + (LOG_LEVEL_IS(LOG_LVL_DEBUG) ? 70000 : 5000);
+       while (!list_empty(&ctx->ready_trace_blocks)) {
+               alive_sleep(100);
+               if (timeval_ms() >= timeout) {
+                       LOG_ERROR("Failed to wait for pended trace blocks!");
+                       return ERROR_FAIL;
+               }
+       }
+       /* signal timer callback to stop */
+       ctx->running = 0;
+       target_unregister_timer_callback(esp32_apptrace_data_processor, ctx);
+       return ERROR_OK;
+}
+
+/*********************************************************************
+*                          Trace commands
+**********************************************************************/
+
+int esp32_apptrace_cmd_ctx_init(struct esp32_apptrace_cmd_ctx *cmd_ctx, struct command_invocation *cmd, int mode)
+{
+       struct target *target = get_current_target(CMD_CTX);
+
+       memset(cmd_ctx, 0, sizeof(struct esp32_apptrace_cmd_ctx));
+       cmd_ctx->target = target;
+       cmd_ctx->mode = mode;
+       cmd_ctx->target_state = target->state;
+       cmd_ctx->cmd = cmd;
+
+       if (target->smp) {
+               struct target_list *head;
+               struct target *curr;
+               unsigned int i = 0;
+               cmd_ctx->cores_num = 0;
+               foreach_smp_target(head, target->smp_targets) {
+                       curr = head->target;
+                       if (i == ESP32_APPTRACE_MAX_CORES_NUM) {
+                               command_print(cmd, "Too many cores configured! Max %d cores are supported.",
+                                       ESP32_APPTRACE_MAX_CORES_NUM);
+                               return ERROR_FAIL;
+                       }
+                       if (!target_was_examined(curr))
+                               continue;
+                       cmd_ctx->cores_num++;
+                       cmd_ctx->cpus[i++] = curr;
+               }
+       } else {
+               cmd_ctx->cores_num = 1;
+               cmd_ctx->cpus[0] = target;
+       }
+       /* some relies on ESP32_APPTRACE_MAX_CORES_NUM
+        * TODO: remove that dependency */
+       assert(cmd_ctx->cores_num <= ESP32_APPTRACE_MAX_CORES_NUM && "Too many cores number!");
+
+       struct xtensa *xtensa = target->arch_info;
+       if (xtensa->common_magic == XTENSA_COMMON_MAGIC) {
+               cmd_ctx->hw = target_to_esp_xtensa(target)->apptrace.hw;
+       } else { /* TODO: riscv is not supported yet */
+               command_print(cmd, "Unsupported target arch 0x%X", xtensa->common_magic);
+               return ERROR_FAIL;
+       }
+
+       cmd_ctx->max_trace_block_sz = cmd_ctx->hw->max_block_size_get(cmd_ctx->cpus[0]);
+       if (cmd_ctx->max_trace_block_sz == 0) {
+               command_print(cmd, "Failed to get max trace block size!");
+               return ERROR_FAIL;
+       }
+       LOG_INFO("Total trace memory: %" PRIu32 " bytes", cmd_ctx->max_trace_block_sz);
+
+       INIT_LIST_HEAD(&cmd_ctx->ready_trace_blocks);
+       INIT_LIST_HEAD(&cmd_ctx->free_trace_blocks);
+       for (unsigned int i = 0; i < ESP_APPTRACE_BLOCKS_POOL_SZ; i++) {
+               struct esp32_apptrace_block *block = calloc(1, sizeof(struct esp32_apptrace_block));
+               if (!block) {
+                       command_print(cmd, "Failed to alloc trace buffer entry!");
+                       esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+                       return ERROR_FAIL;
+               }
+               block->data = malloc(cmd_ctx->max_trace_block_sz);
+               if (!block->data) {
+                       free(block);
+                       command_print(cmd, "Failed to alloc trace buffer %" PRIu32 " bytes!", cmd_ctx->max_trace_block_sz);
+                       esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+                       return ERROR_FAIL;
+               }
+               INIT_LIST_HEAD(&block->node);
+               list_add(&block->node, &cmd_ctx->free_trace_blocks);
+       }
+
+       cmd_ctx->running = 1;
+       if (cmd_ctx->mode != ESP_APPTRACE_CMD_MODE_SYNC) {
+               int res = target_register_timer_callback(esp32_apptrace_data_processor,
+                       0,
+                       TARGET_TIMER_TYPE_PERIODIC,
+                       cmd_ctx);
+               if (res != ERROR_OK) {
+                       command_print(cmd, "Failed to start trace data timer callback (%d)!", res);
+                       esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+                       return ERROR_FAIL;
+               }
+       }
+
+       if (s_time_stats_enable) {
+               cmd_ctx->stats.min_blk_read_time = 1000000.0;
+               cmd_ctx->stats.min_blk_proc_time = 1000000.0;
+       }
+       if (duration_start(&cmd_ctx->idle_time) != 0) {
+               command_print(cmd, "Failed to start idle time measurement!");
+               esp32_apptrace_cmd_ctx_cleanup(cmd_ctx);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+int esp32_apptrace_cmd_ctx_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx)
+{
+       esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+       return ERROR_OK;
+}
+
+#define ESP32_APPTRACE_CMD_NUM_ARG_CHECK(_cmd_, _arg_, _start_, _end_)    \
+       do { \
+               if ((_arg_) == 0 && (_start_) == (_end_)) { \
+                       command_print(_cmd_, "Invalid '" # _arg_ "' arg!"); \
+                       return; \
+               } \
+       } while (0)
+
+void esp32_apptrace_cmd_args_parse(struct esp32_apptrace_cmd_ctx *cmd_ctx,
+       struct esp32_apptrace_cmd_data *cmd_data,
+       const char **argv,
+       int argc)
+{
+       char *end;
+
+       cmd_data->poll_period = strtoul(argv[0], &end, 10);
+       ESP32_APPTRACE_CMD_NUM_ARG_CHECK(cmd_ctx->cmd, cmd_data->poll_period, argv[0], end);
+       if (argc > 1) {
+               cmd_data->max_len = strtoul(argv[1], &end, 10);
+               ESP32_APPTRACE_CMD_NUM_ARG_CHECK(cmd_ctx->cmd, cmd_data->max_len, argv[1], end);
+               if (argc > 2) {
+                       int32_t tmo = strtol(argv[2], &end, 10);
+                       ESP32_APPTRACE_CMD_NUM_ARG_CHECK(cmd_ctx->cmd, tmo, argv[2], end);
+                       cmd_ctx->stop_tmo = 1.0 * tmo;
+                       if (argc > 3) {
+                               cmd_data->wait4halt = strtoul(argv[3], &end, 10);
+                               ESP32_APPTRACE_CMD_NUM_ARG_CHECK(cmd_ctx->cmd, cmd_data->wait4halt, argv[3], end);
+                               if (argc > 4) {
+                                       cmd_data->skip_len = strtoul(argv[4], &end, 10);
+                                       ESP32_APPTRACE_CMD_NUM_ARG_CHECK(cmd_ctx->cmd, cmd_data->skip_len, argv[4], end);
+                               }
+                       }
+               }
+       }
+}
+
+static int esp32_apptrace_core_id_get(struct target *target, uint8_t *hdr_buf)
+{
+       return ESP32_APPTRACE_USER_BLOCK_CORE(target_buffer_get_u16(target, hdr_buf + APPTRACE_BLOCK_SIZE_OFFSET));
+}
+
+static uint32_t esp32_apptrace_usr_block_len_get(struct target *target, uint8_t *hdr_buf, uint32_t *wr_len)
+{
+       *wr_len = ESP32_APPTRACE_USER_BLOCK_LEN(target_buffer_get_u16(target, hdr_buf + APPTRACE_WR_SIZE_OFFSET));
+       return ESP32_APPTRACE_USER_BLOCK_LEN(target_buffer_get_u16(target, hdr_buf + APPTRACE_BLOCK_SIZE_OFFSET));
+}
+
+static int esp32_apptrace_cmd_init(struct esp32_apptrace_cmd_ctx *cmd_ctx,
+       struct command_invocation *cmd,
+       int mode,
+       const char **argv,
+       int argc)
+{
+       struct esp32_apptrace_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;
+
+       cmd_data = calloc(1, sizeof(*cmd_data));
+       assert(cmd_data && "No memory for command data!");
+       cmd_ctx->cmd_priv = cmd_data;
+
+       /*outfile1 [poll_period [trace_size [stop_tmo [wait4halt [skip_size]]]]] */
+       res = esp32_apptrace_dest_init(&cmd_data->data_dest, argv, 1);
+       if (res != 1) { /* only one destination needs to be initialized */
+               command_print(cmd, "Wrong args! Needs a trace data destination!");
+               free(cmd_data);
+               goto on_error;
+       }
+       cmd_ctx->stop_tmo = -1.0;       /* infinite */
+       cmd_data->max_len = UINT32_MAX;
+       cmd_data->poll_period = 0 /*ms*/;
+       if (argc > 1)
+               /* parse remaining args */
+               esp32_apptrace_cmd_args_parse(cmd_ctx, cmd_data, &argv[1], argc - 1);
+
+       LOG_USER("App trace params: from %d cores, size %" PRId32 " bytes, stop_tmo %g s, poll period %" PRId32
+               " ms, wait_rst %d, skip %" PRId32 " bytes", cmd_ctx->cores_num,
+               cmd_data->max_len,
+               cmd_ctx->stop_tmo,
+               cmd_data->poll_period,
+               cmd_data->wait4halt,
+               cmd_data->skip_len);
+
+       cmd_ctx->trace_format.hdr_sz = ESP32_APPTRACE_USER_BLOCK_HDR_SZ;
+       cmd_ctx->trace_format.core_id_get = esp32_apptrace_core_id_get;
+       cmd_ctx->trace_format.usr_block_len_get = esp32_apptrace_usr_block_len_get;
+       return ERROR_OK;
+on_error:
+       command_print(cmd, "Not enough args! Need %d trace data destinations!", cmd_ctx->cores_num);
+       cmd_ctx->running = 0;
+       esp32_apptrace_cmd_ctx_cleanup(cmd_ctx);
+       return res;
+}
+
+static int esp32_apptrace_cmd_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx)
+{
+       struct esp32_apptrace_cmd_data *cmd_data = cmd_ctx->cmd_priv;
+
+       esp32_apptrace_dest_cleanup(&cmd_data->data_dest, 1);
+       free(cmd_data);
+       cmd_ctx->cmd_priv = NULL;
+       esp32_apptrace_cmd_ctx_cleanup(cmd_ctx);
+       return ERROR_OK;
+}
+
+static void esp32_apptrace_print_stats(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       struct esp32_apptrace_cmd_data *cmd_data = ctx->cmd_priv;
+       uint32_t trace_sz = 0;
+
+       if (cmd_data)
+               trace_sz = ctx->tot_len > cmd_data->skip_len ? ctx->tot_len - cmd_data->skip_len : 0;
+       LOG_USER("Tracing is %s. Size is %" PRId32 " of %" PRId32 " @ %f (%f) KiB/s",
+               !ctx->running ? "STOPPED" : "RUNNING",
+               trace_sz,
+               cmd_data ? cmd_data->max_len : 0,
+               duration_kbps(&ctx->read_time, ctx->tot_len),
+               duration_kbps(&ctx->read_time, ctx->raw_tot_len));
+       LOG_USER("Data: blocks incomplete %" PRId32 ", lost bytes: %" PRId32,
+               ctx->stats.incompl_blocks,
+               ctx->stats.lost_bytes);
+       if (s_time_stats_enable) {
+               LOG_USER("Block read time [%f..%f] ms",
+                       1000 * ctx->stats.min_blk_read_time,
+                       1000 * ctx->stats.max_blk_read_time);
+               LOG_USER("Block proc time [%f..%f] ms",
+                       1000 * ctx->stats.min_blk_proc_time,
+                       1000 * ctx->stats.max_blk_proc_time);
+       }
+}
+
+static int esp32_apptrace_wait4halt(struct esp32_apptrace_cmd_ctx *ctx, struct target *target)
+{
+       LOG_USER("Wait for halt...");
+       while (!openocd_is_shutdown_pending()) {
+               int res = target_poll(target);
+               if (res != ERROR_OK)
+                       return res;
+               if (target->state == TARGET_HALTED) {
+                       LOG_USER("%s: HALTED", target->cmd_name);
+                       break;
+               }
+               alive_sleep(500);
+       }
+       return ERROR_OK;
+}
+
+int esp32_apptrace_safe_halt_targets(struct esp32_apptrace_cmd_ctx *ctx,
+       struct esp32_apptrace_target_state *targets)
+{
+       int res = ERROR_OK;
+
+       memset(targets, 0, ctx->cores_num * sizeof(struct esp32_apptrace_target_state));
+       /* halt all CPUs */
+       LOG_DEBUG("Halt all targets!");
+       for (unsigned int k = 0; k < ctx->cores_num; k++) {
+               if (!target_was_examined(ctx->cpus[k]))
+                       continue;
+               if (ctx->cpus[k]->state == TARGET_HALTED)
+                       continue;
+               res = target_halt(ctx->cpus[k]);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to halt target (%d)!", res);
+                       return res;
+               }
+               res = target_wait_state(ctx->cpus[k], TARGET_HALTED, ESP32_APPTRACE_TGT_STATE_TMO);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to wait halt target %s / %d (%d)!",
+                               target_name(ctx->cpus[k]),
+                               ctx->cpus[k]->state,
+                               res);
+                       return res;
+               }
+       }
+       /* read current block statuses from CPUs */
+       LOG_DEBUG("Read current block statuses");
+       for (unsigned int k = 0; k < ctx->cores_num; k++) {
+               uint32_t stat;
+               res = ctx->hw->status_reg_read(ctx->cpus[k], &stat);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to read trace status (%d)!", res);
+                       return res;
+               }
+               /* check if some CPU stopped inside tracing regs update critical section */
+               if (stat) {
+                       if (ctx->hw->leave_trace_crit_section_start) {
+                               res = ctx->hw->leave_trace_crit_section_start(ctx->cpus[k]);
+                               if (res != ERROR_OK)
+                                       return res;
+                       }
+                       uint32_t bp_addr = stat;
+                       res = breakpoint_add(ctx->cpus[k], bp_addr, 1, BKPT_HARD);
+                       if (res != ERROR_OK) {
+                               LOG_ERROR("Failed to set breakpoint (%d)!", res);
+                               return res;
+                       }
+                       while (stat) {
+                               /* allow this CPU to leave ERI write critical section */
+                               res = target_resume(ctx->cpus[k], 1, 0, 1, 0);
+                               if (res != ERROR_OK) {
+                                       LOG_ERROR("Failed to resume target (%d)!", res);
+                                       breakpoint_remove(ctx->cpus[k], bp_addr);
+                                       return res;
+                               }
+                               /* wait for CPU to be halted on BP */
+                               enum target_debug_reason debug_reason = DBG_REASON_UNDEFINED;
+                               while (debug_reason != DBG_REASON_BREAKPOINT) {
+                                       res = target_wait_state(ctx->cpus[k], TARGET_HALTED,
+                                               ESP32_APPTRACE_TGT_STATE_TMO);
+                                       if (res != ERROR_OK) {
+                                               LOG_ERROR("Failed to wait halt on bp (%d)!", res);
+                                               breakpoint_remove(ctx->cpus[k], bp_addr);
+                                               return res;
+                                       }
+                                       debug_reason = ctx->cpus[k]->debug_reason;
+                               }
+                               res = ctx->hw->status_reg_read(ctx->cpus[k], &stat);
+                               if (res != ERROR_OK) {
+                                       LOG_ERROR("Failed to read trace status (%d)!", res);
+                                       breakpoint_remove(ctx->cpus[k], bp_addr);
+                                       return res;
+                               }
+                       }
+                       breakpoint_remove(ctx->cpus[k], bp_addr);
+                       if (ctx->hw->leave_trace_crit_section_stop) {
+                               res = ctx->hw->leave_trace_crit_section_stop(ctx->cpus[k]);
+                               if (res != ERROR_OK)
+                                       return res;
+                       }
+               }
+               res = ctx->hw->data_len_read(ctx->cpus[k], &targets[k].block_id, &targets[k].data_len);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to read trace status (%d)!", res);
+                       return res;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_connect_targets(struct esp32_apptrace_cmd_ctx *ctx,
+       bool conn,
+       bool resume_target)
+{
+       struct esp32_apptrace_target_state target_to_connect[ESP32_APPTRACE_MAX_CORES_NUM];
+
+       if (conn)
+               LOG_USER("Connect targets...");
+       else
+               LOG_USER("Disconnect targets...");
+
+       int res = esp32_apptrace_safe_halt_targets(ctx, target_to_connect);
+       if (res != ERROR_OK) {
+               command_print(ctx->cmd, "Failed to halt targets (%d)!", res);
+               return res;
+       }
+       if (ctx->cores_num > 1) {
+               /* set block ids to the highest value */
+               uint32_t max_id = 0;
+               for (unsigned int k = 0; k < ctx->cores_num; k++) {
+                       if (target_to_connect[k].block_id > max_id)
+                               max_id = target_to_connect[k].block_id;
+               }
+               for (unsigned int k = 0; k < ctx->cores_num; k++)
+                       target_to_connect[k].block_id = max_id;
+       }
+       for (unsigned int k = 0; k < ctx->cores_num; k++) {
+               /* update host connected status */
+               res = ctx->hw->ctrl_reg_write(ctx->cpus[k],
+                       target_to_connect[k].block_id,
+                       0 /*ack target data*/,
+                       conn,
+                       false /*no host data*/);
+               if (res != ERROR_OK) {
+                       command_print(ctx->cmd, "Failed to read trace status (%d)!", res);
+                       return res;
+               }
+       }
+       if (resume_target) {
+               LOG_DEBUG("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) {
+                               command_print(ctx->cmd, "Failed to resume target (%d)!", res);
+                               return res;
+                       }
+                       if (ctx->cpus[k]->smp)
+                               smp_resumed = true;
+               }
+       }
+       if (conn)
+               LOG_INFO("Targets connected.");
+       else
+               LOG_INFO("Targets disconnected.");
+       return ERROR_OK;
+}
+
+int esp_apptrace_usr_block_write(const struct esp32_apptrace_hw *hw, struct target *target,
+       uint32_t block_id,
+       const uint8_t *data,
+       uint32_t size)
+{
+       struct esp_apptrace_host2target_hdr hdr = { .block_sz = size };
+       uint32_t buf_sz[2] = { sizeof(hdr), size };
+       const uint8_t *bufs[2] = { (const uint8_t *)&hdr, data };
+
+       if (size > hw->usr_block_max_size_get(target)) {
+               LOG_ERROR("Too large user block %" PRId32, size);
+               return ERROR_FAIL;
+       }
+
+       return hw->buffs_write(target,
+               ARRAY_SIZE(buf_sz),
+               buf_sz,
+               bufs,
+               block_id,
+               true /*ack target data*/,
+               true /*host data*/);
+}
+
+static uint32_t esp32_apptrace_usr_block_check(struct esp32_apptrace_cmd_ctx *ctx, uint8_t *hdr_buf)
+{
+       uint32_t wr_len = 0;
+       uint32_t usr_len = ctx->trace_format.usr_block_len_get(ctx->target, hdr_buf, &wr_len);
+       if (usr_len != wr_len) {
+               LOG_ERROR("Incomplete block sz %" PRId32 ", wr %" PRId32, usr_len, wr_len);
+               ctx->stats.incompl_blocks++;
+               ctx->stats.lost_bytes += usr_len - wr_len;
+       }
+       return usr_len;
+}
+
+int esp32_apptrace_get_data_info(struct esp32_apptrace_cmd_ctx *ctx,
+       struct esp32_apptrace_target_state *target_state,
+       uint32_t *fired_target_num)
+{
+       if (fired_target_num)
+               *fired_target_num = UINT32_MAX;
+
+       for (unsigned int i = 0; i < ctx->cores_num; i++) {
+               int res = ctx->hw->data_len_read(ctx->cpus[i], &target_state[i].block_id, &target_state[i].data_len);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to read data len on (%s)!", target_name(ctx->cpus[i]));
+                       return res;
+               }
+               if (target_state[i].data_len) {
+                       LOG_TARGET_DEBUG(ctx->cpus[i], "Block %" PRId32 ", len %" PRId32 " bytes on fired",
+                               target_state[i].block_id, target_state[i].data_len);
+                       if (fired_target_num)
+                               *fired_target_num = i;
+                       break;
+               }
+       }
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_process_data(struct esp32_apptrace_cmd_ctx *ctx,
+       unsigned int core_id,
+       uint8_t *data,
+       uint32_t data_len)
+{
+       struct esp32_apptrace_cmd_data *cmd_data = ctx->cmd_priv;
+
+       LOG_DEBUG("Got block %" PRId32 " bytes [%x %x...%x %x]", data_len, data[12], data[13],
+               data[data_len - 2], data[data_len - 1]);
+       if (ctx->tot_len + data_len > cmd_data->skip_len) {
+               uint32_t wr_idx = 0, wr_chunk_len = data_len;
+               if (ctx->tot_len < cmd_data->skip_len) {
+                       wr_chunk_len = (ctx->tot_len + wr_chunk_len) - cmd_data->skip_len;
+                       wr_idx = cmd_data->skip_len - ctx->tot_len;
+               }
+               if (ctx->tot_len + wr_chunk_len > cmd_data->max_len)
+                       wr_chunk_len -= (ctx->tot_len + wr_chunk_len - cmd_data->skip_len) - cmd_data->max_len;
+               if (wr_chunk_len > 0) {
+                       int res = cmd_data->data_dest.write(cmd_data->data_dest.priv, data + wr_idx, wr_chunk_len);
+                       if (res != ERROR_OK) {
+                               LOG_ERROR("Failed to write %" PRId32 " bytes to dest 0!", data_len);
+                               return res;
+                       }
+               }
+               ctx->tot_len += wr_chunk_len;
+       } else {
+               ctx->tot_len += data_len;
+       }
+
+       if (cmd_data->data_dest.log_progress)
+               LOG_USER("%" PRId32 " ", ctx->tot_len);
+       /* check for stop condition */
+       if (ctx->tot_len > cmd_data->skip_len && (ctx->tot_len - cmd_data->skip_len >= cmd_data->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;
+}
+
+static int esp32_apptrace_handle_trace_block(struct esp32_apptrace_cmd_ctx *ctx,
+       struct esp32_apptrace_block *block)
+{
+       uint32_t processed = 0;
+       uint32_t hdr_sz = ctx->trace_format.hdr_sz;
+
+       LOG_DEBUG("Got block %" PRId32 " bytes", block->data_len);
+       /* process user blocks one by one */
+       while (processed < block->data_len) {
+               LOG_DEBUG("Process usr block %" PRId32 "/%" PRId32, processed, block->data_len);
+               /* process user block */
+               uint32_t usr_len = esp32_apptrace_usr_block_check(ctx, block->data + processed);
+               int core_id = ctx->trace_format.core_id_get(ctx->target, block->data + processed);
+               /* process user data */
+               int res = ctx->process_data(ctx, core_id, block->data + processed + hdr_sz, usr_len);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to process %" PRId32 " bytes!", usr_len);
+                       return res;
+               }
+               processed += usr_len + hdr_sz;
+       }
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_data_processor(void *priv)
+{
+       struct esp32_apptrace_cmd_ctx *ctx = (struct esp32_apptrace_cmd_ctx *)priv;
+
+       if (!ctx->running)
+               return ERROR_OK;
+
+       struct esp32_apptrace_block *block = esp32_apptrace_ready_block_get(ctx);
+       if (!block)
+               return ERROR_OK;
+
+       int res = esp32_apptrace_handle_trace_block(ctx, block);
+       if (res != ERROR_OK) {
+               ctx->running = 0;
+               LOG_ERROR("Failed to process trace block %" PRId32 " bytes!", block->data_len);
+               return res;
+       }
+       res = esp32_apptrace_block_free(ctx, block);
+       if (res != ERROR_OK) {
+               ctx->running = 0;
+               LOG_ERROR("Failed to free ready block!");
+               return res;
+       }
+
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_check_connection(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       if (!ctx)
+               return ERROR_FAIL;
+
+       unsigned int busy_target_num = 0;
+
+       for (unsigned int i = 0; i < ctx->cores_num; i++) {
+               bool conn = true;
+               int res = ctx->hw->ctrl_reg_read(ctx->cpus[i], NULL, NULL, &conn);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to read apptrace control reg for cpu(%d) res(%d)!", i, res);
+                       return res;
+               }
+               if (!conn) {
+                       uint32_t stat = 0;
+                       LOG_TARGET_WARNING(ctx->cpus[i], "apptrace connection is lost. Re-connect.");
+                       res = ctx->hw->status_reg_read(ctx->cpus[i], &stat);
+                       if (res != ERROR_OK) {
+                               LOG_ERROR("Failed to read trace status (%d)!", res);
+                               return res;
+                       }
+                       if (stat) {
+                               LOG_TARGET_WARNING(ctx->cpus[i], "in critical state. Retry in next poll");
+                               if (++busy_target_num == ctx->cores_num) {
+                                       LOG_WARNING("No available core");
+                                       return ERROR_WAIT;
+                               }
+                               continue;
+                       }
+                       res = ctx->hw->ctrl_reg_write(ctx->cpus[i],
+                               0,
+                               0,
+                               true /*host connected*/,
+                               false /*no host data*/);
+                       if (res != ERROR_OK) {
+                               LOG_ERROR("Failed to write apptrace control reg for cpu(%d) res(%d)!", i, res);
+                               return res;
+                       }
+                       if (ctx->stop_tmo != -1.0) {
+                               /* re-start idle time measurement */
+                               if (duration_start(&ctx->idle_time) != 0) {
+                                       LOG_ERROR("Failed to re-start idle time measure!");
+                                       return ERROR_FAIL;
+                               }
+                       }
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int esp32_apptrace_poll(void *priv)
+{
+       struct esp32_apptrace_cmd_ctx *ctx = (struct esp32_apptrace_cmd_ctx *)priv;
+       int res;
+       uint32_t fired_target_num = 0;
+       struct esp32_apptrace_target_state target_state[ESP32_APPTRACE_MAX_CORES_NUM];
+       struct duration blk_proc_time;
+
+       if (!ctx->running) {
+               if (ctx->auto_clean)
+                       ctx->auto_clean(ctx);
+               return ERROR_FAIL;
+       }
+
+       /*  Check for connection is alive.For some reason target and therefore host_connected flag
+        *  might have been reset */
+       res = esp32_apptrace_check_connection(ctx);
+       if (res != ERROR_OK) {
+               if (res != ERROR_WAIT)
+                       ctx->running = 0;
+               return res;
+       }
+
+       /* check for data from target */
+       res = esp32_apptrace_get_data_info(ctx, target_state, &fired_target_num);
+       if (res != ERROR_OK) {
+               ctx->running = 0;
+               LOG_ERROR("Failed to read data len!");
+               return res;
+       }
+       /* LOG_DEBUG("Block %d (%d bytes) on target (%s)!", target_state[0].block_id,
+        * target_state[0].data_len, target_name(ctx->cpus[0])); */
+       if (fired_target_num == UINT32_MAX) {
+               /* no data has been received, but block could be switched due to the data transferred
+                * from host to target */
+               if (ctx->cores_num > 1) {
+                       uint32_t max_block_id = 0, min_block_id = ctx->hw->max_block_id;
+                       /* find maximum block ID and set the same ID in control reg for both cores
+                        * */
+                       for (unsigned int i = 0; i < ctx->cores_num; i++) {
+                               if (max_block_id < target_state[i].block_id)
+                                       max_block_id = target_state[i].block_id;
+                               if (min_block_id > target_state[i].block_id)
+                                       min_block_id = target_state[i].block_id;
+                       }
+                       /* handle block ID overflow */
+                       if (max_block_id == ctx->hw->max_block_id && min_block_id == 0)
+                               max_block_id = 0;
+                       for (unsigned int i = 0; i < ctx->cores_num; i++) {
+                               if (max_block_id != target_state[i].block_id) {
+                                       LOG_TARGET_DEBUG(ctx->cpus[i], "Ack empty block %" PRId32 "!", max_block_id);
+                                       res = ctx->hw->ctrl_reg_write(ctx->cpus[i],
+                                               max_block_id,
+                                               0 /*all read*/,
+                                               true /*host connected*/,
+                                               false /*no host data*/);
+                                       if (res != ERROR_OK) {
+                                               ctx->running = 0;
+                                               LOG_TARGET_ERROR(ctx->cpus[i], "Failed to ack empty data block!");
+                                               return res;
+                                       }
+                               }
+                       }
+                       ctx->last_blk_id = max_block_id;
+               }
+               if (ctx->stop_tmo != -1.0) {
+                       if (duration_measure(&ctx->idle_time) != 0) {
+                               ctx->running = 0;
+                               LOG_ERROR("Failed to measure idle time!");
+                               return ERROR_FAIL;
+                       }
+                       if (duration_elapsed(&ctx->idle_time) >= ctx->stop_tmo) {
+                               ctx->running = 0;
+                               LOG_ERROR("Data timeout!");
+                               return ERROR_FAIL;
+                       }
+               }
+               return ERROR_OK;/* no data */
+       }
+       /* sanity check */
+       if (target_state[fired_target_num].data_len > ctx->max_trace_block_sz) {
+               ctx->running = 0;
+               LOG_ERROR("Too large block size %" PRId32 "!", target_state[fired_target_num].data_len);
+               return ERROR_FAIL;
+       }
+       if (ctx->tot_len == 0) {
+               if (duration_start(&ctx->read_time) != 0) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to start trace read time measurement!");
+                       return ERROR_FAIL;
+               }
+       }
+       struct esp32_apptrace_block *block = esp32_apptrace_free_block_get(ctx);
+       if (!block) {
+               ctx->running = 0;
+               LOG_TARGET_ERROR(ctx->cpus[fired_target_num], "Failed to get free block for data!");
+               return ERROR_FAIL;
+       }
+       if (s_time_stats_enable) {
+               /* read block */
+               if (duration_start(&blk_proc_time) != 0) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to start block read time measurement!");
+                       return ERROR_FAIL;
+               }
+       }
+       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 */
+               ctx->mode != ESP_APPTRACE_CMD_MODE_SYNC);
+       if (res != ERROR_OK) {
+               ctx->running = 0;
+               LOG_TARGET_ERROR(ctx->cpus[fired_target_num], "Failed to read data!");
+               return res;
+       }
+       ctx->last_blk_id = target_state[fired_target_num].block_id;
+       block->data_len = target_state[fired_target_num].data_len;
+       ctx->raw_tot_len += block->data_len;
+       if (s_time_stats_enable) {
+               if (duration_measure(&blk_proc_time) != 0) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to measure block read time!");
+                       return ERROR_FAIL;
+               }
+               /* update stats */
+               float brt = duration_elapsed(&blk_proc_time);
+               if (brt > ctx->stats.max_blk_read_time)
+                       ctx->stats.max_blk_read_time = brt;
+               if (brt < ctx->stats.min_blk_read_time)
+                       ctx->stats.min_blk_read_time = brt;
+
+               if (duration_start(&blk_proc_time) != 0) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to start block proc time measurement!");
+                       return ERROR_FAIL;
+               }
+       }
+       /* in sync mode do not ack target data on other cores, esp32_apptrace_handle_trace_block() can write response
+        * data and will do ack thereafter */
+       if (ctx->mode != ESP_APPTRACE_CMD_MODE_SYNC) {
+               for (unsigned int i = 0; i < ctx->cores_num; i++) {
+                       if (i == fired_target_num)
+                               continue;
+                       res = ctx->hw->ctrl_reg_write(ctx->cpus[i],
+                               ctx->last_blk_id,
+                               0 /*all read*/,
+                               true /*host connected*/,
+                               false /*no host data*/);
+                       if (res != ERROR_OK) {
+                               ctx->running = 0;
+                               LOG_TARGET_ERROR(ctx->cpus[i], "Failed to ack data!");
+                               return res;
+                       }
+                       LOG_TARGET_DEBUG(ctx->cpus[i], "Ack block %" PRId32, ctx->last_blk_id);
+               }
+               res = esp32_apptrace_ready_block_put(ctx, block);
+               if (res != ERROR_OK) {
+                       ctx->running = 0;
+                       LOG_TARGET_ERROR(ctx->cpus[fired_target_num], "Failed to put ready block of data!");
+                       return res;
+               }
+       } else {
+               res = esp32_apptrace_handle_trace_block(ctx, block);
+               if (res != ERROR_OK) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to process trace block %" PRId32 " bytes!", block->data_len);
+                       return res;
+               }
+               res = esp32_apptrace_block_free(ctx, block);
+               if (res != ERROR_OK) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to free ready block!");
+                       return res;
+               }
+       }
+       if (ctx->stop_tmo != -1.0) {
+               /* start idle time measurement */
+               if (duration_start(&ctx->idle_time) != 0) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to start idle time measure!");
+                       return ERROR_FAIL;
+               }
+       }
+       if (s_time_stats_enable) {
+               if (duration_measure(&blk_proc_time) != 0) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to stop block proc time measure!");
+                       return ERROR_FAIL;
+               }
+               /* update stats */
+               float bt = duration_elapsed(&blk_proc_time);
+               if (bt > ctx->stats.max_blk_proc_time)
+                       ctx->stats.max_blk_proc_time = bt;
+               if (bt < ctx->stats.min_blk_proc_time)
+                       ctx->stats.min_blk_proc_time = bt;
+       }
+       return ERROR_OK;
+}
+
+static void esp32_apptrace_cmd_stop(struct esp32_apptrace_cmd_ctx *ctx)
+{
+       if (duration_measure(&ctx->read_time) != 0)
+               LOG_ERROR("Failed to stop trace read time measurement!");
+       int res = target_unregister_timer_callback(esp32_apptrace_poll, ctx);
+       if (res != ERROR_OK)
+               LOG_ERROR("Failed to unregister target timer handler (%d)!", res);
+
+       /* data processor is alive, so wait for all received blocks to be processed */
+       res = esp32_apptrace_wait_tracing_finished(ctx);
+       if (res != ERROR_OK)
+               LOG_ERROR("Failed to wait for pended blocks (%d)!", res);
+       res = esp32_apptrace_connect_targets(ctx, false, ctx->target_state == TARGET_RUNNING);
+       if (res != ERROR_OK)
+               LOG_ERROR("Failed to disconnect targets (%d)!", res);
+       esp32_apptrace_print_stats(ctx);
+       res = esp32_apptrace_cmd_cleanup(ctx);
+       if (res != ERROR_OK)
+               LOG_ERROR("Failed to cleanup cmd ctx (%d)!", 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;
+       struct esp32_apptrace_cmd_data *cmd_data;
+       int res = ERROR_FAIL;
+       enum target_state old_state;
+       struct target *target = get_current_target(CMD_CTX);
+
+       if (argc < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       /* command can be invoked on unexamined core, if so find examined one */
+       if (target->smp && !target_was_examined(target)) {
+               struct target_list *head;
+               struct target *curr;
+               LOG_WARNING("Current target '%s' was not examined!", target_name(target));
+               foreach_smp_target(head, target->smp_targets) {
+                       curr = head->target;
+                       if (target_was_examined(curr)) {
+                               target = curr;
+                               LOG_WARNING("Run command on target '%s'", target_name(target));
+                               break;
+                       }
+               }
+       }
+       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;
+               }
+               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);
+                       if (res != ERROR_OK) {
+                               command_print(cmd, "Failed to wait for halt target (%d)!", res);
+                               goto _on_start_error;
+                       }
+               }
+               res = esp32_apptrace_connect_targets(&s_at_cmd_ctx, true, old_state == TARGET_RUNNING);
+               if (res != ERROR_OK) {
+                       command_print(cmd, "Failed to connect to targets (%d)!", res);
+                       goto _on_start_error;
+               }
+               res = target_register_timer_callback(esp32_apptrace_poll,
+                       cmd_data->poll_period,
+                       TARGET_TIMER_TYPE_PERIODIC,
+                       &s_at_cmd_ctx);
+               if (res != ERROR_OK) {
+                       command_print(cmd, "Failed to register target timer handler (%d)!", res);
+                       goto _on_start_error;
+               }
+       } else if (strcmp(argv[0], "stop") == 0) {
+               if (!s_at_cmd_ctx.running) {
+                       command_print(cmd, "Tracing is not running!");
+                       return ERROR_FAIL;
+               }
+               esp32_apptrace_cmd_stop(&s_at_cmd_ctx);
+               return ERROR_OK;
+       } else if (strcmp(argv[0], "status") == 0) {
+               if (s_at_cmd_ctx.running && duration_measure(&s_at_cmd_ctx.read_time) != 0)
+                       LOG_ERROR("Failed to measure trace read time!");
+               esp32_apptrace_print_stats(&s_at_cmd_ctx);
+               return ERROR_OK;
+       } else if (strcmp(argv[0], "dump") == 0) {
+               /* [dump outfile] - post-mortem dump without connection to targets */
+               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;
+               }
+               s_at_cmd_ctx.stop_tmo = 0.01;   /* use small stop tmo */
+               s_at_cmd_ctx.process_data = esp32_apptrace_process_data;
+               /* check for exit signal and command completion */
+               while (!openocd_is_shutdown_pending() && s_at_cmd_ctx.running) {
+                       res = esp32_apptrace_poll(&s_at_cmd_ctx);
+                       if (res != ERROR_OK) {
+                               LOG_ERROR("Failed to poll target for trace data (%d)!", res);
+                               break;
+                       }
+                       /* let registered timer callbacks to run */
+                       target_call_timer_callbacks();
+               }
+               if (s_at_cmd_ctx.running) {
+                       /* data processor is alive, so wait for all received blocks to be processed */
+                       res = esp32_apptrace_wait_tracing_finished(&s_at_cmd_ctx);
+                       if (res != ERROR_OK)
+                               LOG_ERROR("Failed to wait for pended blocks (%d)!", res);
+               }
+               esp32_apptrace_print_stats(&s_at_cmd_ctx);
+               res = esp32_apptrace_cmd_cleanup(&s_at_cmd_ctx);
+               if (res != ERROR_OK)
+                       command_print(cmd, "Failed to cleanup cmd ctx (%d)!", res);
+       } else {
+               command_print(cmd, "Invalid action '%s'!", argv[0]);
+       }
+
+       return res;
+
+_on_start_error:
+       s_at_cmd_ctx.running = 0;
+       esp32_apptrace_cmd_cleanup(&s_at_cmd_ctx);
+       return res;
+}
+
+COMMAND_HANDLER(esp32_cmd_apptrace)
+{
+       return esp32_cmd_apptrace_generic(CMD, ESP_APPTRACE_CMD_MODE_GEN, CMD_ARGV, CMD_ARGC);
+}
+
+const struct command_registration esp32_apptrace_command_handlers[] = {
+       {
+               .name = "apptrace",
+               .handler = esp32_cmd_apptrace,
+               .mode = COMMAND_EXEC,
+               .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>]",
+       },
+       COMMAND_REGISTRATION_DONE
+};

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)