mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-28 01:04:48 +03:00
feat(esp_trace): add example to demonstrate external lib integration
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
set(src_dirs
|
||||
"src"
|
||||
)
|
||||
|
||||
set(include_dirs
|
||||
"include"
|
||||
)
|
||||
|
||||
set(priv_requires
|
||||
"esp_trace"
|
||||
)
|
||||
|
||||
if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
|
||||
idf_component_register(SRC_DIRS ${src_dirs}
|
||||
INCLUDE_DIRS ${include_dirs}
|
||||
PRIV_REQUIRES ${priv_requires}
|
||||
WHOLE_ARCHIVE TRUE)
|
||||
|
||||
idf_component_get_property(freertos_lib freertos COMPONENT_LIB)
|
||||
target_include_directories(${freertos_lib} INTERFACE ${include_dirs})
|
||||
else()
|
||||
idf_component_register(PRIV_REQUIRES ${priv_requires})
|
||||
endif()
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "trace_FreeRTOS.h"
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/*
|
||||
* External Trace Library - FreeRTOS trace hooks
|
||||
*
|
||||
* This header is pulled in (via esp_trace_freertos_impl.h) from FreeRTOSConfig.h
|
||||
* Do NOT include any FreeRTOS header here — keep this file restricted to:
|
||||
* - forward declarations of the C functions called by the macros below,
|
||||
* - the trace*() macro definitions themselves.
|
||||
*
|
||||
* Macros are allowed to reference FreeRTOS identifiers (pxTCB, xTicksToWait, ...)
|
||||
* by name — they are resolved later, when the macro is expanded inside the
|
||||
* FreeRTOS kernel .c files where those names are already in scope.
|
||||
*
|
||||
* Only the trace*() macros this example actually hooks are defined here.
|
||||
* Anything left undefined falls back to FreeRTOS's own empty default (see
|
||||
* the #ifndef guards in freertos/FreeRTOS.h).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward declaration so we can pass an encoder pointer from the adapter to
|
||||
* init_trace_lib() without pulling in esp_trace_port_encoder.h. */
|
||||
typedef struct esp_trace_encoder esp_trace_encoder_t;
|
||||
|
||||
void init_trace_lib(esp_trace_encoder_t *enc);
|
||||
void trace_lib_start(void);
|
||||
void trace_lib_stop(void);
|
||||
|
||||
/* Hook implementations — defined in trace_FreeRTOS.c.
|
||||
* Kept void*-typed to avoid depending on FreeRTOS types in this header. */
|
||||
void trace_lib_task_switched_in(void);
|
||||
void trace_lib_task_create(void *pxNewTCB);
|
||||
void trace_lib_isr_enter(uint32_t irq);
|
||||
void trace_lib_isr_exit(void);
|
||||
void trace_lib_isr_exit_to_scheduler(void);
|
||||
void trace_lib_queue_send(void *pxQueue);
|
||||
void trace_lib_queue_receive(void *pxQueue);
|
||||
void trace_lib_queue_create(void *pxNewQueue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------ *
|
||||
* Active hooks (forwarded to trace_FreeRTOS.c)
|
||||
* ------------------------------------------------------------------ */
|
||||
#define traceTASK_SWITCHED_IN() trace_lib_task_switched_in()
|
||||
#define traceTASK_CREATE(pxNewTCB) trace_lib_task_create(pxNewTCB)
|
||||
#define traceISR_ENTER(n) trace_lib_isr_enter(n)
|
||||
#define traceISR_EXIT() trace_lib_isr_exit()
|
||||
#define traceISR_EXIT_TO_SCHEDULER() trace_lib_isr_exit_to_scheduler()
|
||||
#define traceQUEUE_SEND(pxQueue) trace_lib_queue_send(pxQueue)
|
||||
#define traceQUEUE_RECEIVE(pxQueue) trace_lib_queue_receive(pxQueue)
|
||||
#define traceQUEUE_CREATE(pxNewQueue) trace_lib_queue_create(pxNewQueue)
|
||||
|
||||
/* All other trace*() macros fall back to FreeRTOS's default empty defines
|
||||
* (#ifndef ... #define ... empty in freertos/FreeRTOS.h); no need to list
|
||||
* them here. Add a mapping above when you want to hook one. */
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_trace_types.h"
|
||||
#include "esp_trace_registry.h"
|
||||
#include "esp_trace_port_encoder.h"
|
||||
#include "esp_trace_port_transport.h"
|
||||
#include "esp_trace_util.h"
|
||||
#include "trace_FreeRTOS.h"
|
||||
|
||||
typedef struct {
|
||||
esp_trace_lock_t lock;
|
||||
} ext_trace_lib_ctx_t;
|
||||
/**
|
||||
* @brief Initializes ext_trace_lib encoder.
|
||||
* This function is called for each core.
|
||||
* Adapter implementations do NOT need their own multi-core protection. Core does it for them.
|
||||
*
|
||||
* @param enc Pointer to the encoder structure. Must not be NULL.
|
||||
* @param enc_cfg Pointer to the encoder configuration. Can be NULL for defaults.
|
||||
*
|
||||
* @return ESP_OK on success, otherwise \see esp_err_t
|
||||
*/
|
||||
static esp_err_t init(esp_trace_encoder_t *enc, const void *enc_cfg)
|
||||
{
|
||||
(void)enc_cfg;
|
||||
|
||||
// Ensure the encoder is initialized only once unless something todo for both cores
|
||||
static bool initialized = false;
|
||||
|
||||
if (!enc) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ext_trace_lib_ctx_t *ctx = heap_caps_calloc(1, sizeof(*ctx),
|
||||
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
if (!ctx) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
esp_trace_lock_init(&ctx->lock);
|
||||
enc->ctx = ctx;
|
||||
|
||||
init_trace_lib(enc);
|
||||
|
||||
initialized = true;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t write(esp_trace_encoder_t *enc, const void *data, size_t size, uint32_t tmo)
|
||||
{
|
||||
if (!enc || !data || size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!enc->tp || !enc->tp->vt->write) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return enc->tp->vt->write(enc->tp, data, size, tmo);
|
||||
}
|
||||
|
||||
static esp_err_t start(esp_trace_encoder_t *enc)
|
||||
{
|
||||
(void)enc;
|
||||
trace_lib_start();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t stop(esp_trace_encoder_t *enc)
|
||||
{
|
||||
(void)enc;
|
||||
trace_lib_stop();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t flush(esp_trace_encoder_t *enc)
|
||||
{
|
||||
if (!enc || !enc->tp || !enc->tp->vt->flush_nolock) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return enc->tp->vt->flush_nolock(enc->tp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Panic handler
|
||||
*
|
||||
* Called during system panic to finalize encoder state.
|
||||
*
|
||||
* @param enc Pointer to the encoder structure. Must not be NULL.
|
||||
* @param info Panic information
|
||||
*/
|
||||
static void panic_handler(esp_trace_encoder_t *enc, const void *info)
|
||||
{
|
||||
(void)info;
|
||||
flush(enc);
|
||||
}
|
||||
|
||||
static unsigned int take_lock(esp_trace_encoder_t *enc, uint32_t tmo_us)
|
||||
{
|
||||
if (!enc || !enc->ctx) {
|
||||
return 0;
|
||||
}
|
||||
ext_trace_lib_ctx_t *ctx = enc->ctx;
|
||||
esp_trace_lock_take(&ctx->lock, tmo_us);
|
||||
return ctx->lock.int_state;
|
||||
}
|
||||
|
||||
static void give_lock(esp_trace_encoder_t *enc, unsigned int int_state)
|
||||
{
|
||||
if (!enc || !enc->ctx) {
|
||||
return;
|
||||
}
|
||||
ext_trace_lib_ctx_t *ctx = enc->ctx;
|
||||
ctx->lock.int_state = int_state;
|
||||
esp_trace_lock_give(&ctx->lock);
|
||||
}
|
||||
|
||||
static const esp_trace_encoder_vtable_t s_ext_trace_lib_vt = {
|
||||
.init = init,
|
||||
.write = write,
|
||||
.panic_handler = panic_handler,
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.flush = flush,
|
||||
.take_lock = take_lock,
|
||||
.give_lock = give_lock,
|
||||
};
|
||||
|
||||
ESP_TRACE_REGISTER_ENCODER("ext_trace_lib", &s_ext_trace_lib_vt);
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Implementations of the trace*() hooks declared in trace_FreeRTOS.h.
|
||||
*
|
||||
* Each hook encodes a single human-readable line that can be observed directly
|
||||
* in any serial monitor — no decoder is needed:
|
||||
*
|
||||
* [+ 123 us] TASK_IN Task 1
|
||||
* [+ 1000 us] ISR_IN irq=5
|
||||
*
|
||||
* The leading number is the time elapsed since the previous traced event.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "trace_FreeRTOS.h"
|
||||
#include "esp_trace.h"
|
||||
#include "esp_trace_port_encoder.h"
|
||||
#include "esp_trace_util.h"
|
||||
|
||||
static esp_trace_handle_t s_esp_trace_handle = NULL;
|
||||
static esp_trace_encoder_t *s_enc = NULL;
|
||||
static uint32_t s_ts_freq_hz = 1000000; /* default assume 1 MHz */
|
||||
static uint32_t s_last_ts = 0;
|
||||
static volatile bool s_enabled = false;
|
||||
|
||||
void init_trace_lib(esp_trace_encoder_t *enc)
|
||||
{
|
||||
s_esp_trace_handle = esp_trace_get_active_handle();
|
||||
s_enc = enc;
|
||||
|
||||
uint32_t freq = esp_trace_timestamp_init();
|
||||
if (freq != 0) {
|
||||
s_ts_freq_hz = freq;
|
||||
}
|
||||
s_last_ts = esp_trace_timestamp_get();
|
||||
}
|
||||
|
||||
void trace_lib_start(void)
|
||||
{
|
||||
if (!s_esp_trace_handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_last_ts = esp_trace_timestamp_get();
|
||||
s_enabled = true;
|
||||
}
|
||||
|
||||
void trace_lib_stop(void)
|
||||
{
|
||||
s_enabled = false;
|
||||
}
|
||||
|
||||
/* Encode one trace line and write it through. */
|
||||
static void encode(const char *type, const char *detail)
|
||||
{
|
||||
if (!s_enabled || !s_esp_trace_handle || !s_enc) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int int_state = s_enc->vt->take_lock(s_enc, ESP_TRACE_TMO_INFINITE);
|
||||
|
||||
uint32_t now = esp_trace_timestamp_get();
|
||||
uint32_t delta = now - s_last_ts; /* uint32 modular subtraction handles wrap */
|
||||
s_last_ts = now;
|
||||
|
||||
uint32_t delta_us = (s_ts_freq_hz == 1000000)
|
||||
? delta
|
||||
: (uint32_t)((uint64_t)delta * 1000000ULL / s_ts_freq_hz);
|
||||
|
||||
char line[96];
|
||||
int n = snprintf(line, sizeof(line), "[+%7lu us] %-12s %s\n",
|
||||
(unsigned long)delta_us, type, detail ? detail : "");
|
||||
if (n > 0) {
|
||||
if (n >= (int)sizeof(line)) {
|
||||
n = (int)sizeof(line) - 1;
|
||||
}
|
||||
esp_trace_write(s_esp_trace_handle, line, (size_t)n, 0);
|
||||
}
|
||||
|
||||
s_enc->vt->give_lock(s_enc, int_state);
|
||||
}
|
||||
|
||||
void trace_lib_task_switched_in(void)
|
||||
{
|
||||
TaskHandle_t h = xTaskGetCurrentTaskHandle();
|
||||
encode("TASK_IN", h ? pcTaskGetName(h) : "?");
|
||||
}
|
||||
|
||||
void trace_lib_task_create(void *pxNewTCB)
|
||||
{
|
||||
if (!pxNewTCB) {
|
||||
encode("TASK_CREATE", "(null)");
|
||||
return;
|
||||
}
|
||||
encode("TASK_CREATE", pcTaskGetName((TaskHandle_t)pxNewTCB));
|
||||
}
|
||||
|
||||
void trace_lib_isr_enter(uint32_t irq)
|
||||
{
|
||||
char d[24];
|
||||
snprintf(d, sizeof(d), "irq=%lu", (unsigned long)irq);
|
||||
encode("ISR_IN", d);
|
||||
}
|
||||
|
||||
void trace_lib_isr_exit(void)
|
||||
{
|
||||
encode("ISR_OUT", "");
|
||||
}
|
||||
|
||||
void trace_lib_isr_exit_to_scheduler(void)
|
||||
{
|
||||
encode("ISR_YIELD", "");
|
||||
}
|
||||
|
||||
void trace_lib_queue_send(void *pxQueue)
|
||||
{
|
||||
char d[24];
|
||||
snprintf(d, sizeof(d), "q=%p", pxQueue);
|
||||
encode("Q_SEND", d);
|
||||
}
|
||||
|
||||
void trace_lib_queue_receive(void *pxQueue)
|
||||
{
|
||||
char d[24];
|
||||
snprintf(d, sizeof(d), "q=%p", pxQueue);
|
||||
encode("Q_RECEIVE", d);
|
||||
}
|
||||
|
||||
void trace_lib_queue_create(void *pxNewQueue)
|
||||
{
|
||||
char d[24];
|
||||
snprintf(d, sizeof(d), "q=%p", pxNewQueue);
|
||||
encode("Q_CREATE", d);
|
||||
}
|
||||
Reference in New Issue
Block a user