feat(esp_gdbstub): support watchpoint trigger reason

This commit is contained in:
Alexey Lapshin
2026-02-18 12:16:19 +07:00
parent f40dc50300
commit 6019f9689a
5 changed files with 81 additions and 6 deletions
@@ -169,6 +169,15 @@ void esp_gdbstub_clear_step(void);
void esp_gdbstub_do_step(esp_gdbstub_frame_t *regs_frame);
void esp_gdbstub_trigger_cpu(void);
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/**
* Check if a watchpoint triggered the current debug exception.
* @param[out] addr Address of the triggered watchpoint (only valid if return is true)
* @return true if a watchpoint triggered, false otherwise (breakpoint/step/other)
*/
bool esp_gdbstub_get_watchpoint_trigger_addr(uint32_t *addr);
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/**
* Write a value to register in frame
* @param frame gdbstub frame
+36
View File
@@ -28,6 +28,10 @@
#define GDBSTUB_QXFER_SUPPORTED_STR ""
#endif
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static void send_watchpoint_reason(void);
#endif
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static inline int gdb_tid_to_task_index(int tid);
static inline int task_index_to_gdb_tid(int tid);
@@ -107,6 +111,9 @@ static void send_reason(void)
esp_gdbstub_send_start();
esp_gdbstub_send_char('T');
esp_gdbstub_send_hex(s_scratch.signal, 8);
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
send_watchpoint_reason();
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
esp_gdbstub_send_end();
}
@@ -212,6 +219,35 @@ static bool not_send_reason = false;
static bool process_gdb_kill = false;
static bool gdb_debug_int = false;
/**
* Detect if a watchpoint triggered and append the corresponding
* GDB RSP stop-reply field (watch/rwatch/awatch) to the current packet.
*/
static void send_watchpoint_reason(void)
{
uint32_t wp_addr = 0;
if (!esp_gdbstub_get_watchpoint_trigger_addr(&wp_addr)) {
return;
}
const char *type_str = "watch";
for (size_t i = 0; i < SOC_CPU_WATCHPOINTS_NUM; i++) {
if (wp_list[i] == wp_addr) {
if (wp_access[i] == ESP_CPU_WATCHPOINT_LOAD) {
type_str = "rwatch";
} else if (wp_access[i] == ESP_CPU_WATCHPOINT_ACCESS) {
type_str = "awatch";
}
break;
}
}
esp_gdbstub_send_str(type_str);
esp_gdbstub_send_char(':');
esp_gdbstub_send_hex(wp_addr, 32);
esp_gdbstub_send_char(';');
}
/**
* @brief Handle UART interrupt
*
@@ -250,6 +250,22 @@ void esp_gdbstub_init_dports(void)
{
}
bool esp_gdbstub_get_watchpoint_trigger_addr(uint32_t *addr)
{
for (int i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) {
RV_WRITE_CSR(tselect, i);
uint32_t tdata1 = RV_READ_CSR(tdata1);
bool is_load = tdata1 & TDATA1_LOAD;
bool is_store = tdata1 & TDATA1_STORE;
bool is_exec = tdata1 & TDATA1_EXECUTE;
if (!is_exec && (is_load || is_store)) {
*addr = RV_READ_CSR(tdata2);
return true;
}
}
return false;
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#if (!CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
@@ -307,6 +307,23 @@ void esp_gdbstub_init_dports(void)
{
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
bool esp_gdbstub_get_watchpoint_trigger_addr(uint32_t *addr)
{
uint32_t debugcause;
RSR(XT_REG_DEBUGCAUSE, debugcause);
if (debugcause & XCHAL_DEBUGCAUSE_DBREAK_MASK) {
if (debugcause & (1 << 8)) {
RSR(XT_REG_DBREAKA_1, *addr);
} else {
RSR(XT_REG_DBREAKA_0, *addr);
}
return true;
}
return false;
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static bool stall_started = false;
#endif
@@ -368,14 +368,11 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
assert dut.find_gdb_response('done', 'result', responses) is not None
cmd = '-exec-continue'
payload = run_and_break(dut, cmd)
assert payload['reason'] == 'signal-received'
assert payload['reason'] == 'watchpoint-trigger'
assert int(payload['value']['new']) == int(payload['value']['old']) + 2
assert payload['frame']['func'] == 'foo'
assert payload['frame']['line'] == str(get_line_number('var_2--;'))
assert payload['stopped-threads'] == 'all'
# Uncomment this when implement send reason to gdb: GCC-313
#
# assert payload['reason'] == 'watchpoint-trigger'
# assert int(payload['value']['new']) == int(payload['value']['old']) + 1
# assert payload['frame']['line'] == '14'
cmd = '-break-delete 2'
responses = dut.gdb_write(cmd)