Skip to content

Commit 5892a12

Browse files
committed
feat(esp_sysview): add function tracing SystemView backend
1 parent 3031c1c commit 5892a12

9 files changed

Lines changed: 176 additions & 10 deletions

File tree

esp_sysview/CMakeLists.txt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
2-
set(include_dirs "")
3-
set(srcs "")
1+
set(include_dirs "")
2+
set(srcs "")
3+
set(requires "esp_trace")
4+
set(priv_requires "esp_app_format")
45

6+
if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
57
list(APPEND include_dirs
68
src/Config
79
src/SEGGER
@@ -17,6 +19,10 @@ if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
1719
src/esp/adapter_encoder_sysview.c
1820
src/ext/logging.c)
1921

22+
if(CONFIG_ESP_TRACE_FUNCTION_TRACE)
23+
list(APPEND srcs src/esp/function_trace_sysview.c)
24+
endif()
25+
2026
if(CONFIG_HEAP_TRACING_TOHOST)
2127
list(APPEND srcs
2228
src/ext/heap_trace_module.c
@@ -30,7 +36,8 @@ if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
3036

3137
idf_component_register(SRCS "${srcs}"
3238
INCLUDE_DIRS "${include_dirs}"
33-
REQUIRES esp_trace
39+
REQUIRES "${requires}"
40+
PRIV_REQUIRES "${priv_requires}"
3441
LDFRAGMENTS ${CMAKE_CURRENT_BINARY_DIR}/linker.lf
3542
WHOLE_ARCHIVE TRUE # Link all symbols so self-registering adapters (ESP_TRACE_REGISTER_*) are not discarded
3643
)
@@ -40,5 +47,5 @@ if(CONFIG_ESP_TRACE_LIB_EXTERNAL)
4047
idf_component_get_property(freertos_lib freertos COMPONENT_LIB)
4148
target_include_directories(${freertos_lib} INTERFACE ${include_dirs})
4249
else()
43-
idf_component_register(REQUIRES "esp_trace")
50+
idf_component_register(REQUIRES "${requires}" PRIV_REQUIRES "${priv_requires}")
4451
endif()

esp_sysview/Kconfig

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
menu "SEGGER SystemView Configuration"
22
depends on ESP_TRACE_LIB_EXTERNAL
3+
4+
config SEGGER_SYSVIEW_UART_AVAILABLE
5+
bool
6+
default y if ESP_TRACE_TRANSPORT_APPTRACE && (APPTRACE_DEST_UART || APPTRACE_DEST_ALL)
7+
38
choice SEGGER_SYSVIEW_DEST_CPU
49
prompt "CPU to trace"
5-
depends on ESP_TRACE_TRANSPORT_APPTRACE && APPTRACE_DEST_UART && !ESP_SYSTEM_SINGLE_CORE_MODE
10+
depends on SEGGER_SYSVIEW_UART_AVAILABLE && !ESP_SYSTEM_SINGLE_CORE_MODE
611
default SEGGER_SYSVIEW_DEST_CPU_0
712
help
8-
Define the CPU to trace.
13+
Select the CPU to trace when SystemView uses a single UART stream.
14+
15+
UART transport carries one SystemView stream, so events from only one
16+
CPU can be represented reliably. JTAG tracing can keep
17+
events from multiple CPUs and this option is ignored there.
918

1019
config SEGGER_SYSVIEW_DEST_CPU_0
1120
bool "CPU0"

esp_sysview/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ Configure SystemView tracing in `idf.py menuconfig`:
1919
- Select timestamp source: Component config > ESP Trace Configuration > Trace timestamp source
2020
- Configure event filters: Component config > SEGGER SystemView Configuration
2121

22+
## Function tracing
23+
24+
When `CONFIG_ESP_TRACE_FUNCTION_TRACE` is enabled, this component implements the
25+
`esp_trace` function-trace encoder callbacks and sends enter/exit events through
26+
a dedicated SystemView module (`ESP_FunctionTrace`). Host-driven start/stop (over
27+
JTAG or UART) is propagated to the function-trace runtime so recording follows
28+
the SystemView session. Addresses are sent as `U32`. SystemView records them as
29+
raw addresses. Symbols can be mapped to function names offline against the ELF file (for example
30+
with `addr2line`).
31+
2232
## Documentation and examples
2333

2434
- ESP-IDF examples:

esp_sysview/idf_component.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: 1.0.2
1+
version: 1.1.0
22
description: SEGGER SystemView component for ESP-IDF
33
url: https://github.com/espressif/idf-extra-components/tree/master/esp_sysview
44
issues: https://github.com/espressif/idf-extra-components/issues

esp_sysview/src/SEGGER/SEGGER_SYSVIEW.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,19 @@ Additional information:
150150
#include <string.h>
151151
#include <stdlib.h>
152152
#include <stdarg.h>
153+
#include <stdbool.h>
153154
#include <assert.h>
154155
#include "SEGGER_SYSVIEW_Int.h"
155156
#include "SEGGER_RTT.h"
156157

158+
/*
159+
* ESP: notify esp_trace core so host-driven start/stop propagates to
160+
* dependent features (e.g. function tracing). Declared weak so esp_sysview
161+
* still links against an esp_trace revision that does not provide it.
162+
* The notification becomes a no-op when the symbol is absent.
163+
*/
164+
extern void esp_trace_notify_recording_state(bool active) __attribute__((weak));
165+
157166
/*********************************************************************
158167
*
159168
* Defines, fixed
@@ -1902,6 +1911,7 @@ void SEGGER_SYSVIEW_RecordString(unsigned int EventID, const char *pString)
19021911
*/
19031912
void SEGGER_SYSVIEW_Start(void)
19041913
{
1914+
U8 WasEnabled = _SYSVIEW_Globals.EnableState; /* ESP */
19051915
#if (SEGGER_SYSVIEW_CAN_RESTART == 0)
19061916
if (_SYSVIEW_Globals.EnableState == 0) {
19071917
#endif
@@ -1933,10 +1943,24 @@ void SEGGER_SYSVIEW_Start(void)
19331943
SEGGER_SYSVIEW_RecordSystime();
19341944
SEGGER_SYSVIEW_SendTaskList();
19351945
SEGGER_SYSVIEW_SendNumModules();
1946+
/* ESP: send module descriptions on start so streaming captures (e.g. over
1947+
* JTAG, where the host never requests them) are self-describing. */
1948+
if (_NumModules > 0) {
1949+
int n;
1950+
for (n = 0; n < _NumModules; n++) {
1951+
SEGGER_SYSVIEW_SendModule(n);
1952+
}
1953+
}
19361954
#endif
19371955
#if (SEGGER_SYSVIEW_CAN_RESTART == 0)
19381956
}
19391957
#endif
1958+
/* ESP: notify on an actual disabled->enabled transition, or unconditionally
1959+
* for restart-capable builds. This avoids resetting dependent features
1960+
* (e.g. function tracing) on redundant Start commands. */
1961+
if ((WasEnabled == 0 || SEGGER_SYSVIEW_CAN_RESTART) && esp_trace_notify_recording_state) {
1962+
esp_trace_notify_recording_state(true); /* ESP */
1963+
}
19401964
}
19411965

19421966
/*********************************************************************
@@ -1967,6 +1991,9 @@ void SEGGER_SYSVIEW_Stop(void)
19671991
_SYSVIEW_Globals.EnableState = 0;
19681992
}
19691993
RECORD_END();
1994+
if (esp_trace_notify_recording_state) {
1995+
esp_trace_notify_recording_state(false); /* ESP */
1996+
}
19701997
}
19711998

19721999
U8 SEGGER_SYSVIEW_Started(void)

esp_sysview/src/Sample/FreeRTOSV10.4/Config/esp/SEGGER_SYSVIEW_Config_FreeRTOS.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Revision: $Rev: 7745 $
6262
#include "sdkconfig.h"
6363
#include "freertos/FreeRTOS.h"
6464
#include "SEGGER_SYSVIEW.h"
65+
#include "esp_app_desc.h"
6566
#include "esp_intr_alloc.h"
6667
#include "soc/soc.h"
6768
#include "soc/interrupts.h"
@@ -128,6 +129,11 @@ static void _cbSendSystemDesc(void)
128129
strncat(irq_str, esp_isr_names[i], sizeof(irq_str) - strlen(irq_str) - 1);
129130
SEGGER_SYSVIEW_SendSysDesc(irq_str);
130131
}
132+
/* Application ELF identity so host tools can resolve the matching ELF */
133+
char elf_desc[sizeof("ELF_SHA256=") + CONFIG_APP_RETRIEVE_LEN_ELF_SHA];
134+
strlcpy(elf_desc, "ELF_SHA256=", sizeof(elf_desc));
135+
strlcat(elf_desc, esp_app_get_elf_sha256_str(), sizeof(elf_desc));
136+
SEGGER_SYSVIEW_SendSysDesc(elf_desc);
131137
}
132138

133139
/*********************************************************************

esp_sysview/src/esp/adapter_encoder_sysview.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
* All encoding and transport operations are done by SystemView component. (SEGGER_RTT_esp.c)
2424
*/
2525

26+
extern void esp_trace_notify_recording_state(bool active) __attribute__((weak));
27+
2628
#define SYSVIEW_FLUSH_TMO_US (1000 * 1000) /* 1second */
2729
#define SYSVIEW_FLUSH_THRESH 0
2830

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

56+
#if CONFIG_SEGGER_SYSVIEW_DEST_CPU_1
57+
dest_cpu = 1;
58+
#endif
59+
5460
if (cfg) {
5561
dest_cpu = cfg->dest_cpu;
5662
}
@@ -64,6 +70,10 @@ static esp_err_t init(esp_trace_encoder_t *enc, const void *enc_cfg)
6470
/* Segger Sysview needs locking mechanism. */
6571
esp_trace_lock_init(&ctx->lock);
6672
ctx->dest_cpu = dest_cpu;
73+
ctx->filter_by_cpu = true;
74+
if (enc->tp && enc->tp->vt && enc->tp->vt->get_link_type) {
75+
ctx->filter_by_cpu = (enc->tp->vt->get_link_type(enc->tp) != ESP_TRACE_LINK_DEBUG_PROBE);
76+
}
6777
enc->ctx = ctx;
6878

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

8595
SEGGER_SYSVIEW_Conf();
8696

97+
#if CONFIG_ESP_TRACE_FUNCTION_TRACE
98+
esp_sysview_function_trace_register();
99+
#endif
100+
101+
if (esp_trace_notify_recording_state) {
102+
esp_trace_notify_recording_state(SEGGER_SYSVIEW_Started() != 0);
103+
}
104+
87105
initialized = true;
88106

89107
return ESP_OK;
@@ -142,6 +160,10 @@ static const esp_trace_encoder_vtable_t s_sysview_vt = {
142160
.panic_handler = panic_handler,
143161
.take_lock = take_lock,
144162
.give_lock = give_lock,
163+
#if CONFIG_ESP_TRACE_FUNCTION_TRACE
164+
.function_enter = esp_sysview_function_enter,
165+
.function_exit = esp_sysview_function_exit,
166+
#endif
145167
};
146168

147169
ESP_TRACE_REGISTER_ENCODER("sysview", &s_sysview_vt);

esp_sysview/src/esp/adapter_encoder_sysview.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
#pragma once
77

8+
#include <stdbool.h>
89
#include "esp_trace_util.h"
910

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

46+
/* Forward declaration to avoid pulling the encoder port header here. */
47+
struct esp_trace_encoder;
48+
49+
/** @brief Send a function entry event (esp_trace function-trace callback). */
50+
void esp_sysview_function_enter(struct esp_trace_encoder *enc, void *func, void *call_site);
51+
52+
/** @brief Send a function exit event (esp_trace function-trace callback). */
53+
void esp_sysview_function_exit(struct esp_trace_encoder *enc, void *func, void *call_site);
54+
55+
/** @brief Register the function-trace SystemView module (call once at init). */
56+
void esp_sysview_function_trace_register(void);
57+
4458
#ifdef __cplusplus
4559
}
4660
#endif
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/*
8+
* SystemView backend for esp_trace compiler-instrumented function tracing.
9+
*/
10+
11+
#include <stdbool.h>
12+
#include <stdint.h>
13+
#include "esp_cpu.h"
14+
#include "esp_trace_port_encoder.h"
15+
#include "adapter_encoder_sysview.h"
16+
#include "SEGGER_SYSVIEW.h"
17+
18+
/* Event IDs sent as EventOffset + id. EventOffset is allocated in registration order starting at 512. */
19+
enum {
20+
FT_EVT_ENTER = 0,
21+
FT_EVT_EXIT,
22+
FT_EVT_COUNT,
23+
};
24+
25+
static SEGGER_SYSVIEW_MODULE s_ft_module = {
26+
.sModule =
27+
"M=ESP_FunctionTrace, "
28+
"0 function_enter func=%p call_site=%p, "
29+
"1 function_exit func=%p call_site=%p",
30+
.NumEvents = FT_EVT_COUNT,
31+
};
32+
33+
static bool s_registered;
34+
35+
static inline bool should_drop_for_cpu(struct esp_trace_encoder *enc)
36+
{
37+
sysview_encoder_ctx_t *ctx = enc->ctx;
38+
return ctx && ctx->filter_by_cpu && (esp_cpu_get_core_id() != ctx->dest_cpu);
39+
}
40+
41+
void esp_sysview_function_trace_register(void)
42+
{
43+
if (!s_registered) {
44+
SEGGER_SYSVIEW_RegisterModule(&s_ft_module);
45+
s_registered = true;
46+
}
47+
}
48+
49+
void esp_sysview_function_enter(struct esp_trace_encoder *enc, void *func, void *call_site)
50+
{
51+
if (!s_registered || should_drop_for_cpu(enc)) {
52+
return;
53+
}
54+
U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32];
55+
U8 *pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket);
56+
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)func);
57+
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)call_site);
58+
SEGGER_SYSVIEW_SendPacket(aPacket, pPayload, s_ft_module.EventOffset + FT_EVT_ENTER);
59+
}
60+
61+
void esp_sysview_function_exit(struct esp_trace_encoder *enc, void *func, void *call_site)
62+
{
63+
if (!s_registered || should_drop_for_cpu(enc)) {
64+
return;
65+
}
66+
U8 aPacket[SEGGER_SYSVIEW_INFO_SIZE + 2 * SEGGER_SYSVIEW_QUANTA_U32];
67+
U8 *pPayload = SEGGER_SYSVIEW_PREPARE_PACKET(aPacket);
68+
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)func);
69+
pPayload = SEGGER_SYSVIEW_EncodeU32(pPayload, (U32)(uintptr_t)call_site);
70+
SEGGER_SYSVIEW_SendPacket(aPacket, pPayload, s_ft_module.EventOffset + FT_EVT_EXIT);
71+
}

0 commit comments

Comments
 (0)