mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feature(gdbstub): Move runtime gdbstub out of panic config
Closes https://github.com/espressif/esp-idf/pull/11569
This commit is contained in:
parent
cfa1896780
commit
47e400c296
@ -6,6 +6,17 @@ menu "GDB Stub"
|
||||
bool
|
||||
select FREERTOS_ENABLE_TASK_SNAPSHOT
|
||||
|
||||
config ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
bool "GDBStub at runtime"
|
||||
select ESP_GDBSTUB_ENABLED
|
||||
help
|
||||
Enable builtin GDBStub.
|
||||
This allows to debug the target device using serial port:
|
||||
- Run 'idf.py monitor'.
|
||||
- Wait for the device to initialize.
|
||||
- Press Ctrl+C to interrupt the execution and enter GDB attached to your device for debugging.
|
||||
NOTE: all UART input will be handled by GDBStub.
|
||||
|
||||
config ESP_GDBSTUB_SUPPORT_TASKS
|
||||
bool "Enable listing FreeRTOS tasks through GDB Stub"
|
||||
depends on ESP_GDBSTUB_ENABLED
|
||||
|
@ -18,18 +18,21 @@ menu "ESP System Settings"
|
||||
|
||||
config ESP_SYSTEM_PANIC_PRINT_HALT
|
||||
bool "Print registers and halt"
|
||||
depends on !ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
help
|
||||
Outputs the relevant registers over the serial port and halt the
|
||||
processor. Needs a manual reset to restart.
|
||||
|
||||
config ESP_SYSTEM_PANIC_PRINT_REBOOT
|
||||
bool "Print registers and reboot"
|
||||
depends on !ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
help
|
||||
Outputs the relevant registers over the serial port and immediately
|
||||
reset the processor.
|
||||
|
||||
config ESP_SYSTEM_PANIC_SILENT_REBOOT
|
||||
bool "Silent reboot"
|
||||
depends on !ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
help
|
||||
Just resets the processor without outputting anything
|
||||
|
||||
@ -40,16 +43,6 @@ menu "ESP System Settings"
|
||||
Invoke gdbstub on the serial port, allowing for gdb to attach to it to do a postmortem
|
||||
of the crash.
|
||||
|
||||
config ESP_SYSTEM_GDBSTUB_RUNTIME
|
||||
bool "GDBStub at runtime"
|
||||
select ESP_GDBSTUB_ENABLED
|
||||
help
|
||||
Enable GDBStub inclusion into firmware.
|
||||
This allows to debug the target device using serial port:
|
||||
- Run 'idf.py monitor'.
|
||||
- Wait for the device to initialize.
|
||||
- Press Ctrl+C to interrupt the execution and enter GDB attached to your device for debugging.
|
||||
NOTE: all UART input will be handled by GDBStub.
|
||||
endchoice
|
||||
|
||||
config ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS
|
||||
|
@ -65,10 +65,6 @@ Subsequent behavior of the panic handler can be set using :ref:`CONFIG_ESP_SYSTE
|
||||
|
||||
Start GDB server which can communicate with GDB over console UART port. This option will only provide read-only debugging or post-mortem debugging. See `GDB Stub`_ for more details.
|
||||
|
||||
- Invoke dynamic GDB Stub (``ESP_SYSTEM_GDBSTUB_RUNTIME``)
|
||||
|
||||
Start the GDB server which can communicate with GDB over the console UART port. This option allows the user to debug a program at run time and set breakpoints, alter the execution, etc. See `GDB Stub`_ for more details.
|
||||
|
||||
The behavior of the panic handler is affected by three other configuration options.
|
||||
|
||||
- If :ref:`CONFIG_ESP_DEBUG_OCDAWARE` is enabled (which is the default), the panic handler will detect whether a JTAG debugger is connected. If it is, execution will be halted and control will be passed to the debugger. In this case, registers and backtrace are not dumped to the console, and GDBStub / Core Dump functions are not used.
|
||||
|
@ -240,7 +240,7 @@ Launching GDB with GDBStub
|
||||
|
||||
GDBStub is a useful runtime debugging feature that runs on the target and connects to the host over the serial port to receive debugging commands. GDBStub supports commands such as reading memory and variables, examining call stack frames etc. Although GDBStub is less versatile than JTAG debugging, it does not require any special hardware (such as a JTAG to USB bridge) as communication is done entirely over the serial port.
|
||||
|
||||
A target can be configured to run GDBStub in the background by setting the :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``GDBStub on runtime``. GDBStub will run in the background until a ``Ctrl+C`` message is sent over the serial port and causes the GDBStub to break (i.e., stop the execution of) the program, thus allowing GDBStub to handle debugging commands.
|
||||
A target can be configured to run GDBStub in the background by setting the :ref:`CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME`. GDBStub will run in the background until a ``Ctrl+C`` message is sent over the serial port and causes the GDBStub to break (i.e., stop the execution of) the program, thus allowing GDBStub to handle debugging commands.
|
||||
|
||||
Furthermore, the panic handler can be configured to run GDBStub on a crash by setting the :ref:`CONFIG_ESP_SYSTEM_PANIC` to ``GDBStub on panic``. When a crash occurs, GDBStub will output a special string pattern over the serial port to indicate that it is running.
|
||||
|
||||
|
@ -65,10 +65,6 @@
|
||||
|
||||
启动 GDB 服务器,通过控制台 UART 接口与 GDB 进行通信。该选项只提供只读调试或者事后调试,详细信息请参阅 `GDB Stub`_。
|
||||
|
||||
- 调用动态 GDB Stub (``ESP_SYSTEM_GDBSTUB_RUNTIME``)
|
||||
|
||||
启动 GDB 服务器,通过控制台 UART 接口与 GDB 进行通信。该选项允许用户在程序运行时对其进行调试、设置断点和改变其执行方式等,详细信息请参阅 `GDB Stub`_。
|
||||
|
||||
紧急处理程序的行为还受到另外两个配置项的影响:
|
||||
|
||||
- 如果使能了 :ref:`CONFIG_ESP_DEBUG_OCDAWARE` (默认),紧急处理程序会检测 {IDF_TARGET_NAME} 是否已经连接 JTAG 调试器。如果检测成功,程序会暂停运行,并将控制权交给调试器。在这种情况下,寄存器和回溯不会被打印到控制台,并且也不会使用 GDB Stub 和 Core Dump 的功能。
|
||||
|
@ -240,7 +240,7 @@ ROM ELF 文件会根据 ``IDF_PATH`` 和 ``ESP_ROM_ELF_DIR`` 环境变量的路
|
||||
|
||||
GDBStub 支持在运行时进行调试。GDBStub 在目标上运行,并通过串口连接到主机从而接收调试命令。GDBStub 支持读取内存和变量、检查调用堆栈帧等命令。虽然没有 JTAG 调试通用,但由于 GDBStub 完全通过串行端口完成通信,故不需要使用特殊硬件(如 JTAG/USB 桥接器)。
|
||||
|
||||
通过将 :ref:`CONFIG_ESP_SYSTEM_PANIC` 设置为 ``GDBStub on runtime``,可以将目标配置为在后台运行 GDBStub。GDBStub 将保持在后台运行,直到通过串行端口发送 ``Ctrl+C`` 导致应用程序中断(即停止程序执行),从而让 GDBStub 处理调试命令。
|
||||
通过设置 :ref:`CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME`,可以将目标配置为在后台运行 GDBStub。GDBStub 将保持在后台运行,直到通过串行端口发送 ``Ctrl+C`` 导致应用程序中断(即停止程序执行),从而让 GDBStub 处理调试命令。
|
||||
|
||||
此外,还可以通过设置 :ref:`CONFIG_ESP_SYSTEM_PANIC` 为 ``GDBStub on panic`` 来配置 panic 处理程序,使其在发生 crash 事件时运行 GDBStub。当 crash 发生时,GDBStub 将通过串口输出特殊的字符串模式,表示 GDBStub 正在运行。
|
||||
|
||||
|
@ -26,11 +26,9 @@ idf.py menuconfig
|
||||
```
|
||||
Current example is pre-configured. The user can scroll through the system parameters and see the settings.
|
||||
Most important one is:
|
||||
-> Component Config -> ESP System Settings -> Panic handler behaviour -> GDBStub on runtime
|
||||
-> Component Config -> GDB Stub -> GDBStub on runtime
|
||||
This selection switches gdbstub to runtime mode.
|
||||
Depending on the project, following settings could be used:
|
||||
-> Component Config -> GDB Stub -> ...
|
||||
The user can enable or disable task list handling and define a maximum amount of tasks.
|
||||
Using another options in this menu, the user can also enable or disable task list handling and define a maximum amount of tasks.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
|
@ -1,18 +1,7 @@
|
||||
#
|
||||
# GDB Stub
|
||||
#
|
||||
CONFIG_ESP_GDBSTUB_ENABLED=y
|
||||
CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME=y
|
||||
CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
|
||||
CONFIG_ESP_GDBSTUB_MAX_TASKS=32
|
||||
# end of GDB Stub
|
||||
|
||||
#
|
||||
# ESP System Settings
|
||||
#
|
||||
# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set
|
||||
# CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT is not set
|
||||
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
|
||||
CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME=y
|
||||
# CONFIG_ESP_INT_WDT is not set
|
||||
# CONFIG_ESP_TASK_WDT_EN is not set
|
||||
# end of ESP System Settings
|
||||
|
@ -1,3 +1,5 @@
|
||||
idf_component_register(SRCS "test_app_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
REQUIRES esp_gdbstub)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-unused-label")
|
||||
|
@ -5,22 +5,37 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
int var_1;
|
||||
int var_2;
|
||||
int do_panic;
|
||||
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
var_2++;
|
||||
var_2+=2;
|
||||
var_2--;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("tested app is runnig.\n");
|
||||
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
|
||||
while(1) {
|
||||
var_1++;
|
||||
if (var_1 % 10 == 0) {
|
||||
label_1:
|
||||
foo();
|
||||
}
|
||||
label_3:
|
||||
if (do_panic) {
|
||||
char *t = NULL;
|
||||
label_5:
|
||||
*t = 'X';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,24 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import os
|
||||
import os.path as path
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.append(os.path.expandvars(os.path.join('$IDF_PATH', 'tools', 'test_apps', 'system', 'panic')))
|
||||
sys.path.append(path.expandvars(path.join('$IDF_PATH', 'tools', 'test_apps', 'system', 'panic')))
|
||||
from test_panic_util import PanicTestDut # noqa: E402
|
||||
|
||||
|
||||
def get_line_number(lookup: str, offset: int = 0) -> int:
|
||||
src_file = path.join(path.dirname(path.abspath(__file__)), 'main', 'test_app_main.c')
|
||||
with open(src_file) as f:
|
||||
for num, line in enumerate(f, 1):
|
||||
if lookup in line:
|
||||
return num + offset
|
||||
return -1
|
||||
|
||||
|
||||
@pytest.mark.supported_targets
|
||||
@pytest.mark.generic
|
||||
def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
@ -18,21 +27,21 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
dut.start_gdb()
|
||||
|
||||
# Test breakpoint
|
||||
cmd = '-break-insert --source test_app_main.c --line 23'
|
||||
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
||||
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||
assert response is not None
|
||||
cmd = '-exec-continue'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# does not stoped on breakpoint yet
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'breakpoint-hit'
|
||||
assert payload['bkptno'] == '1'
|
||||
assert payload['frame']['func'] == 'app_main'
|
||||
assert payload['frame']['line'] == '23'
|
||||
assert payload['frame']['line'] == str(get_line_number('label_1:', 1))
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
# Test step command
|
||||
@ -40,13 +49,13 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# does not stoped on breakpoint yet
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'end-stepping-range'
|
||||
assert payload['frame']['func'] == 'foo'
|
||||
assert payload['frame']['line'] == '14'
|
||||
assert payload['frame']['line'] == str(get_line_number('var_2+=2;'))
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
# Test finish command
|
||||
@ -54,26 +63,36 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# does not stoped on breakpoint yet
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'function-finished'
|
||||
assert payload['frame']['line'] == '23'
|
||||
# On riscv we may have situation when returned from a function but stay on exactly the same line
|
||||
# foo();
|
||||
# 4200ae5c: f99ff0ef jal ra,4200adf4 <foo>
|
||||
# 4200ae60: a011 j 4200ae64 <app_main+0x4e> <----------- here after return from foo()
|
||||
# }
|
||||
assert payload['frame']['line'] == str(get_line_number('label_3:', 1) if dut.is_xtensa else get_line_number('foo();', 0))
|
||||
assert payload['frame']['func'] == 'app_main'
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
cmd = '-exec-continue'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
assert dut.find_gdb_response('running', 'notify', responses) is not None
|
||||
|
||||
# Test next command
|
||||
cmd = '-exec-next'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# does not stoped on breakpoint yet
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'end-stepping-range'
|
||||
assert payload['frame']['line'] == '21'
|
||||
assert payload['frame']['line'] == str(get_line_number('label_3:', 1))
|
||||
assert payload['frame']['func'] == 'app_main'
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
@ -100,7 +119,7 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# does not stoped on breakpoint yet
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'signal-received'
|
||||
@ -111,3 +130,83 @@ def test_gdbstub_runtime(dut: PanicTestDut) -> None:
|
||||
# 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)
|
||||
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||
|
||||
# test set variable
|
||||
cmd = '-gdb-set do_panic=1'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('done', 'result', responses) is not None
|
||||
|
||||
# test panic handling
|
||||
cmd = '-exec-continue'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'signal-received'
|
||||
assert payload['signal-name'] == 'SIGSEGV'
|
||||
assert payload['frame']['func'] == 'app_main'
|
||||
assert payload['frame']['line'] == str(get_line_number('label_5', 1))
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='fix IDF-7927')
|
||||
def test_gdbstub_runtime_xtensa_stepping_bug(dut: PanicTestDut) -> None:
|
||||
dut.expect_exact('tested app is runnig.')
|
||||
dut.write(b'\x03') # send Ctrl-C
|
||||
dut.start_gdb()
|
||||
|
||||
# Test breakpoint
|
||||
cmd = '-break-insert --source test_app_main.c --function app_main --label label_1'
|
||||
response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd))
|
||||
assert response is not None
|
||||
cmd = '-exec-continue'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'breakpoint-hit'
|
||||
assert payload['bkptno'] == '1'
|
||||
assert payload['frame']['func'] == 'app_main'
|
||||
assert payload['frame']['line'] == str(get_line_number('label_1:', 1))
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
# Test step command
|
||||
cmd = '-exec-step'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'end-stepping-range'
|
||||
assert payload['frame']['func'] == 'foo'
|
||||
assert payload['frame']['line'] == str(get_line_number('var_2+=2;'))
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
||||
# Test next command
|
||||
cmd = '-exec-next'
|
||||
responses = dut.gdb_write(cmd)
|
||||
assert dut.find_gdb_response('running', 'result', responses) is not None
|
||||
if not dut.find_gdb_response('stopped', 'notify', responses):
|
||||
# have not stopped on breakpoint yet
|
||||
responses = dut.gdbmi.get_gdb_response(timeout_sec=3)
|
||||
assert dut.find_gdb_response('stopped', 'notify', responses) is not None
|
||||
payload = dut.find_gdb_response('stopped', 'notify', responses)['payload']
|
||||
assert payload['reason'] == 'end-stepping-range'
|
||||
assert payload['frame']['line'] == str(get_line_number('var_2--;', 0))
|
||||
assert payload['frame']['func'] == 'foo'
|
||||
assert payload['stopped-threads'] == 'all'
|
||||
|
Loading…
x
Reference in New Issue
Block a user