Merge branch 'tracing_test_fixes' into 'master'

Tracing test fixes

See merge request espressif/esp-idf!51601
This commit is contained in:
Alexey Gerenkov
2026-08-13 16:56:07 +08:00
16 changed files with 215 additions and 88 deletions
+11 -13
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -75,7 +75,7 @@ static esp_err_t esp_apptrace_membufs_swap(esp_apptrace_membufs_proto_data_t *pr
esp_err_t res = proto->hw->swap_start(proto->state.in_block);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to swap to new block: %d", res);
ESP_APPTRACE_LOGV("Failed to swap to new block: %s", esp_err_to_name(res));
return res;
}
@@ -100,7 +100,7 @@ static esp_err_t esp_apptrace_membufs_swap(esp_apptrace_membufs_proto_data_t *pr
*(p - 8), *(p - 7), *(p - 6), *(p - 5), *(p - 4), *(p - 3), *(p - 2), *(p - 1));
uint32_t sz = esp_apptrace_membufs_down_buffer_write_nolock(proto, (uint8_t *)(hdr + 1), hdr->block_sz);
if (sz != hdr->block_sz) {
ESP_APPTRACE_LOGE("Failed to write %" PRIu32 " bytes to down buffer (%" PRIu16 " %" PRIu32 ")!",
ESP_APPTRACE_LOGE("Failed to write %" PRIu32 " bytes to down buffer (%" PRIu32 " %" PRIu32 ")!",
hdr->block_sz - sz, hdr->block_sz, sz);
}
hdr->block_sz = 0;
@@ -149,20 +149,18 @@ uint8_t *esp_apptrace_membufs_down_buffer_get(esp_apptrace_membufs_proto_data_t
}
break;
}
// may need to flush
// may need to flush to expose host down-channel data
if (proto->hw->host_data_pending()) {
ESP_APPTRACE_LOGD("force flush");
int res = esp_apptrace_membufs_swap_waitus(proto, tmo);
if (res != ESP_OK) {
ESP_APPTRACE_LOGE("Failed to switch to another block to recv data from host!");
/*do not return error because data can be in down buffer already*/
}
} else {
// check tmo only if there is no data from host
int res = esp_apptrace_tmo_check(tmo);
if (res != ESP_OK) {
return NULL;
if (res == ESP_OK) {
continue; /* re-check rb_down */
}
ESP_APPTRACE_LOGE("Failed to switch to another block to recv data from host!");
}
/* Check the timeout even while host data is pending so a zero timeout returns immediately instead of spinning. */
if (esp_apptrace_tmo_check(tmo) != ESP_OK) {
return NULL;
}
}
return ptr;
+1 -1
View File
@@ -380,7 +380,7 @@ void esp_panic_handler(panic_info_t *info)
// then only print up to details. Users should be able to probe for the other information
// in debug mode.
#if CONFIG_ESP_DEBUG_OCDAWARE
if (esp_cpu_dbgr_is_attached()) {
if (esp_cpu_dbgr_is_attached() && g_panic_entry_count[esp_cpu_get_core_id()] <= 1) {
char *panic_reason_str = NULL;
if (info->pseudo_excause) {
panic_reason_str = (char *)info->reason;
+3 -2
View File
@@ -76,12 +76,13 @@ menu "ESP Trace Configuration"
config ESP_TRACE_USJ_TX_BUFFER_SIZE
int "TX buffer size"
default 2048
range 256 32768
range 2048 32768
help
Size of the TX ring buffer for USB Serial JTAG trace transport.
Larger buffer allows more trace data to be queued before blocking.
Note: Buffer size must be a power of 2.
Note: Buffer size must be a power of 2. Minimum is 2048 since
smaller buffers overflow easily on the USJ transport.
endmenu
@@ -15,6 +15,8 @@
* is responsible for serializing access using esp_trace_lock_init(), esp_trace_lock_take(),
* and esp_trace_lock_give(). All transport operations (read, write, flush_nolock) are
* invoked while the encoder holds the lock, so no transport-level locking is required.
*
* Logging: use ESP_EARLY_LOGx only, ESP_LOGx is not safe in ISR context.
*/
#include <stdlib.h>
@@ -29,6 +31,7 @@
#include "esp_cpu.h"
#include "esp_attr.h"
#include "esp_rom_caps.h"
#include "esp_rom_sys.h"
#include "esp_heap_caps.h"
#include "esp_private/periph_ctrl.h"
#include "hal/usb_serial_jtag_ll.h"
@@ -51,7 +54,6 @@ static const char *TAG = "usj_transport";
*/
typedef enum {
USJ_TX_IDLE,
USJ_TX_SHORT_PENDING,
USJ_TX_ZLP_PENDING,
} usj_tx_state_t;
@@ -67,11 +69,8 @@ typedef struct {
uint32_t flush_thresh; ///< Flush threshold in bytes
} usj_ctx_t;
/*
* flush_nolock() runs with interrupts masked, so normal flushes use a short
* fixed timeout even if an encoder requests a longer one.
*/
#define USJ_FLUSH_TIMEOUT_US (1000) // 1 ms
/* Default flush timeout, used on non-masked paths (e.g. panic handler). */
#define USJ_FLUSH_TIMEOUT_US (1000000) // 1 s
#define USJ_FLUSH_THRESH_BYTES (0) // 0 bytes
#define USJ_FLUSH_MAX_INTR_MASKED_US (2000) // 2 ms
#define USJ_FLUSH_POLL_STEP_US (100) // delay between no-progress polls
@@ -84,7 +83,7 @@ typedef struct {
static uint32_t usj_write_fifo(usj_ctx_t *ctx, esp_trace_rb_t *rb)
{
if (!usb_serial_jtag_ll_txfifo_writable()) {
/* FIFO is full, no blocking */
/* Previous packet is not sent yet */
return 0;
}
@@ -97,17 +96,21 @@ static uint32_t usj_write_fifo(usj_ctx_t *ctx, esp_trace_rb_t *rb)
uint32_t written = usb_serial_jtag_ll_write_txfifo(ptr, to_send);
esp_trace_rb_consume(rb, written);
ctx->tx_state = usb_serial_jtag_ll_txfifo_writable() ? USJ_TX_SHORT_PENDING : USJ_TX_ZLP_PENDING;
/* Checked before wr_done, which also clears the writable flag */
ctx->tx_state = usb_serial_jtag_ll_txfifo_writable() ? USJ_TX_IDLE : USJ_TX_ZLP_PENDING;
/* Send the packet. A no-op if the HW already flushed a full FIFO. */
usb_serial_jtag_ll_txfifo_flush();
return written;
}
static uint32_t usj_fill_txfifo(usj_ctx_t *ctx, bool commit_short)
static uint32_t usj_fill_txfifo(usj_ctx_t *ctx)
{
esp_trace_rb_t *rb = &ctx->tx_ring;
uint32_t total_written = 0;
while (esp_trace_rb_data_len(rb) > 0 && usb_serial_jtag_ll_txfifo_writable()) {
while (esp_trace_rb_data_len(rb) > 0) {
uint32_t written = usj_write_fifo(ctx, rb);
if (written == 0) {
break;
@@ -115,11 +118,6 @@ static uint32_t usj_fill_txfifo(usj_ctx_t *ctx, bool commit_short)
total_written += written;
}
if (commit_short && ctx->tx_state == USJ_TX_SHORT_PENDING) {
usb_serial_jtag_ll_txfifo_flush();
ctx->tx_state = USJ_TX_IDLE;
}
return total_written;
}
@@ -127,7 +125,15 @@ static void usj_read_rx_fifo(usj_ctx_t *ctx)
{
uint8_t tmp[USJ_HW_FIFO_SIZE];
while (usb_serial_jtag_ll_rxfifo_data_available()) {
uint32_t n = usb_serial_jtag_ll_read_rxfifo(tmp, sizeof(tmp));
/* Read only what fits, the rest stays in the HW FIFO */
uint32_t space = esp_trace_rb_free_len(&ctx->rx_ring);
if (space == 0) {
break;
}
if (space > sizeof(tmp)) {
space = sizeof(tmp);
}
uint32_t n = usb_serial_jtag_ll_read_rxfifo(tmp, space);
if (n == 0) {
break;
}
@@ -185,22 +191,23 @@ static esp_err_t usj_init(esp_trace_transport_t *tp, const void *tp_cfg)
/* Initialize TX ring buffer */
esp_err_t ret = esp_trace_rb_init(&ctx->tx_ring, CONFIG_ESP_TRACE_USJ_TX_BUFFER_SIZE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize TX ring buffer");
ESP_EARLY_LOGE(TAG, "Failed to initialize TX ring buffer");
goto err_ctx;
}
/* Initialize RX ring buffer to capture host commands */
ret = esp_trace_rb_init(&ctx->rx_ring, USJ_RX_BUFFER_SIZE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize RX ring buffer");
ESP_EARLY_LOGE(TAG, "Failed to initialize RX ring buffer");
goto err_tx_ring;
}
#if ESP_ROM_HAS_ETS_PRINTF_BUG
/* Make sure no printf output is sent to USB-Serial-JTAG */
#if ESP_ROM_HAS_ETS_PRINTF_BUG
extern bool g_usb_print;
g_usb_print = false;
#endif
esp_rom_install_channel_putc(2, NULL);
}
@@ -267,13 +274,15 @@ static esp_err_t usj_write(esp_trace_transport_t *tp, const void *data, size_t s
/* Read any new RX data into the RX ring buffer to avoid losing host commands in case of heavy trace output */
usj_read_rx_fifo(ctx);
/* Add data to TX ring buffer */
esp_trace_rb_put(rb, (const uint8_t *)data, size);
esp_err_t ret = esp_trace_rb_put(rb, (const uint8_t *)data, size);
if (ret != ESP_OK) {
/* Free what the HW can take, then retry before dropping the record */
usj_fill_txfifo(ctx);
ret = esp_trace_rb_put(rb, (const uint8_t *)data, size);
}
usj_fill_txfifo(ctx);
/* Try to move data to HW FIFO immediately, without forcing a short packet. */
usj_fill_txfifo(ctx, false);
return ESP_OK;
return ret;
}
static esp_err_t usj_down_buffer_config(esp_trace_transport_t *tp, uint8_t *buf, uint32_t size)
@@ -292,7 +301,7 @@ static esp_err_t usj_flush_with_timeout(usj_ctx_t *ctx, uint32_t tmo_us)
esp_trace_rb_t *rb = &ctx->tx_ring;
uint32_t pending = esp_trace_rb_data_len(rb);
if (pending < ctx->flush_thresh && ctx->tx_state == USJ_TX_IDLE) {
if ((pending == 0 || pending < ctx->flush_thresh) && ctx->tx_state == USJ_TX_IDLE) {
return ESP_OK;
}
@@ -300,9 +309,11 @@ static esp_err_t usj_flush_with_timeout(usj_ctx_t *ctx, uint32_t tmo_us)
esp_trace_tmo_init(&timeout, tmo_us);
while (esp_trace_rb_data_len(rb) > 0 || ctx->tx_state != USJ_TX_IDLE) {
uint32_t written = usj_fill_txfifo(ctx, true);
uint32_t written = usj_fill_txfifo(ctx);
if (ctx->tx_state == USJ_TX_ZLP_PENDING && usb_serial_jtag_ll_txfifo_writable()) {
/* Ring is empty: send a ZLP if the last packet filled the endpoint */
if (esp_trace_rb_data_len(rb) == 0 && ctx->tx_state == USJ_TX_ZLP_PENDING
&& usb_serial_jtag_ll_txfifo_writable()) {
usb_serial_jtag_ll_txfifo_flush();
ctx->tx_state = USJ_TX_IDLE;
continue;
@@ -363,7 +374,7 @@ static esp_err_t usj_set_config(esp_trace_transport_t *tp, esp_trace_transport_c
ctx->flush_thresh = *(const uint32_t *)value;
return ESP_OK;
default:
ESP_LOGE(TAG, "Key %d is not supported", key);
ESP_EARLY_LOGE(TAG, "Key %d is not supported", key);
return ESP_ERR_NOT_SUPPORTED;
}
}
@@ -387,7 +398,7 @@ static esp_err_t usj_get_config(esp_trace_transport_t *tp, esp_trace_transport_c
*(uint32_t *)value = ctx->flush_thresh;
return ESP_OK;
default:
ESP_LOGE(TAG, "Key %d is not supported", key);
ESP_EARLY_LOGE(TAG, "Key %d is not supported", key);
return ESP_ERR_NOT_SUPPORTED;
}
}
+12 -2
View File
@@ -157,12 +157,22 @@ static inline uint32_t esp_trace_rb_data_len(const esp_trace_rb_t *rb)
}
/**
* @brief Write data into the ring buffer (overwrites oldest data if full)
* @brief Get number of bytes that can still be written to the ring buffer
*/
static inline uint32_t esp_trace_rb_free_len(const esp_trace_rb_t *rb)
{
return rb->max_size - rb->count;
}
/**
* @brief Write data into the ring buffer, all of it or none
*
* Data already in the buffer is never overwritten.
*
* @param rb Ring buffer
* @param data Source data
* @param len Number of bytes to write
* @return ESP_OK always (data is always accepted; oldest data may be dropped)
* @return ESP_OK on success, ESP_ERR_NO_MEM if the data does not fit
*/
esp_err_t esp_trace_rb_put(esp_trace_rb_t *rb, const uint8_t *data, uint32_t len);
+4 -4
View File
@@ -195,10 +195,10 @@ esp_err_t esp_trace_rb_init(esp_trace_rb_t *rb, uint32_t size)
esp_err_t esp_trace_rb_put(esp_trace_rb_t *rb, const uint8_t *data, uint32_t len)
{
/* Drop oldest data if needed to make room */
uint32_t free_len = rb->max_size - rb->count;
if (len > free_len) {
rb_advance_tail(rb, len - free_len);
/* Drop the new record instead of overwriting old ones, so that the records
* in the buffer stay complete */
if (len > rb->max_size - rb->count) {
return ESP_ERR_NO_MEM;
}
uint32_t head = rb->head;