This is an automated email from Gerrit.

"Erhan Kurubas <erhan.kuru...@espressif.com>" just uploaded a new patch set to 
Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7163

-- gerrit

commit 492945ab337a25b2333e1b84fca6c4f0257d47e6
Author: Erhan Kurubas <erhan.kuru...@espressif.com>
Date:   Thu Aug 25 15:38:34 2022 +0300

    target/espressif: add application tracing functionality over JTAG
    
    This feature allows to transfer arbitrary data between host and
    ESP32 via JTAG.
    
    The main use cases:
    
    1- Collecting application specific data
    2- Lightweight logging to the host
    3- System behaviour analysis with SEGGER SystemView
    4- Source code coverage
    
    Signed-off-by: Erhan Kurubas <erhan.kuru...@espressif.com>
    Change-Id: I95dee00ac22891fa326915a3fcac3c088cbb2afc

diff --git a/doc/openocd.texi b/doc/openocd.texi
index 0df25406ff..d12071e103 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -11149,6 +11149,40 @@ Stop current trace as started by the tracestart 
command.
 Dump trace memory to a file.
 @end deffn
 
+@section Espressif Specific Commands
+
+@deffn {Command} {esp apptrace} (start file://<outfile> [<poll_period> 
[<trace_size> [<stop_tmo> [<wait4halt> [<skip_size>]]]]])
+Starts
+@uref{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#application-level-tracing-library,
 application level tracing}.
+Data will be stored to specified destination.
+@itemize @bullet
+@item @code{poll_period} - trace data polling period in ms.
+@item @code{trace_size} - maximum trace data size.
+Tracing will be stopped automatically when that amount is reached.
+Use "-1" to disable the limitation.
+@item @code{stop_tmo} - Data receiption timeout in ms.
+Tracing will be stopped automatically when no data is received withn that 
period.
+@item @code{wait4halt} - if non-zero then wait for target to be halted before 
tracing start.
+@item @code{skip_size} - amount of tracing data to be skipped before writing 
it to destination.
+@end itemize
+@end deffn
+
+@deffn {Command} {esp apptrace} (stop)
+Stops tracing started with above command.
+@end deffn
+
+@deffn {Command} {esp apptrace} (status)
+Requests ongoing tracing status.
+@end deffn
+
+@deffn {Command} {esp apptrace} (dump file://<outfile>)
+Dumps tracing data from target buffer. It can be usefull to dump the latest 
data
+buffered on target for post-mortem analysis. For example when target starts 
tracing automatically
+w/o OpenOCD command and keeps only the latest data window which fit into the 
buffer.
+@uref{https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/app_trace.html#application-level-tracing-library,
 application level tracing}.
+Data will be stored to specified destination.
+@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 8367a38819..c1759ed77f 100644
--- a/src/target/espressif/Makefile.am
+++ b/src/target/espressif/Makefile.am
@@ -8,6 +8,10 @@ noinst_LTLIBRARIES += %D%/libespressif.la
        %D%/esp_xtensa_smp.h \
        %D%/esp_xtensa_semihosting.c \
        %D%/esp_xtensa_semihosting.h \
+       %D%/esp_xtensa_apptrace.c \
+       %D%/esp_xtensa_apptrace.h \
+       %D%/esp32_apptrace.c \
+       %D%/esp32_apptrace.h \
        %D%/esp32.c \
        %D%/esp32s2.c \
        %D%/esp32s3.c \
diff --git a/src/target/espressif/esp32.c b/src/target/espressif/esp32.c
index 8ad8bad835..76fa2483bc 100644
--- a/src/target/espressif/esp32.c
+++ b/src/target/espressif/esp32.c
@@ -440,6 +440,11 @@ static const struct command_registration 
esp32_command_handlers[] = {
        {
                .chain = esp_xtensa_smp_command_handlers,
        },
+       {
+               .name = "esp",
+               .usage = "",
+               .chain = esp32_apptrace_command_handlers,
+       },
        {
                .name = "esp32",
                .usage = "",
diff --git a/src/target/espressif/esp32_apptrace.c 
b/src/target/espressif/esp32_apptrace.c
new file mode 100644
index 0000000000..b5335d08f4
--- /dev/null
+++ b/src/target/espressif/esp32_apptrace.c
@@ -0,0 +1,1457 @@
+/* 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/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;
+};
+
+struct esp32_apptrace_block {
+       struct hlist_node node;
+       uint8_t *data;
+       uint32_t data_len;
+};
+
+static void *esp32_apptrace_data_processor(void *arg);
+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(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(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;
+       }
+       assert(port_sep > dest_name);
+       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)",
+                               (int)ai_it->ai_family,
+                               (int)ai_it->ai_socktype,
+                               (int)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(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(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[], int 
max_dests)
+{
+       for (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 hlist_head *head = &ctx->free_trace_blocks;
+       struct hlist_node *tmp, *pos = head->first;
+
+       while (pos && ({ tmp = pos->next; 1; })) {
+               cur = hlist_entry(pos, struct esp32_apptrace_block, node);
+               if (cur) {
+                       hlist_del(&cur->node);
+                       free(cur->data);
+                       free(cur);
+               }
+               pos = tmp;
+       }
+
+       head = &ctx->ready_trace_blocks;
+       pos = head->first;
+
+       while (pos && ({ tmp = pos->next; 1; })) {
+               cur = hlist_entry(pos, struct esp32_apptrace_block, node);
+               if (cur) {
+                       hlist_del(&cur->node);
+                       free(cur->data);
+                       free(cur);
+               }
+               pos = tmp;
+       }
+}
+
+struct esp32_apptrace_block *esp32_apptrace_free_block_get(struct 
esp32_apptrace_cmd_ctx *ctx)
+{
+       struct esp32_apptrace_block *block = NULL;
+
+       int res = pthread_mutex_lock(&ctx->trax_blocks_mux);
+       if (res == 0) {
+               if (!hlist_empty(&ctx->free_trace_blocks)) {
+                       /*get first */
+                       block = hlist_entry(ctx->free_trace_blocks.first, 
struct esp32_apptrace_block, node);
+                       hlist_del(&block->node);
+               }
+               res = pthread_mutex_unlock(&ctx->trax_blocks_mux);
+               if (res)
+                       LOG_ERROR("Failed to unlock blocks pool (%d)!", res);
+       }
+
+       return block;
+}
+
+static int esp32_apptrace_ready_block_put(struct esp32_apptrace_cmd_ctx *ctx, 
struct esp32_apptrace_block *block)
+{
+       int res = pthread_mutex_lock(&ctx->trax_blocks_mux);
+       if (res == 0) {
+               LOG_DEBUG("esp32_apptrace_ready_block_put");
+               /* add to ready blocks list */
+               INIT_HLIST_NODE(&block->node);
+               hlist_add_head(&block->node, &ctx->ready_trace_blocks);
+               res = pthread_mutex_unlock(&ctx->trax_blocks_mux);
+               if (res)
+                       LOG_ERROR("Failed to unlock blocks pool (%d)!", res);
+       } else {
+               LOG_ERROR("Failed to lock blocks pool (%d)!", res);
+       }
+
+       return res == 0 ? ERROR_OK : ERROR_FAIL;
+}
+
+static struct esp32_apptrace_block *esp32_apptrace_ready_block_get(struct 
esp32_apptrace_cmd_ctx *ctx)
+{
+       struct esp32_apptrace_block *block = NULL;
+       if (pthread_mutex_trylock(&ctx->trax_blocks_mux) == 0) {
+               if (!hlist_empty(&ctx->ready_trace_blocks)) {
+                       struct hlist_head *head = &ctx->ready_trace_blocks;
+                       struct hlist_node *pos = head->first;
+                       while (pos) {
+                               block = hlist_entry(pos, struct 
esp32_apptrace_block, node);
+                               pos = pos->next;
+                       }
+                       /* remove it from ready list */
+                       hlist_del(&block->node);
+               }
+               int res = pthread_mutex_unlock(&ctx->trax_blocks_mux);
+               if (res)
+                       LOG_ERROR("Failed to unlock blocks pool (%d)!", res);
+               /* TODO; return NULL and free block?? */
+       }
+       return block;
+}
+
+static int esp32_apptrace_block_free(struct esp32_apptrace_cmd_ctx *ctx, 
struct esp32_apptrace_block *block)
+{
+       int res = pthread_mutex_lock(&ctx->trax_blocks_mux);
+       if (res == 0) {
+               /* add to free blocks list */
+               INIT_HLIST_NODE(&block->node);
+               hlist_add_head(&block->node, &ctx->free_trace_blocks);
+               res = pthread_mutex_unlock(&ctx->trax_blocks_mux);
+               if (res)
+                       LOG_ERROR("Failed to unlock blocks pool (%d)!", res);
+       } else {
+               LOG_ERROR("Failed to lock blocks pool (%d)!", res);
+       }
+
+       return res == 0 ? ERROR_OK : ERROR_FAIL;
+}
+
+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 (!hlist_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 thread to stop */
+       ctx->running = 0;
+       /* wait for the processor thread to finish */
+       if (ctx->data_processor != (pthread_t)-1) {
+               void *thr_res;
+               int res = pthread_join(ctx->data_processor, (void *)&thr_res);
+               if (res)
+                       LOG_ERROR("Failed to join trace data processor thread 
(%d)!", res);
+               else
+                       LOG_INFO("Trace data processor thread exited with %ld", 
(long)thr_res);
+       }
+
+       return ERROR_OK;
+}
+
+/*********************************************************************
+*                          Trace commands
+**********************************************************************/
+
+int esp32_apptrace_cmd_ctx_init(struct target *target, struct 
esp32_apptrace_cmd_ctx *cmd_ctx, int mode)
+{
+       memset(cmd_ctx, 0, sizeof(struct esp32_apptrace_cmd_ctx));
+
+       cmd_ctx->data_processor = (pthread_t)-1;
+       cmd_ctx->mode = mode;
+       cmd_ctx->target_state = target->state;
+
+       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) {
+                               LOG_ERROR("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!");
+
+       const char *arch = target_get_gdb_arch(target);
+       if (arch) {
+               /* TODO: riscv is not supported yet */
+               if (strncmp(arch, "xtensa", 6) == 0) {
+                       cmd_ctx->hw = target_to_esp_xtensa(target)->apptrace.hw;
+               } else {
+                       LOG_ERROR("Unsupported target arch '%s'!", arch);
+                       return ERROR_FAIL;
+               }
+       } else {
+               LOG_ERROR("Unknown target arch!");
+               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) {
+               LOG_ERROR("Failed to get max trace block size!");
+               return ERROR_FAIL;
+       }
+       LOG_INFO("Total trace memory: %d bytes", cmd_ctx->max_trace_block_sz);
+
+       INIT_HLIST_HEAD(&cmd_ctx->ready_trace_blocks);
+       INIT_HLIST_HEAD(&cmd_ctx->free_trace_blocks);
+       for (unsigned int i = 0; i < ESP_APPTRACE_BLOCKS_POOL_SZ; i++) {
+               struct esp32_apptrace_block *block = malloc(sizeof(struct 
esp32_apptrace_block));
+               if (!block) {
+                       LOG_ERROR("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);
+                       LOG_ERROR("Failed to alloc trace buffer %" PRId32 " 
bytes!", cmd_ctx->max_trace_block_sz);
+                       esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+                       return ERROR_FAIL;
+               }
+               INIT_HLIST_NODE(&block->node);
+               hlist_add_head(&block->node, &cmd_ctx->free_trace_blocks);
+       }
+
+       cmd_ctx->running = 1;
+
+       int res = pthread_mutex_init(&cmd_ctx->trax_blocks_mux, NULL);
+       if (res) {
+               LOG_ERROR("Failed to blocks pool mux (%d)!", res);
+               esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+               return ERROR_FAIL;
+       }
+       if (cmd_ctx->mode != ESP_APPTRACE_CMD_MODE_SYNC) {
+               res = pthread_create(&cmd_ctx->data_processor,
+                       NULL,
+                       esp32_apptrace_data_processor,
+                       cmd_ctx);
+               if (res) {
+                       LOG_ERROR("Failed to start trace data processor thread 
(%d)!", res);
+                       cmd_ctx->data_processor = (pthread_t)-1;
+                       pthread_mutex_destroy(&cmd_ctx->trax_blocks_mux);
+                       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) {
+               LOG_ERROR("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)
+{
+       pthread_mutex_destroy(&cmd_ctx->trax_blocks_mux);
+       esp32_apptrace_blocks_pool_cleanup(cmd_ctx);
+       return ERROR_OK;
+}
+
+#define ESP32_APPTRACE_CMD_NUM_ARG_CHECK(_arg_, _start_, _end_)           \
+       do { \
+               if ((_arg_) == 0 && (_start_) == (_end_)) { \
+                       LOG_ERROR("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_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_data->max_len, argv[1], 
end);
+               if (argc > 2) {
+                       int32_t tmo = strtol(argv[2], &end, 10);
+                       ESP32_APPTRACE_CMD_NUM_ARG_CHECK(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_data->wait4halt, argv[3], end);
+                               if (argc > 4) {
+                                       cmd_data->skip_len = strtoul(argv[4], 
&end, 10);
+                                       
ESP32_APPTRACE_CMD_NUM_ARG_CHECK(cmd_data->skip_len, argv[4], end);
+                               }
+                       }
+               }
+       }
+}
+
+static int esp32_apptrace_core_id_get(uint8_t *hdr_buf)
+{
+       struct esp_apptrace_target2host_hdr tmp_hdr;
+       memcpy(&tmp_hdr, hdr_buf, sizeof(tmp_hdr));
+       return ESP32_APPTRACE_USER_BLOCK_CORE(tmp_hdr.block_sz);
+}
+
+static uint32_t esp32_apptrace_usr_block_len_get(uint8_t *hdr_buf, uint32_t 
*wr_len)
+{
+       struct esp_apptrace_target2host_hdr tmp_hdr;
+       memcpy(&tmp_hdr, hdr_buf, sizeof(tmp_hdr));
+       *wr_len = ESP32_APPTRACE_USER_BLOCK_LEN(tmp_hdr.wr_sz);
+       return ESP32_APPTRACE_USER_BLOCK_LEN(tmp_hdr.block_sz);
+}
+
+static int esp32_apptrace_cmd_init(struct target *target,
+       struct esp32_apptrace_cmd_ctx *cmd_ctx,
+       int mode,
+       const char **argv,
+       int argc)
+{
+       struct esp32_apptrace_cmd_data *cmd_data;
+
+       if (argc < 1) {
+               LOG_ERROR("Not enough args! Need trace data destination!");
+               return ERROR_FAIL;
+       }
+
+       int res = esp32_apptrace_cmd_ctx_init(target, cmd_ctx, 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 */
+               LOG_ERROR("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:
+       LOG_ERROR("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)
+{
+       int halted = 0;
+
+       LOG_USER("Wait for halt...");
+       while (!openocd_is_shutdown_pending()) {
+               int res = target_poll(target);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Failed to poll target (%d)!", res);
+                       return res;
+               }
+               if (target->state == TARGET_HALTED) {
+                       LOG_USER("%s: HALTED", target->cmd_name);
+                       break;
+               }
+               if (halted)
+                       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 != NULL) {
+                               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) {
+               LOG_ERROR("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) {
+                       LOG_ERROR("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) {
+                               LOG_ERROR("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 res;
+}
+
+uint8_t *esp_apptrace_usr_block_get(uint8_t *buffer, uint32_t *size)
+{
+       struct esp_apptrace_target2host_hdr tmp_hdr;
+       memcpy(&tmp_hdr, buffer, sizeof(tmp_hdr));
+
+       *size = tmp_hdr.wr_sz;
+
+       return buffer + sizeof(tmp_hdr);
+}
+
+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,
+               sizeof(buf_sz) / sizeof(buf_sz[0]),
+               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(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(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 void *esp32_apptrace_data_processor(void *arg)
+{
+       long res = ERROR_OK;
+       struct esp32_apptrace_cmd_ctx *ctx = (struct esp32_apptrace_cmd_ctx 
*)arg;
+
+       while (ctx->running) {
+               struct esp32_apptrace_block *block = 
esp32_apptrace_ready_block_get(ctx);
+               if (!block)
+                       continue;
+               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);
+                       break;
+               }
+               res = esp32_apptrace_block_free(ctx, block);
+               if (res != ERROR_OK) {
+                       ctx->running = 0;
+                       LOG_ERROR("Failed to free ready block!");
+                       break;
+               }
+       }
+
+       return (void *)res;     /*TODO /??? */
+}
+
+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 transfered
+                * 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 target *target, 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_OK;
+       enum target_state old_state;
+
+       if (argc < 1) {
+               LOG_ERROR("Action missed!");
+               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(target,
+                       &s_at_cmd_ctx,
+                       mode,
+                       &argv[1],
+                       argc - 1);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("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) {
+                               LOG_ERROR("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) {
+                       LOG_ERROR("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) {
+                       LOG_ERROR("Failed to register target timer handler 
(%d)!", res);
+                       return res;
+               }
+       } else if (strcmp(argv[0], "stop") == 0) {
+               if (!s_at_cmd_ctx.running) {
+                       LOG_WARNING("Tracing is not running!");
+                       return ERROR_FAIL;
+               }
+               esp32_apptrace_cmd_stop(&s_at_cmd_ctx);
+       } 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);
+       } else if (strcmp(argv[0], "dump") == 0) {
+               /* [dump outfile] - post-mortem dump without connection to 
targets */
+               res = esp32_apptrace_cmd_init(target,
+                       &s_at_cmd_ctx,
+                       mode,
+                       &argv[1],
+                       argc - 1);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("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 comand 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)
+                       LOG_ERROR("Failed to cleanup cmd ctx (%d)!", res);
+       } else {
+               LOG_ERROR("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(get_current_target(CMD_CTX),
+               ESP_APPTRACE_CMD_MODE_GEN,
+               CMD_ARGV,
+               CMD_ARGC);
+}
+
+#ifdef _WIN32
+#define DIR_SEPARATORS      ("\\/")
+#define IS_DIR_SEPARATOR(c) (c == '\\' || c == '/')
+#else
+#define DIR_SEPARATORS      ("/")
+#define IS_DIR_SEPARATOR(c) (c == '/')
+#endif
+
+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 file://<outfile> [poll_period [trace_size 
[stop_tmo [wait4halt [skip_size]]]]] | [stop] | [status] | [dump 
file://<outfile>]",
+       },
+       COMMAND_REGISTRATION_DONE
+};
diff --git a/src/target/espressif/esp32_apptrace.h 
b/src/target/espressif/esp32_apptrace.h
new file mode 100644
index 0000000000..cf790bbd33
--- /dev/null
+++ b/src/target/espressif/esp32_apptrace.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   ESP32 application trace module                                        *
+ *   Copyright (C) 2017-2019 Espressif Systems Ltd.                        *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESP32_APPTRACE_H
+#define OPENOCD_TARGET_ESP32_APPTRACE_H
+
+#include <pthread.h>
+#include <helper/command.h>
+#include <helper/time_support.h>
+#include <target/target.h>
+
+#define ESP32_APPTRACE_MAX_CORES_NUM 2
+
+struct esp32_apptrace_hw {
+       uint32_t max_block_id;
+       uint32_t (*max_block_size_get)(struct target *target);
+       int (*status_reg_read)(struct target *target, uint32_t *stat);
+       int (*ctrl_reg_write)(struct target *target,
+               uint32_t block_id,
+               uint32_t len,
+               bool conn,
+               bool data);
+       int (*ctrl_reg_read)(struct target *target,
+               uint32_t *block_id,
+               uint32_t *len,
+               bool *conn);
+       int (*data_len_read)(struct target *target,
+               uint32_t *block_id,
+               uint32_t *len);
+       int (*data_read)(struct target *target,
+               uint32_t size,
+               uint8_t *buffer,
+               uint32_t block_id,
+               bool ack);
+       uint32_t (*usr_block_max_size_get)(struct target *target);
+       int (*buffs_write)(struct target *target,
+               uint32_t bufs_num,
+               uint32_t buf_sz[],
+               const uint8_t *bufs[],
+               uint32_t block_id,
+               bool ack,
+               bool data);
+       int (*leave_trace_crit_section_start)(struct target *target);
+       int (*leave_trace_crit_section_stop)(struct target *target);
+};
+
+struct esp_apptrace_host2target_hdr {
+       uint16_t block_sz;
+};
+
+struct esp32_apptrace_dest {
+       void *priv;
+       int (*write)(void *priv, uint8_t *data, int size);
+       int (*clean)(void *priv);
+       bool log_progress;
+};
+
+struct esp32_apptrace_format {
+       uint32_t hdr_sz;
+       int (*core_id_get)(uint8_t *hdr_buf);
+       uint32_t (*usr_block_len_get)(uint8_t *hdr_buf, uint32_t *wr_len);
+};
+
+struct esp32_apptrace_cmd_stats {
+       uint32_t incompl_blocks;
+       uint32_t lost_bytes;
+       float min_blk_read_time;
+       float max_blk_read_time;
+       float min_blk_proc_time;
+       float max_blk_proc_time;
+};
+
+struct esp32_apptrace_cmd_ctx {
+       volatile int running;
+       int mode;
+       /* TODO: use subtargets from target arch info */
+       struct target *cpus[ESP32_APPTRACE_MAX_CORES_NUM];
+       /* TODO: use cores num from target */
+       unsigned int cores_num;
+       const struct esp32_apptrace_hw *hw;
+       enum target_state target_state;
+       uint32_t last_blk_id;
+       pthread_mutex_t trax_blocks_mux;
+       struct hlist_head free_trace_blocks;
+       struct hlist_head ready_trace_blocks;
+       uint32_t max_trace_block_sz;
+       pthread_t data_processor;
+       struct esp32_apptrace_format trace_format;
+       int (*process_data)(struct esp32_apptrace_cmd_ctx *ctx, unsigned int 
core_id, uint8_t *data, uint32_t data_len);
+       void (*auto_clean)(struct esp32_apptrace_cmd_ctx *ctx);
+       uint32_t tot_len;
+       uint32_t raw_tot_len;
+       float stop_tmo;
+       struct esp32_apptrace_cmd_stats stats;
+       struct duration read_time;
+       struct duration idle_time;
+       void *cmd_priv;
+};
+
+struct esp32_apptrace_cmd_data {
+       struct esp32_apptrace_dest data_dest;
+       uint32_t poll_period;
+       uint32_t max_len;
+       uint32_t skip_len;
+       bool wait4halt;
+};
+
+int esp32_apptrace_cmd_ctx_init(struct target *target, struct 
esp32_apptrace_cmd_ctx *cmd_ctx, int mode);
+int esp32_apptrace_cmd_ctx_cleanup(struct esp32_apptrace_cmd_ctx *cmd_ctx);
+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);
+int esp32_apptrace_dest_init(struct esp32_apptrace_dest dest[], const char 
*dest_paths[], unsigned int max_dests);
+int esp32_apptrace_dest_cleanup(struct esp32_apptrace_dest dest[], int 
max_dests);
+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);
+uint8_t *esp_apptrace_usr_block_get(uint8_t *buffer, uint32_t *size);
+
+extern const struct command_registration esp32_apptrace_command_handlers[];
+
+#endif /* OPENOCD_TARGET_ESP32_APPTRACE_H */
diff --git a/src/target/espressif/esp32s2.c b/src/target/espressif/esp32s2.c
index 4aef3791bf..03e582e58d 100644
--- a/src/target/espressif/esp32s2.c
+++ b/src/target/espressif/esp32s2.c
@@ -494,6 +494,11 @@ static const struct command_registration 
esp32s2_command_handlers[] = {
        {
                .chain = xtensa_command_handlers,
        },
+       {
+               .name = "esp",
+               .usage = "",
+               .chain = esp32_apptrace_command_handlers,
+       },
        {
                .name = "arm",
                .mode = COMMAND_ANY,
diff --git a/src/target/espressif/esp32s3.c b/src/target/espressif/esp32s3.c
index 0da8552a35..d7c9c017d6 100644
--- a/src/target/espressif/esp32s3.c
+++ b/src/target/espressif/esp32s3.c
@@ -364,6 +364,11 @@ static const struct command_registration 
esp32s3_command_handlers[] = {
                .usage = "",
                .chain = esp_xtensa_smp_command_handlers,
        },
+       {
+               .name = "esp",
+               .usage = "",
+               .chain = esp32_apptrace_command_handlers,
+       },
        {
                .name = "esp32",
                .usage = "",
diff --git a/src/target/espressif/esp_xtensa.c 
b/src/target/espressif/esp_xtensa.c
index 6a1b72ec48..697da3f6bb 100644
--- a/src/target/espressif/esp_xtensa.c
+++ b/src/target/espressif/esp_xtensa.c
@@ -12,6 +12,8 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <target/smp.h>
+#include "esp_xtensa.h"
+#include "esp_xtensa_apptrace.h"
 #include <target/register.h>
 #include "esp_xtensa.h"
 #include "esp_semihosting.h"
@@ -25,6 +27,7 @@ int esp_xtensa_init_arch_info(struct target *target,
        if (ret != ERROR_OK)
                return ret;
        esp_xtensa->semihost.ops = (struct esp_semihost_ops *)semihost_ops;
+       esp_xtensa->apptrace.hw = &esp_xtensa_apptrace_hw;
        return ERROR_OK;
 }
 
diff --git a/src/target/espressif/esp_xtensa.h 
b/src/target/espressif/esp_xtensa.h
index 1ad6c377f0..8807f0c325 100644
--- a/src/target/espressif/esp_xtensa.h
+++ b/src/target/espressif/esp_xtensa.h
@@ -12,10 +12,12 @@
 #include <target/xtensa/xtensa.h>
 #include "esp_xtensa.h"
 #include "esp_semihosting.h"
+#include "esp_xtensa_apptrace.h"
 
 struct esp_xtensa_common {
        struct xtensa xtensa;   /* must be the first element */
        struct esp_semihost_data semihost;
+       struct esp_xtensa_apptrace_info apptrace;
 };
 
 static inline struct esp_xtensa_common *target_to_esp_xtensa(struct target 
*target)
diff --git a/src/target/espressif/esp_xtensa_apptrace.c 
b/src/target/espressif/esp_xtensa_apptrace.c
new file mode 100644
index 0000000000..86b430168c
--- /dev/null
+++ b/src/target/espressif/esp_xtensa_apptrace.c
@@ -0,0 +1,498 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   Xtensa application tracing module for OpenOCD                         *
+ *   Copyright (C) 2017 Espressif Systems Ltd.                             *
+ ***************************************************************************/
+
+/*
+    How it works?
+    
https://github.com/espressif/esp-idf/blob/master/components/app_trace/port/xtensa/port.c#L8
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <helper/align.h>
+#include <target/xtensa/xtensa.h>
+#include <target/xtensa/xtensa_debug_module.h>
+#include "esp_xtensa_apptrace.h"
+
+/* TRAX is disabled, so we use its registers for our own purposes
+ * | 31..XXXXXX..24 | 23 .(host_connect). 23 | 22 .(host_data). 22| 
21..(block_id)..15 | 14..(block_len)..0 |
+ */
+#define XTENSA_APPTRACE_CTRL_REG                NARADR_DELAYCNT
+#define XTENSA_APPTRACE_BLOCK_ID_MSK            0x7FUL
+#define XTENSA_APPTRACE_BLOCK_ID_MAX            XTENSA_APPTRACE_BLOCK_ID_MSK
+/* if non-zero then apptrace code entered the critical section and the value 
is an address of the
+ * critical section's exit point */
+#define XTENSA_APPTRACE_STAT_REG                NARADR_TRIGGERPC
+
+#define XTENSA_APPTRACE_BLOCK_LEN_MSK           0x7FFFUL
+#define XTENSA_APPTRACE_BLOCK_LEN(_l_)          ((_l_) & 
XTENSA_APPTRACE_BLOCK_LEN_MSK)
+#define XTENSA_APPTRACE_BLOCK_LEN_GET(_v_)      ((_v_) & 
XTENSA_APPTRACE_BLOCK_LEN_MSK)
+#define XTENSA_APPTRACE_BLOCK_ID(_id_)          (((_id_) & 
XTENSA_APPTRACE_BLOCK_ID_MSK) << 15)
+#define XTENSA_APPTRACE_BLOCK_ID_GET(_v_)       (((_v_) >> 15) & 
XTENSA_APPTRACE_BLOCK_ID_MSK)
+#define XTENSA_APPTRACE_HOST_DATA               BIT(22)
+#define XTENSA_APPTRACE_HOST_CONNECT            BIT(23)
+
+static int esp_xtensa_apptrace_leave_crit_section_start(struct target *target);
+static int esp_xtensa_apptrace_leave_crit_section_stop(struct target *target);
+static int esp_xtensa_apptrace_buffs_write(struct target *target,
+       uint32_t bufs_num,
+       uint32_t buf_sz[],
+       const uint8_t *bufs[],
+       uint32_t block_id,
+       bool ack,
+       bool data);
+
+struct esp32_apptrace_hw esp_xtensa_apptrace_hw = {
+       .max_block_id = XTENSA_APPTRACE_BLOCK_ID_MAX,
+       .max_block_size_get = esp_xtensa_apptrace_block_max_size_get,
+       .status_reg_read = esp_xtensa_apptrace_status_reg_read,
+       .ctrl_reg_write = esp_xtensa_apptrace_ctrl_reg_write,
+       .ctrl_reg_read = esp_xtensa_apptrace_ctrl_reg_read,
+       .data_len_read = esp_xtensa_apptrace_data_len_read,
+       .data_read = esp_xtensa_apptrace_data_read,
+       .usr_block_max_size_get = esp_xtensa_apptrace_usr_block_max_size_get,
+       .buffs_write = esp_xtensa_apptrace_buffs_write,
+       .leave_trace_crit_section_start = 
esp_xtensa_apptrace_leave_crit_section_start,
+       .leave_trace_crit_section_stop = 
esp_xtensa_apptrace_leave_crit_section_stop,
+};
+
+uint32_t esp_xtensa_apptrace_block_max_size_get(struct target *target)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+       struct xtensa_trace_status trace_status;
+       struct xtensa_trace_config trace_config;
+       uint32_t max_trace_block_sz;
+
+       int res = xtensa_dm_trace_status_read(&xtensa->dbg_mod, &trace_status);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to read TRAX status (%d)!", res);
+               return 0;
+       }
+
+       max_trace_block_sz = BIT(((trace_status.stat >> 8) & 0x1f) - 2) * 4;
+       res = xtensa_dm_trace_config_read(&xtensa->dbg_mod, &trace_config);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to read TRAX config (%d)!", res);
+               return 0;
+       }
+       LOG_DEBUG("ctrl=0x%" PRIx32 " memadrstart=0x%" PRIx32 " memadrend=0x%" 
PRIx32 " traxadr=0x%" PRIx32,
+               trace_config.ctrl,
+               trace_config.memaddr_start,
+               trace_config.memaddr_end,
+               trace_config.addr);
+
+       return max_trace_block_sz;
+}
+
+uint32_t esp_xtensa_apptrace_usr_block_max_size_get(struct target *target)
+{
+       return esp_xtensa_apptrace_block_max_size_get(target) - sizeof(struct 
esp_apptrace_host2target_hdr);
+}
+
+int esp_xtensa_apptrace_data_len_read(struct target *target,
+       uint32_t *block_id,
+       uint32_t *len)
+{
+       return esp_xtensa_apptrace_ctrl_reg_read(target, block_id, len, NULL);
+}
+
+int esp_xtensa_apptrace_usr_block_write(struct target *target,
+       uint32_t block_id,
+       const uint8_t *data,
+       uint32_t size)
+{
+       return esp_apptrace_usr_block_write(&esp_xtensa_apptrace_hw, target, 
block_id, data, size);
+}
+
+static int esp_xtensa_apptrace_data_reverse_read(struct xtensa *xtensa,
+       uint32_t size,
+       uint8_t *buffer,
+       uint8_t *unal_bytes)
+{
+       int res = 0;
+       uint32_t rd_sz = ALIGN_UP(size, 4);
+
+       res = xtensa_queue_dbg_reg_write(xtensa, NARADR_TRAXADDR, 
(xtensa->core_config->trace.mem_sz - rd_sz) / 4);
+       if (res != ERROR_OK)
+               return res;
+       if (size & 0x3UL) {
+               res = xtensa_queue_dbg_reg_read(xtensa, NARADR_TRAXDATA, 
unal_bytes);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       for (unsigned int i = size / 4; i != 0; i--) {
+               res = xtensa_queue_dbg_reg_read(xtensa, NARADR_TRAXDATA, 
&buffer[(i - 1) * 4]);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       return ERROR_OK;
+}
+
+static int esp_xtensa_apptrace_data_normal_read(struct xtensa *xtensa,
+       uint32_t size,
+       uint8_t *buffer,
+       uint8_t *unal_bytes)
+{
+       int res = xtensa_queue_dbg_reg_write(xtensa, NARADR_TRAXADDR, 0);
+       if (res != ERROR_OK)
+               return res;
+       for (unsigned int i = 0; i < size / 4; i++) {
+               res = xtensa_queue_dbg_reg_read(xtensa, NARADR_TRAXDATA, 
&buffer[i * 4]);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       if (size & 0x3UL) {
+               res = xtensa_queue_dbg_reg_read(xtensa, NARADR_TRAXDATA, 
unal_bytes);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       return ERROR_OK;
+}
+
+int esp_xtensa_apptrace_data_read(struct target *target,
+       uint32_t size,
+       uint8_t *buffer,
+       uint32_t block_id,
+       bool ack)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+       int res;
+       uint32_t tmp = XTENSA_APPTRACE_HOST_CONNECT | 
XTENSA_APPTRACE_BLOCK_ID(block_id) |
+               XTENSA_APPTRACE_BLOCK_LEN(0);
+       uint8_t unal_bytes[4];
+
+       LOG_DEBUG("Read data on target (%s)", target_name(target));
+       if (xtensa->core_config->trace.reversed_mem_access)
+               res = esp_xtensa_apptrace_data_reverse_read(xtensa, size, 
buffer, unal_bytes);
+       else
+               res = esp_xtensa_apptrace_data_normal_read(xtensa, size, 
buffer, unal_bytes);
+       if (res != ERROR_OK)
+               return res;
+       if (ack) {
+               LOG_DEBUG("Ack block %d target (%s)!", block_id, 
target_name(target));
+               res = xtensa_queue_dbg_reg_write(xtensa, 
XTENSA_APPTRACE_CTRL_REG, tmp);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       res = jtag_execute_queue();
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to exec JTAG queue!");
+               return res;
+       }
+       if (size & 0x3UL) {
+               /* copy the last unaligned bytes */
+               memcpy(buffer + size - (size & 0x3UL), unal_bytes, size & 
0x3UL);
+       }
+       return ERROR_OK;
+}
+
+int esp_xtensa_apptrace_ctrl_reg_write(struct target *target,
+       uint32_t block_id,
+       uint32_t len,
+       bool conn,
+       bool data)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+       uint32_t tmp = (conn ? XTENSA_APPTRACE_HOST_CONNECT : 0) |
+               (data ? XTENSA_APPTRACE_HOST_DATA : 0) | 
XTENSA_APPTRACE_BLOCK_ID(block_id) |
+               XTENSA_APPTRACE_BLOCK_LEN(len);
+
+       xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       int res = jtag_execute_queue();
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to exec JTAG queue!");
+               return res;
+       }
+
+       return ERROR_OK;
+}
+
+int esp_xtensa_apptrace_ctrl_reg_read(struct target *target,
+       uint32_t *block_id,
+       uint32_t *len,
+       bool *conn)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+       uint8_t tmp[4];
+
+       xtensa_queue_dbg_reg_read(xtensa, XTENSA_APPTRACE_CTRL_REG, tmp);
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       int res = jtag_execute_queue();
+       if (res != ERROR_OK)
+               return res;
+       uint32_t val = buf_get_u32(tmp, 0, 32);
+       if (block_id)
+               *block_id = XTENSA_APPTRACE_BLOCK_ID_GET(val);
+       if (len)
+               *len = XTENSA_APPTRACE_BLOCK_LEN_GET(val);
+       if (conn)
+               *conn = val & XTENSA_APPTRACE_HOST_CONNECT;
+       return ERROR_OK;
+}
+
+int esp_xtensa_apptrace_status_reg_read(struct target *target, uint32_t *stat)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+       uint8_t tmp[4];
+
+       xtensa_queue_dbg_reg_read(xtensa, XTENSA_APPTRACE_STAT_REG, tmp);
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       int res = jtag_execute_queue();
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to exec JTAG queue!");
+               return res;
+       }
+       *stat = buf_get_u32(tmp, 0, 32);
+       return ERROR_OK;
+}
+
+int esp_xtensa_apptrace_status_reg_write(struct target *target, uint32_t stat)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+
+       xtensa_queue_dbg_reg_write(xtensa, XTENSA_APPTRACE_STAT_REG, stat);
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       int res = jtag_execute_queue();
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to exec JTAG queue!");
+               return res;
+       }
+       return ERROR_OK;
+}
+
+static int esp_xtensa_swdbg_activate(struct target *target, int enab)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+
+       xtensa_queue_dbg_reg_write(xtensa, enab ? NARADR_DCRSET : 
NARADR_DCRCLR, OCDDCR_DEBUGSWACTIVE);
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       int res = jtag_execute_queue();
+       if (res != ERROR_OK) {
+               LOG_ERROR("%s: writing DCR failed!", target->cmd_name);
+               return ERROR_FAIL;
+       }
+
+       return ERROR_OK;
+}
+
+static int esp_xtensa_apptrace_leave_crit_section_start(struct target *target)
+{
+       /* TODO: not sure that we need this, but it seems that we fail to leave 
tracing critical
+        *section w/o this */
+       int res = esp_xtensa_swdbg_activate(target, 1 /*enable*/);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to activate SW debug (%d)!", res);
+               return res;
+       }
+       return ERROR_OK;
+}
+
+static int esp_xtensa_apptrace_leave_crit_section_stop(struct target *target)
+{
+       int res = esp_xtensa_swdbg_activate(target, 0 /*disable*/);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to activate SW debug (%d)!", res);
+               return res;
+       }
+       return ERROR_OK;
+}
+
+/* TODO: for now this function assumes the same endianness on the target and 
host */
+static int esp_xtensa_apptrace_queue_reverse_write(struct xtensa *xtensa, 
uint32_t bufs_num,
+       uint32_t buf_sz[], const uint8_t *bufs[])
+{
+       int res = ERROR_OK;
+       uint32_t cached_bytes = 0, total_sz = 0;
+       union {
+               uint8_t data8[4];
+               uint32_t data32;
+       } dword_cache;
+
+       for (uint32_t i = 0; i < bufs_num; i++)
+               total_sz += buf_sz[i];
+       if (total_sz & 0x3UL) {
+               cached_bytes = 4 - (total_sz & 0x3UL);
+               total_sz = ALIGN_UP(total_sz, 4);
+       }
+       dword_cache.data32 = 0;
+       xtensa_queue_dbg_reg_write(xtensa, NARADR_TRAXADDR, 
(xtensa->core_config->trace.mem_sz - total_sz) / 4);
+       for (uint32_t i = bufs_num; i > 0; i--) {
+               uint32_t bsz = buf_sz[i - 1];
+               const uint8_t *cur_buf = &bufs[i - 1][bsz];
+               uint32_t bytes_to_cache;
+               /* if there are cached bytes from the previous buffer, combine 
them with the last
+                * from the current buffer */
+               if (cached_bytes) {
+                       if ((cached_bytes + bsz) < sizeof(uint32_t))
+                               bytes_to_cache = bsz;
+                       else
+                               bytes_to_cache = sizeof(uint32_t) - 
cached_bytes;
+                       memcpy(&dword_cache.data8[sizeof(uint32_t) - 
cached_bytes - bytes_to_cache],
+                               cur_buf - bytes_to_cache,
+                               bytes_to_cache);
+                       cached_bytes += bytes_to_cache;
+                       if (cached_bytes < sizeof(uint32_t))
+                               continue;
+                       res = xtensa_queue_dbg_reg_write(xtensa, 
NARADR_TRAXDATA, dword_cache.data32);
+                       if (res != ERROR_OK)
+                               return res;
+                       bsz -= bytes_to_cache;
+                       cur_buf -= bytes_to_cache;
+                       dword_cache.data32 = 0;
+                       cached_bytes = 0;
+               }
+               /* write full dwords */
+               for (unsigned int k = bsz; k >= sizeof(uint32_t); k -= 
sizeof(uint32_t)) {
+                       uint32_t temp = 0;
+                       memcpy(&temp, cur_buf - sizeof(uint32_t), 
sizeof(uint32_t));
+                       res = xtensa_queue_dbg_reg_write(xtensa, 
NARADR_TRAXDATA, temp);
+                       if (res != ERROR_OK)
+                               return res;
+                       cur_buf -= sizeof(uint32_t);
+               }
+               /* if there are bytes to be cached (1..3) */
+               bytes_to_cache = bsz & 0x3UL;
+               if (bytes_to_cache > 0) {
+                       if (bytes_to_cache + cached_bytes >= sizeof(uint32_t)) {
+                               /* filling the cache buffer from the end to 
beginning */
+                               uint32_t to_copy = sizeof(uint32_t) - 
cached_bytes;
+                               memcpy(&dword_cache.data8, cur_buf - to_copy, 
to_copy);
+                               /* write full word of cached bytes */
+                               res = xtensa_queue_dbg_reg_write(xtensa, 
NARADR_TRAXDATA, dword_cache.data32);
+                               if (res != ERROR_OK)
+                                       return res;
+                               /* cache remaining bytes */
+                               dword_cache.data32 = 0;
+                               cur_buf -= to_copy;
+                               to_copy = bytes_to_cache + cached_bytes - 
sizeof(uint32_t);
+                               memcpy(&dword_cache.data8[sizeof(uint32_t) - 
to_copy], cur_buf - to_copy, to_copy);
+                               cached_bytes = to_copy;
+                       } else {
+                               /* filling the cache buffer from the end to 
beginning */
+                               memcpy(&dword_cache.data8[sizeof(uint32_t) - 
cached_bytes - bytes_to_cache],
+                                       cur_buf - bytes_to_cache,
+                                       bytes_to_cache);
+                               cached_bytes += bytes_to_cache;
+                       }
+               }
+       }
+       return ERROR_OK;
+}
+
+/* TODO: for now this function assumes the same endianness on the target and 
host */
+static int esp_xtensa_apptrace_queue_normal_write(struct xtensa *xtensa, 
uint32_t bufs_num,
+       uint32_t buf_sz[], const uint8_t *bufs[])
+{
+       int res = ERROR_OK;
+       uint32_t cached_bytes = 0;
+       union {
+               uint8_t data8[4];
+               uint32_t data32;
+       } dword_cache;
+
+       /* | 1 |   2   | 1 | 2     |       4       |.......|
+        * |       4       |       4       |       4       | */
+       dword_cache.data32 = 0;
+       xtensa_queue_dbg_reg_write(xtensa, NARADR_TRAXADDR, 0);
+       for (unsigned int i = 0; i < bufs_num; i++) {
+               uint32_t bsz = buf_sz[i];
+               const uint8_t *cur_buf = bufs[i];
+               uint32_t bytes_to_cache;
+               /* if there are cached bytes from the previous buffer, combine 
them with the last
+                * from the current buffer */
+               if (cached_bytes) {
+                       if ((cached_bytes + bsz) < sizeof(uint32_t))
+                               bytes_to_cache = bsz;
+                       else
+                               bytes_to_cache = sizeof(uint32_t) - 
cached_bytes;
+                       memcpy(&dword_cache.data8[cached_bytes], cur_buf, 
bytes_to_cache);
+                       cached_bytes += bytes_to_cache;
+                       if (cached_bytes < sizeof(uint32_t))
+                               continue;
+                       res = xtensa_queue_dbg_reg_write(xtensa, 
NARADR_TRAXDATA, dword_cache.data32);
+                       if (res != ERROR_OK)
+                               return res;
+                       bsz -= bytes_to_cache;
+                       cur_buf += bytes_to_cache;
+                       dword_cache.data32 = 0;
+                       cached_bytes = 0;
+               }
+               /* write full dwords */
+               for (unsigned int k = 0; (k + sizeof(uint32_t)) <= bsz; k += 
sizeof(uint32_t)) {
+                       uint32_t temp = 0;
+                       memcpy(&temp, cur_buf, sizeof(uint32_t));
+                       res = xtensa_queue_dbg_reg_write(xtensa, 
NARADR_TRAXDATA, temp);
+                       if (res != ERROR_OK)
+                               return res;
+                       cur_buf += sizeof(uint32_t);
+               }
+               /* if there are bytes to be cached (1..3) */
+               bytes_to_cache = bsz & 0x3UL;
+               if (bytes_to_cache > 0) {
+                       if (bytes_to_cache + cached_bytes >= sizeof(uint32_t)) {
+                               memcpy(&dword_cache.data8[cached_bytes], 
cur_buf, sizeof(uint32_t) - cached_bytes);
+                               /* write full word of cached bytes */
+                               res = xtensa_queue_dbg_reg_write(xtensa, 
NARADR_TRAXDATA, dword_cache.data32);
+                               if (res != ERROR_OK)
+                                       return res;
+                               /* cache remaining bytes */
+                               dword_cache.data32 = 0;
+                               cur_buf += sizeof(uint32_t) - cached_bytes;
+                               cached_bytes = bytes_to_cache + cached_bytes - 
sizeof(uint32_t);
+                               memcpy(&dword_cache.data8[0], cur_buf, 
cached_bytes);
+                       } else {
+                               memcpy(&dword_cache.data8[cached_bytes], 
cur_buf, bytes_to_cache);
+                               cached_bytes += bytes_to_cache;
+                       }
+               }
+       }
+       if (cached_bytes) {
+               /* write remaining cached bytes */
+               res = xtensa_queue_dbg_reg_write(xtensa, NARADR_TRAXDATA, 
dword_cache.data32);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       return ERROR_OK;
+}
+
+static int esp_xtensa_apptrace_buffs_write(struct target *target,
+       uint32_t bufs_num,
+       uint32_t buf_sz[],
+       const uint8_t *bufs[],
+       uint32_t block_id,
+       bool ack,
+       bool data)
+{
+       struct xtensa *xtensa = target_to_xtensa(target);
+       int res = ERROR_OK;
+       uint32_t tmp = XTENSA_APPTRACE_HOST_CONNECT |
+               (data ? XTENSA_APPTRACE_HOST_DATA : 0) | 
XTENSA_APPTRACE_BLOCK_ID(block_id) |
+               XTENSA_APPTRACE_BLOCK_LEN(0);
+
+       if (xtensa->core_config->trace.reversed_mem_access)
+               res = esp_xtensa_apptrace_queue_reverse_write(xtensa, bufs_num, 
buf_sz, bufs);
+       else
+               res = esp_xtensa_apptrace_queue_normal_write(xtensa, bufs_num, 
buf_sz, bufs);
+       if (res != ERROR_OK)
+               return res;
+       if (ack) {
+               LOG_DEBUG("Ack block %" PRId32 " on target (%s)!", block_id, 
target_name(target));
+               res = xtensa_queue_dbg_reg_write(xtensa, 
XTENSA_APPTRACE_CTRL_REG, tmp);
+               if (res != ERROR_OK)
+                       return res;
+       }
+       xtensa_dm_queue_tdi_idle(&xtensa->dbg_mod);
+       res = jtag_execute_queue();
+       if (res != ERROR_OK) {
+               LOG_ERROR("Failed to exec JTAG queue!");
+               return res;
+       }
+       return ERROR_OK;
+}
diff --git a/src/target/espressif/esp_xtensa_apptrace.h 
b/src/target/espressif/esp_xtensa_apptrace.h
new file mode 100644
index 0000000000..0a9be731fe
--- /dev/null
+++ b/src/target/espressif/esp_xtensa_apptrace.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   Xtensa application tracing module for OpenOCD                         *
+ *   Copyright (C) 2017 Espressif Systems Ltd.                             *
+ ***************************************************************************/
+
+#ifndef OPENOCD_TARGET_ESP_XTENSA_APPTRACE_H
+#define OPENOCD_TARGET_ESP_XTENSA_APPTRACE_H
+
+#include "esp32_apptrace.h"
+
+struct esp_xtensa_apptrace_info {
+       const struct esp32_apptrace_hw *hw;
+};
+
+extern struct esp32_apptrace_hw esp_xtensa_apptrace_hw;
+
+int esp_xtensa_apptrace_data_len_read(struct target *target, uint32_t 
*block_id, uint32_t *len);
+int esp_xtensa_apptrace_data_read(struct target *target,
+       uint32_t size,
+       uint8_t *buffer,
+       uint32_t block_id,
+       bool ack);
+int esp_xtensa_apptrace_ctrl_reg_read(struct target *target, uint32_t 
*block_id, uint32_t *len, bool *conn);
+int esp_xtensa_apptrace_ctrl_reg_write(struct target *target,
+       uint32_t block_id,
+       uint32_t len,
+       bool conn,
+       bool data);
+int esp_xtensa_apptrace_status_reg_write(struct target *target, uint32_t stat);
+int esp_xtensa_apptrace_status_reg_read(struct target *target, uint32_t *stat);
+uint32_t esp_xtensa_apptrace_block_max_size_get(struct target *target);
+uint32_t esp_xtensa_apptrace_usr_block_max_size_get(struct target *target);
+int esp_xtensa_apptrace_usr_block_write(struct target *target, uint32_t 
block_id, const uint8_t *data, uint32_t size);
+
+#endif /* OPENOCD_TARGET_ESP_XTENSA_APPTRACE_H */

-- 

Reply via email to