Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions esp_sysview/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
set(include_dirs "")
set(srcs "")
set(include_dirs "")
set(srcs "")
set(requires "esp_trace")
set(priv_requires "esp_app_format")

if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
list(APPEND include_dirs
src/Config
src/SEGGER
Expand All @@ -17,6 +19,10 @@ if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
src/esp/adapter_encoder_sysview.c
src/ext/logging.c)

if(CONFIG_ESP_TRACE_FUNCTION_TRACE)
list(APPEND srcs src/esp/function_trace_sysview.c)
endif()

if(CONFIG_HEAP_TRACING_TOHOST)
list(APPEND srcs
src/ext/heap_trace_module.c
Expand All @@ -30,7 +36,8 @@ if(CONFIG_ESP_TRACE_LIB_EXTERNAL)

idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}"
REQUIRES esp_trace
REQUIRES "${requires}"
PRIV_REQUIRES "${priv_requires}"
LDFRAGMENTS ${CMAKE_CURRENT_BINARY_DIR}/linker.lf
WHOLE_ARCHIVE TRUE # Link all symbols so self-registering adapters (ESP_TRACE_REGISTER_*) are not discarded
)
Expand All @@ -40,5 +47,5 @@ if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
idf_component_get_property(freertos_lib freertos COMPONENT_LIB)
target_include_directories(${freertos_lib} INTERFACE ${include_dirs})
else()
idf_component_register(REQUIRES "esp_trace")
idf_component_register(REQUIRES "${requires}" PRIV_REQUIRES "${priv_requires}")
endif()
13 changes: 11 additions & 2 deletions esp_sysview/Kconfig
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
menu "SEGGER SystemView Configuration"
depends on ESP_TRACE_LIB_EXTERNAL

config SEGGER_SYSVIEW_UART_AVAILABLE
bool
default y if ESP_TRACE_TRANSPORT_APPTRACE && (APPTRACE_DEST_UART || APPTRACE_DEST_ALL)

choice SEGGER_SYSVIEW_DEST_CPU
prompt "CPU to trace"
depends on ESP_TRACE_TRANSPORT_APPTRACE && APPTRACE_DEST_UART && !ESP_SYSTEM_SINGLE_CORE_MODE
depends on SEGGER_SYSVIEW_UART_AVAILABLE && !ESP_SYSTEM_SINGLE_CORE_MODE
default SEGGER_SYSVIEW_DEST_CPU_0
help
Define the CPU to trace.
Select the CPU to trace when SystemView uses a single UART stream.

UART transport carries one SystemView stream, so events from only one
CPU can be represented reliably. JTAG tracing can keep
events from multiple CPUs and this option is ignored there.

config SEGGER_SYSVIEW_DEST_CPU_0
bool "CPU0"
Expand Down
10 changes: 10 additions & 0 deletions esp_sysview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ Configure SystemView tracing in `idf.py menuconfig`:
- Select timestamp source: Component config > ESP Trace Configuration > Trace timestamp source
- Configure event filters: Component config > SEGGER SystemView Configuration

## Function tracing

When `CONFIG_ESP_TRACE_FUNCTION_TRACE` is enabled, this component implements the
`esp_trace` function-trace encoder callbacks and sends enter/exit events through
a dedicated SystemView module (`ESP_FunctionTrace`). Host-driven start/stop (over
JTAG or UART) is propagated to the function-trace runtime so recording follows
the SystemView session. Addresses are sent as `U32`. SystemView records them as
raw addresses. Symbols can be mapped to function names offline against the ELF file (for example
with `addr2line`).

## Documentation and examples

- ESP-IDF examples:
Expand Down
2 changes: 1 addition & 1 deletion esp_sysview/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 1.0.2
version: 1.1.0
description: SEGGER SystemView component for ESP-IDF
url: https://github.com/espressif/idf-extra-components/tree/master/esp_sysview
issues: https://github.com/espressif/idf-extra-components/issues
Expand Down
29 changes: 29 additions & 0 deletions esp_sysview/src/SEGGER/SEGGER_SYSVIEW.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,19 @@ Additional information:
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <assert.h>
#include "SEGGER_SYSVIEW_Int.h"
#include "SEGGER_RTT.h"

/*
* ESP: notify esp_trace core so host-driven start/stop propagates to
* dependent features (e.g. function tracing). Declared weak so esp_sysview
* still links against an esp_trace revision that does not provide it.
* The notification becomes a no-op when the symbol is absent.
*/
extern void esp_trace_notify_recording_state(bool active) __attribute__((weak));

/*********************************************************************
*
* Defines, fixed
Expand Down Expand Up @@ -1902,6 +1911,7 @@ void SEGGER_SYSVIEW_RecordString(unsigned int EventID, const char *pString)
*/
void SEGGER_SYSVIEW_Start(void)
{
U8 WasEnabled = _SYSVIEW_Globals.EnableState; /* ESP */
#if (SEGGER_SYSVIEW_CAN_RESTART == 0)
if (_SYSVIEW_Globals.EnableState == 0) {
#endif
Expand Down Expand Up @@ -1933,10 +1943,24 @@ void SEGGER_SYSVIEW_Start(void)
SEGGER_SYSVIEW_RecordSystime();
SEGGER_SYSVIEW_SendTaskList();
SEGGER_SYSVIEW_SendNumModules();
/* ESP: send module descriptions on start so streaming captures (e.g. over
* JTAG, where the host never requests them) are self-describing. */
if (_NumModules > 0) {
int n;
for (n = 0; n < _NumModules; n++) {
SEGGER_SYSVIEW_SendModule(n);
}
}
#endif
#if (SEGGER_SYSVIEW_CAN_RESTART == 0)
}
#endif
/* ESP: notify on an actual disabled->enabled transition, or unconditionally
* for restart-capable builds. This avoids resetting dependent features
* (e.g. function tracing) on redundant Start commands. */
if ((WasEnabled == 0 || SEGGER_SYSVIEW_CAN_RESTART) && esp_trace_notify_recording_state) {
esp_trace_notify_recording_state(true); /* ESP */
}
}

/*********************************************************************
Expand All @@ -1959,6 +1983,11 @@ void SEGGER_SYSVIEW_Start(void)
void SEGGER_SYSVIEW_Stop(void)
{
U8 *pPayloadStart;

if (esp_trace_notify_recording_state) {
esp_trace_notify_recording_state(false); /* ESP */
}

RECORD_START(SEGGER_SYSVIEW_INFO_SIZE);
// We should send answer for the Stop command in any case.
_SYSVIEW_Globals.EnableState = 1;
Expand Down
1 change: 0 additions & 1 deletion esp_sysview/src/SEGGER/SEGGER_SYSVIEW.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ void SEGGER_SYSVIEW_X_OnEventRecorded (unsigned NumBytes);
*/
int SEGGER_SYSVIEW_ESP_SetEncoder(void *encoder);
void *SEGGER_SYSVIEW_ESP_GetEncoder(void);
int SEGGER_SYSVIEW_ESP_GetDestCpu(void);

#ifdef __cplusplus
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Revision: $Rev: 7745 $
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "SEGGER_SYSVIEW.h"
#include "esp_app_desc.h"
#include "esp_intr_alloc.h"
#include "soc/soc.h"
#include "soc/interrupts.h"
Expand Down Expand Up @@ -128,6 +129,11 @@ static void _cbSendSystemDesc(void)
strncat(irq_str, esp_isr_names[i], sizeof(irq_str) - strlen(irq_str) - 1);
SEGGER_SYSVIEW_SendSysDesc(irq_str);
}
/* Application ELF identity so host tools can resolve the matching ELF */
char elf_desc[sizeof("ELF_SHA256=") + CONFIG_APP_RETRIEVE_LEN_ELF_SHA];
strlcpy(elf_desc, "ELF_SHA256=", sizeof(elf_desc));
strlcat(elf_desc, esp_app_get_elf_sha256_str(), sizeof(elf_desc));
SEGGER_SYSVIEW_SendSysDesc(elf_desc);
}

/*********************************************************************
Expand Down
7 changes: 4 additions & 3 deletions esp_sysview/src/esp/SEGGER_RTT_esp.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "esp_trace_port_transport.h"
#include "esp_trace_port_encoder.h"
#include "esp_trace_types.h"
#include "adapter_encoder_sysview.h"
#include "esp_private/startup_internal.h"

const static char *TAG = "segger_rtt";
Expand Down Expand Up @@ -163,10 +164,10 @@ unsigned SEGGER_RTT_WriteSkipNoLock(unsigned BufferIndex, const void *pBuffer, u
uint8_t event_id = *pbuf;

esp_trace_link_types_t link_type = tp->vt->get_link_type(tp);
if (link_type != ESP_TRACE_LINK_DEBUG_PROBE) { // Uart and USB Serial JTAG are handled separately
int dest_cpu = SEGGER_SYSVIEW_ESP_GetDestCpu();
sysview_encoder_ctx_t *ctx = encoder->ctx;
if (ctx && ctx->filter_by_cpu) {
if (
(dest_cpu != esp_cpu_get_core_id()) &&
(ctx->dest_cpu != esp_cpu_get_core_id()) &&
(
(event_id == SYSVIEW_EVTID_ISR_ENTER) ||
(event_id == SYSVIEW_EVTID_ISR_EXIT) ||
Expand Down
19 changes: 0 additions & 19 deletions esp_sysview/src/esp/SEGGER_SYSVIEW_esp.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,3 @@ void *SEGGER_SYSVIEW_ESP_GetEncoder(void)
{
return s_encoder;
}

/*********************************************************************
*
* SEGGER_SYSVIEW_ESP_GetDestCpu()
*
* Function description
* Gets the destination CPU from the encoder context.
*
* Parameters
* None
*
* Return value
* CPU ID (0 or 1) to trace
*/
int SEGGER_SYSVIEW_ESP_GetDestCpu(void)
{
sysview_encoder_ctx_t *ctx = s_encoder->ctx;
return ctx->dest_cpu;
}
22 changes: 22 additions & 0 deletions esp_sysview/src/esp/adapter_encoder_sysview.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
* All encoding and transport operations are done by SystemView component. (SEGGER_RTT_esp.c)
*/

extern void esp_trace_notify_recording_state(bool active) __attribute__((weak));

#define SYSVIEW_FLUSH_TMO_US (1000 * 1000) /* 1second */
#define SYSVIEW_FLUSH_THRESH 0

Expand Down Expand Up @@ -51,6 +53,10 @@ static esp_err_t init(esp_trace_encoder_t *enc, const void *enc_cfg)
const esp_trace_sysview_config_t *cfg = enc_cfg;
int dest_cpu = 0; // Default to CPU0

#if CONFIG_SEGGER_SYSVIEW_DEST_CPU_1
dest_cpu = 1;
#endif

if (cfg) {
dest_cpu = cfg->dest_cpu;
}
Expand All @@ -64,6 +70,10 @@ static esp_err_t init(esp_trace_encoder_t *enc, const void *enc_cfg)
/* Segger Sysview needs locking mechanism. */
esp_trace_lock_init(&ctx->lock);
ctx->dest_cpu = dest_cpu;
ctx->filter_by_cpu = true;
if (enc->tp && enc->tp->vt && enc->tp->vt->get_link_type) {
ctx->filter_by_cpu = (enc->tp->vt->get_link_type(enc->tp) != ESP_TRACE_LINK_DEBUG_PROBE);
}
enc->ctx = ctx;

if (SEGGER_SYSVIEW_ESP_SetEncoder(enc) != 0) {
Expand All @@ -84,6 +94,14 @@ static esp_err_t init(esp_trace_encoder_t *enc, const void *enc_cfg)

SEGGER_SYSVIEW_Conf();

#if CONFIG_ESP_TRACE_FUNCTION_TRACE
esp_sysview_function_trace_register();
#endif

if (esp_trace_notify_recording_state) {
esp_trace_notify_recording_state(SEGGER_SYSVIEW_Started() != 0);
}

initialized = true;

return ESP_OK;
Expand Down Expand Up @@ -142,6 +160,10 @@ static const esp_trace_encoder_vtable_t s_sysview_vt = {
.panic_handler = panic_handler,
.take_lock = take_lock,
.give_lock = give_lock,
#if CONFIG_ESP_TRACE_FUNCTION_TRACE
.function_enter = esp_sysview_function_enter,
.function_exit = esp_sysview_function_exit,
#endif
};

ESP_TRACE_REGISTER_ENCODER("sysview", &s_sysview_vt);
18 changes: 16 additions & 2 deletions esp_sysview/src/esp/adapter_encoder_sysview.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#pragma once

#include <stdbool.h>
#include "esp_trace_util.h"

#ifdef __cplusplus
Expand All @@ -18,8 +19,8 @@ typedef struct {
/**
* @brief CPU to trace (0 or 1)
*
* Determines which CPU's events are captured by SystemView.
* Only relevant when using UART apptrace transport.
* UART apptrace carries one SystemView stream, so events are filtered to
* this CPU. JTAG tracing keeps multicore events and ignores this value.
*
* - 0: Capture events from CPU0 (Pro CPU)
* - 1: Capture events from CPU1 (App CPU)
Expand All @@ -38,9 +39,22 @@ typedef struct {
*/
typedef struct {
int dest_cpu; ///< CPU to trace (0 or 1)
bool filter_by_cpu; ///< true for single-stream transports that cannot represent multicore events
esp_trace_lock_t lock; ///< Encoder lock
} sysview_encoder_ctx_t;

/* Forward declaration to avoid pulling the encoder port header here. */
struct esp_trace_encoder;

/** @brief Send a function entry event (esp_trace function-trace callback). */
void esp_sysview_function_enter(struct esp_trace_encoder *enc, void *func, void *call_site);

/** @brief Send a function exit event (esp_trace function-trace callback). */
void esp_sysview_function_exit(struct esp_trace_encoder *enc, void *func, void *call_site);

/** @brief Register the function-trace SystemView module (call once at init). */
void esp_sysview_function_trace_register(void);

#ifdef __cplusplus
}
#endif
71 changes: 71 additions & 0 deletions esp_sysview/src/esp/function_trace_sysview.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

/*
* SystemView backend for esp_trace compiler-instrumented function tracing.
*/

#include <stdbool.h>
#include <stdint.h>
#include "esp_cpu.h"
#include "esp_trace_port_encoder.h"
#include "adapter_encoder_sysview.h"
#include "SEGGER_SYSVIEW.h"

/* Event IDs sent as EventOffset + id. EventOffset is allocated in registration order starting at 512. */
enum {
FT_EVT_ENTER = 0,
FT_EVT_EXIT,
FT_EVT_COUNT,
};

static SEGGER_SYSVIEW_MODULE s_ft_module = {
.sModule =
"M=ESP_FunctionTrace, "
"0 function_enter func=%p call_site=%p, "
"1 function_exit func=%p call_site=%p",
.NumEvents = FT_EVT_COUNT,
};

static bool s_registered;

static inline bool should_drop_for_cpu(struct esp_trace_encoder *enc)
{
sysview_encoder_ctx_t *ctx = enc->ctx;
return ctx && ctx->filter_by_cpu && (esp_cpu_get_core_id() != ctx->dest_cpu);
}

void esp_sysview_function_trace_register(void)
{
if (!s_registered) {
SEGGER_SYSVIEW_RegisterModule(&s_ft_module);
s_registered = true;
}
}

void esp_sysview_function_enter(struct esp_trace_encoder *enc, void *func, void *call_site)
{
if (!s_registered || should_drop_for_cpu(enc)) {
return;
}
U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32];
U8 *pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket);
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)func);
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)call_site);
SEGGER_SYSVIEW_SendPacket(aPacket, pPayload, s_ft_module.EventOffset + FT_EVT_ENTER);
}

void esp_sysview_function_exit(struct esp_trace_encoder *enc, void *func, void *call_site)
{
if (!s_registered || should_drop_for_cpu(enc)) {
return;
}
U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32];
U8 *pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket);
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)func);
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)call_site);
SEGGER_SYSVIEW_SendPacket(aPacket, pPayload, s_ft_module.EventOffset + FT_EVT_EXIT);
}
Loading