# ESP Trace Component The `esp_trace` component provides a unified tracing infrastructure for ESP-IDF. It acts as a central hub that coordinates different trace libraries (like SEGGER SystemView) and trace transports (like apptrace over JTAG/UART). ## Overview The `esp_trace` component enables: - Integration of multiple trace libraries through a common interface - Flexible transport layer selection (JTAG, UART, or custom) - Centralized trace configuration and management - Support for both built-in and external trace libraries ## Architecture The `esp_trace` component uses a **Port & Adapter** design pattern (also known as Hexagonal Architecture) to provide flexibility and extensibility: ```mermaid flowchart TB %% ======================= %% Application Layer %% ======================= subgraph APP["📱 APPLICATION"] app_main["app_main()"] sysview_tracing["sysview_tracing.c"] examples["tracing_heap_log.c"] freertos_events["FreeRTOS Events"] end %% ======================= %% Primary Port (Driver Port) %% ======================= subgraph PRIMARY["🔌 PUBLIC INTERFACE"] api["- esp_trace.h - esp_trace_init() - esp_trace_record() - esp_trace_write() - esp_trace_flush() - esp_trace_print()"] end %% wiring: App uses API (labels land on the short pre-edges to api_in) app_main -->|esp_trace.h| api sysview_tracing -->|esp_trace.h| api examples -->|esp_trace.h| api freertos_events -->|trace macros| api %% ======================= %% Core Domain %% ======================= subgraph CORE["⚙️ CORE DOMAIN"] corec["- esp_trace_core.c - Creates encoder + transport instances - Thread safe multi-core init - Adapter coordination"] regc["esp_trace_registry.c - Runtime adapter discovery - Maps names to vtables"] end api --> corec corec --- regc %% ======================= %% Ports (Outbound) %% ======================= subgraph PORT_ENC["🔌 PORT Encoder"] enc_port["esp_trace_port_encoder.h Interface: - init() - print_event() - write() - flush()"] end subgraph PORT_TR["🔌 PORT Transport"] tr_port["esp_trace_port_transport.h Interface: - init() - write() - flush() - is_host_connected()"] end corec --> enc_port corec --> tr_port %% ======================= %% Adapters %% ======================= subgraph ADAPTERS["🔶 ADAPTERS"] %% Encoders enc_sysview["encoder_sysview.c implements sysview vtable"] enc_ctf["encoder_ctf.c implements ctf vtable"] enc_percepio["encoder_percepio.c implements percepio vtable"] %% Transports tr_apptrace["transport_apptrace.c implements apptrace vtable"] end enc_port --> enc_sysview enc_port --> enc_ctf enc_port --> enc_percepio tr_port --> tr_apptrace %% ======================= %% Host tools %% ======================= subgraph HOST_TOOLS["💻 TOOLS"] direction LR view_openocd["OpenOCD"] view_sysview["SEGGER SystemView"] view_ctf["Trace Compass"] view_perfetto["Perfetto"] view_babeltrace["Babeltrace2"] view_percepio["TraceAlyzer"] end %% invisible hub to center align hub(("HOST")) %% Adapters feed into the hub (not directly into subgraph) enc_sysview --> hub enc_ctf --> hub tr_apptrace --> hub %% hub connects downward to host tools hub --> view_openocd & view_sysview & view_ctf & view_perfetto & view_babeltrace & view_percepio %% Styling classDef appStyle fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000 classDef primaryStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000 classDef coreStyle fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#000 classDef portStyle fill:#e1f5fe,stroke:#0277bd,stroke-width:3px,color:#000 classDef adapterStyle fill:#fff9c4,stroke:#f9a825,stroke-width:2px,color:#000 classDef hostStyle fill:#e8f5e9,stroke:#388e3c,stroke-width:2px,color:#000 classDef hubStyle fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#000 class APP appStyle class PRIMARY,api primaryStyle class CORE,corec,regc coreStyle class PORT_ENC,PORT_TR,enc_port,tr_port portStyle class ADAPTERS,enc_sysview,enc_ctf,enc_percepio,tr_apptrace adapterStyle class HOST_TOOLS,view_openocd,view_sysview,view_ctf,view_perfetto,view_babeltrace,view_percepio hostStyle class hub hubStyle ``` The architecture follows a layered Port & Adapter pattern where the core manages trace sessions and coordinates adapters through well-defined interfaces (ports). Applications interact with a public API that forwards calls to the core, which then delegates work to encoder adapters (for trace formatting) and transport adapters (for data transmission). The registry enables runtime discovery of adapters that register themselves at link time using `ESP_TRACE_REGISTER_ENCODER()` and `ESP_TRACE_REGISTER_TRANSPORT()` macros. Encoders use transport ports to send formatted trace data, allowing any encoder to work with any transport. This separation means you can, for example, use SystemView with JTAG, other library with UART, or any other combination without modifying the core or adapters themselves. ## How to Enable ### Using Menuconfig 1. Go to `Component config` → `ESP Trace Configuration` 2. Select your trace library under `Trace library`: - **External library from component registry** - Use a custom encoder provided by an external component (e.g. SystemView) - **Disabled** - Disable trace library 3. Select your desired trace transport under `Trace transport`: - **ESP-IDF apptrace** - Use built-in apptrace for custom tracing - **External transport from component registry** - Use a custom transport provided by an external component - **None** - Disable tracing transport ### Using sdkconfig For standalone apptrace (without a trace library): ``` CONFIG_ESP_TRACE_ENABLE=y CONFIG_ESP_TRACE_LIB_NONE=y CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y ``` For SystemView tracing over JTAG: 1. Add `espressif/esp_sysview` component to your `idf_component.yml`: ```yaml dependencies: espressif/esp_sysview: "^1" ``` 2. Configure in `sdkconfig`: ``` CONFIG_ESP_TRACE_ENABLE=y CONFIG_ESP_TRACE_LIB_EXTERNAL=y CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y CONFIG_APPTRACE_DEST_JTAG=y ``` ## Component Dependencies ### When Using External Trace Libraries (e.g., SystemView) If you're using an external trace library from the component registry (like `espressif/esp_sysview`), you **don't need** to explicitly add `esp_trace` to your dependencies. The external component already has a public dependency on `esp_trace` (using `REQUIRES`), so it's automatically available to your application code: ```cmake idf_component_register( SRCS "main.c" INCLUDE_DIRS "." # No need to add esp_trace here when using esp_sysview ) ``` This means you can directly use both the trace library APIs (e.g., SystemView) and `esp_trace` APIs (like `esp_trace_get_user_params()`, `esp_trace_is_host_connected()`, etc.) without explicitly declaring the dependency. ### When Using Standalone Apptrace For standalone apptrace usage (without an external trace library), add `esp_trace` to your component's dependencies: ```cmake idf_component_register( SRCS "main.c" INCLUDE_DIRS "." REQUIRES esp_trace ) ``` The `esp_trace` component will automatically include necessary sub-components (like `app_trace`) based on your configuration. ## Adding External Trace Libraries The `esp_trace` component supports integration of external trace libraries through two types of adapters: - **Transport Adapters**: Handle the physical transport layer (e.g., JTAG, UART) - **Encoder Adapters**: Handle the trace encoding/formatting (e.g., SystemView, custom formats) ### Creating a Transport Adapter Transport adapters provide the physical communication layer for trace data. **Example: Creating a custom transport adapter** ```c #include "esp_trace_registry.h" #include "esp_trace_port_transport.h" static esp_err_t my_transport_init(esp_trace_transport_t *tp, const void *cfg) { // Initialize your transport return ESP_OK; } static esp_err_t my_transport_write(esp_trace_transport_t *tp, const void *data, size_t size, uint32_t tmo) { // Write data to your transport return ESP_OK; } static esp_err_t my_transport_flush(esp_trace_transport_t *tp, uint32_t tmo) { // Flush transport buffers return ESP_OK; } // Create the vtable with your functions static const esp_trace_transport_vtable_t s_my_transport_vt = { .init = my_transport_init, .write = my_transport_write, .flush = my_transport_flush, // Add other required function pointers as needed }; // Register the transport adapter ESP_TRACE_REGISTER_TRANSPORT("my_transport", &s_my_transport_vt); ``` See `components/esp_trace/adapters/transport/adapter_transport_apptrace.c` for a complete reference implementation. ### Creating an Encoder Adapter Encoder adapters handle trace data formatting and encoding for specific trace libraries. **Example: Creating a custom encoder adapter** ```c #include "esp_trace_registry.h" #include "esp_trace_port_encoder.h" static esp_err_t my_encoder_init(esp_trace_encoder_t *enc, const void *cfg) { // Initialize your encoder // Configure transport if needed: // enc->tp->vt->set_config(enc->tp, ESP_TRACE_TRANSPORT_CFG_HEADER_SIZE, &value); return ESP_OK; } static esp_err_t my_encoder_print_event(esp_trace_encoder_t *enc, const char *event_name, const char *formatted_str) { // Format and send trace event return ESP_OK; } static esp_err_t my_encoder_flush(esp_trace_encoder_t *enc, uint32_t tmo) { // Flush encoder buffers return ESP_OK; } // Create the vtable with your functions static const esp_trace_encoder_vtable_t s_my_encoder_vt = { .init = my_encoder_init, .print_event = my_encoder_print_event, .flush = my_encoder_flush, // Add other required function pointers as needed }; // Register the encoder adapter ESP_TRACE_REGISTER_ENCODER("my_encoder", &s_my_encoder_vt); ``` **Key Points:** - Use `ESP_TRACE_REGISTER_ENCODER(name, vtable)` to register your encoder - Use `ESP_TRACE_REGISTER_TRANSPORT(name, vtable)` if you're implementing a custom transport - Registration happens automatically at link time (no manual initialization needed) See `espressif/esp_sysview` component source (specifically `adapter_encoder_sysview.c`) for a complete reference implementation. ### FreeRTOS Trace Integration If your external trace library needs to capture FreeRTOS events, you must provide an `esp_trace_freertos_impl.h` header in your component's include directory. This header should define FreeRTOS trace macros (like `traceTASK_SWITCHED_IN()`, `traceISR_ENTER()`, etc.) that will be called by FreeRTOS. The `esp_trace` component includes this header when `CONFIG_ESP_TRACE_LIB_EXTERNAL=y` is set: ```c // In esp_trace_freertos.h #if CONFIG_ESP_TRACE_LIB_EXTERNAL #include "esp_trace_freertos_impl.h" #endif ``` **Example esp_trace_freertos_impl.h:** ```c #pragma once #include "your_trace_library.h" #define traceTASK_SWITCHED_IN() your_lib_task_switched_in(xTaskGetCurrentTaskHandle()) #define traceISR_ENTER(irq_num) your_lib_isr_enter(irq_num) #define traceISR_EXIT() your_lib_isr_exit() // ... define other FreeRTOS trace macros as needed ``` ### Component CMakeLists.txt Setup For external adapters to work properly, your component's CMakeLists.txt needs two important settings: **Example CMakeLists.txt:** ```cmake idf_component_register( SRCS "src/adapter_encoder_ctf.c" INCLUDE_DIRS "include" REQUIRES esp_trace WHOLE_ARCHIVE TRUE # Important: ensures adapter registration is linked ) # Auto-register this component with esp_trace # This allows esp_trace to find your esp_trace_freertos_impl.h idf_component_get_property(esp_trace_lib esp_trace COMPONENT_LIB) target_link_libraries(${esp_trace_lib} INTERFACE $) ``` **Important:** - `WHOLE_ARCHIVE TRUE` ensures your adapter registration code is linked - The `target_link_libraries` trick makes your headers visible to esp_trace component ### Using an External Adapter 1. **Add the component to your project dependencies** in your main component's `CMakeLists.txt`: ```cmake idf_component_register( SRCS "main.c" INCLUDE_DIRS "." REQUIRES my_trace_component esp_trace ) ``` 2. **Configure trace settings** in your project's `sdkconfig.defaults`: ```ini # Enable esp_trace CONFIG_ESP_TRACE_ENABLE=y # Use external trace library CONFIG_ESP_TRACE_LIB_EXTERNAL=y # Select transport (e.g., apptrace over UART) CONFIG_ESP_TRACE_TRANSPORT_APPTRACE=y CONFIG_APPTRACE_DEST_UART=y CONFIG_APPTRACE_DEST_UART_NUM=0 ``` 3. **Build and run** - The adapter will be automatically registered and used when the component is linked. **Note:** External trace libraries should use `CONFIG_ESP_TRACE_LIB_EXTERNAL=y` instead of defining their own Kconfig option in the esp_trace menu. This keeps the external component independent from the esp_trace component. ## Documentation For detailed usage instructions, see: - [Application Tracing Guide](../../docs/en/api-guides/app_trace.rst) - [Migration Guide](../../docs/en/migration-guides/release-6.x/6.0/system.rst) ## Examples Examples demonstrating trace usage can be found in: - `examples/system/app_trace_basic/` - Basic application tracing - `examples/system/sysview_tracing/` - SystemView tracing example - `examples/system/sysview_tracing_heap_log/` - SystemView heap and log tracing example