Merge branch 'feature/move-gdb-options-to_project_description_json_v5.3' into 'release/v5.3'

feat(debugging): move gdbinit generation to CMake (v5.3)

See merge request espressif/esp-idf!35176
This commit is contained in:
Roland Dobai
2024-12-06 19:29:12 +08:00
20 changed files with 504 additions and 428 deletions

View File

@ -152,6 +152,10 @@ if(CONFIG_COMPILER_DUMP_RTL_FILES)
list(APPEND compile_options "-fdump-rtl-expand")
endif()
idf_build_set_property(GDBINIT_FILES_PREFIX_MAP "${BUILD_DIR}/gdbinit/prefix_map")
file(MAKE_DIRECTORY "${BUILD_DIR}/gdbinit")
file(WRITE "${BUILD_DIR}/gdbinit/prefix_map" "")
if(NOT ${CMAKE_C_COMPILER_VERSION} VERSION_LESS 8.0.0)
if(CONFIG_COMPILER_HIDE_PATHS_MACROS)
list(APPEND compile_options "-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=.")

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import json
import os
import sys
from textwrap import indent
IDF_PATH = os.getenv('IDF_PATH', '')
ROMS_JSON = os.path.join(IDF_PATH, 'tools', 'idf_py_actions', 'roms.json') # type: ignore
# Direct string extraction and comparison is not feasible due to:
# 1. ROM ELF binaries for esp32XX chips are little-endian. The byte order in
# the binary may differ from the system's endianness. We must ensure the
# bytes are read correctly for the system where the script runs.
# 2. GDB lacks built-in string comparison functionality. To work around this,
# strings are converted to numeric values for comparison.
def get_rom_if_condition_str(date_addr: int, date_str: str) -> str:
r = []
for i in range(0, len(date_str), 4):
value = hex(int.from_bytes(bytes(date_str[i:i + 4], 'utf-8'), 'little'))
r.append(f'(*(int*) {hex(date_addr + i)}) == {value}')
return 'if ' + ' && '.join(r)
def generate_gdbinit_rom_add_symbols(target: str) -> str:
base_ident = ' '
rom_elfs_dir = os.getenv('ESP_ROM_ELF_DIR')
if not rom_elfs_dir:
raise EnvironmentError(
'ESP_ROM_ELF_DIR environment variable is not defined. Please try to run IDF "install" and "export" scripts.')
if os.name == 'nt':
# convert to posix-path for windows
rom_elfs_dir = rom_elfs_dir.replace('\\', '/')
with open(ROMS_JSON, 'r') as f:
roms = json.load(f)
if target not in roms:
msg_body = f'Warning: ROM ELF is not supported yet for "{target}".' # noqa: E713
return f'echo {msg_body}\\n\n'
r = ['', f'# Load {target} ROM ELF symbols']
r.append('define target hookpost-remote')
r.append('set confirm off')
# Since GDB does not have 'else if' statement than we use nested 'if..else' instead.
for i, k in enumerate(roms[target], 1):
indent_str = base_ident * i
rom_file = f'{target}_rev{k["rev"]}_rom.elf'
build_date_addr = int(k['build_date_str_addr'], base=16)
r.append(indent(f'# if $_streq((char *) {hex(build_date_addr)}, "{k["build_date_str"]}")', indent_str))
r.append(indent(get_rom_if_condition_str(build_date_addr, k['build_date_str']), indent_str))
r.append(indent(f'add-symbol-file {rom_elfs_dir}{rom_file}', indent_str + base_ident))
r.append(indent('else', indent_str))
if i == len(roms[target]):
# In case no one known ROM ELF fits - print warning
indent_str += base_ident
msg_body = f'Warning: Unknown {target} ROM revision.'
r.append(indent(f'echo {msg_body}\\n', indent_str))
# Close 'else' operators
for i in range(len(roms[target]), 0, -1):
r.append(indent('end', base_ident * i))
r.append('set confirm on')
r.append('end')
return '\n'.join(r)[1:]
if __name__ == '__main__':
if len(sys.argv) != 2:
raise ValueError('Please pass only one argument (target).')
target = sys.argv[1]
gdbinit_lines = generate_gdbinit_rom_add_symbols(target)
print(gdbinit_lines)

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import json
import os
import elftools.common.utils as ecu
import jsonschema
from elftools.elf.elffile import ELFFile
from idf_build_apps.constants import SUPPORTED_TARGETS
IDF_PATH = os.getenv('IDF_PATH', '')
ROMS_JSON = os.path.join(IDF_PATH, 'tools', 'idf_py_actions', 'roms.json') # type: ignore
def test_roms_validate_json() -> None:
with open(ROMS_JSON, 'r') as f:
roms_json = json.load(f)
json_schema_path = os.path.join(os.path.dirname(ROMS_JSON), 'roms_schema.json')
with open(json_schema_path, 'r') as f:
schema_json = json.load(f)
jsonschema.validate(roms_json, schema_json)
def test_roms_check_supported_chips() -> None:
with open(ROMS_JSON, 'r') as f:
roms_json = json.load(f)
for chip in SUPPORTED_TARGETS:
assert chip in roms_json, f'Have no ROM data for chip {chip}'
def test_roms_validate_build_date() -> None:
def get_string_from_elf_by_addr(filename: str, address: int) -> str:
result = ''
with open(filename, 'rb') as stream:
elf_file = ELFFile(stream)
ro = elf_file.get_section_by_name('.rodata')
ro_addr_delta = ro['sh_addr'] - ro['sh_offset']
cstring = ecu.parse_cstring_from_stream(ro.stream, address - ro_addr_delta)
if cstring:
result = str(cstring.decode('utf-8'))
return result
rom_elfs_dir = os.getenv('ESP_ROM_ELF_DIR', '')
with open(ROMS_JSON, 'r') as f:
roms_json = json.load(f)
for chip in roms_json:
for k in roms_json[chip]:
rom_file = os.path.join(rom_elfs_dir, f'{chip}_rev{k["rev"]}_rom.elf')
build_date_str = get_string_from_elf_by_addr(rom_file, int(k['build_date_str_addr'], base=16))
assert len(build_date_str) == 11
assert build_date_str == k['build_date_str']

View File

@ -117,13 +117,17 @@ Setup of OpenOCD
.. highlight:: bash
If you have already set up ESP-IDF with CMake build system according to the :doc:`Getting Started Guide <../../get-started/index>`, then OpenOCD is already installed. After :ref:`setting up the environment <get-started-set-up-env>` in your terminal, you should be able to run OpenOCD. Check this by executing the following command::
If you have already set up ESP-IDF with CMake build system according to the :doc:`Getting Started Guide <../../get-started/index>`, then OpenOCD is already installed. After :ref:`setting up the environment <get-started-set-up-env>` in your terminal, you should be able to run OpenOCD. Check this by executing the following command:
.. code-block:: none
openocd --version
.. highlight:: none
The output should be as follows (although the version may be more recent than listed here)::
The output should be as follows (although the version may be more recent than listed here):
.. code-block:: none
Open On-Chip Debugger v0.12.0-esp32-20240318 (2024-03-18-18:25)
Licensed under GNU GPL v2
@ -174,7 +178,7 @@ Once target is configured and connected to computer, you are ready to launch Ope
.. highlight:: bash
Open a terminal and set it up for using the ESP-IDF as described in the :ref:`setting up the environment <get-started-set-up-env>` section of the Getting Started Guide. Then run OpenOCD (this command works on Windows, Linux, and macOS):
Open a terminal and set it up for using the ESP-IDF as described in the :ref:`setting up the environment <get-started-set-up-env>` section of the Getting Started Guide. To run OpenOCD for a specific board, you must pass the board-specific configuration. The default configuration for the built project can be found in the ``debug_arguments_openocd`` field of the ``build/project_description.json`` file. There is an example to run OpenOCD (this command works on Windows, Linux, and macOS):
.. include:: {IDF_TARGET_PATH_NAME}.inc
:start-after: run-openocd
@ -276,7 +280,7 @@ Before proceeding to examples, set up your {IDF_TARGET_NAME} target and load it
.. _jtag-debugging-building-openocd:
Building OpenOCD from Sources
---------------------------------
-----------------------------
Please refer to separate documents listed below, that describe build process.
@ -318,7 +322,7 @@ and Windows:
.. _jtag-debugging-tips-and-quirks:
Tips and Quirks
------------------
---------------
This section provides collection of links to all tips and quirks referred to from various parts of this guide.
@ -329,7 +333,7 @@ This section provides collection of links to all tips and quirks referred to fro
Related Documents
---------------------
-----------------
.. toctree::
:hidden:

View File

@ -23,8 +23,8 @@ Eclipse
Eclipse is an integrated development environment (IDE) that provides a powerful set of tools for developing and debugging software applications. For ESP-IDF applications, `IDF Eclipse plugin <https://github.com/espressif/idf-eclipse-plugin>`_ provides two ways of debugging:
1. `ESP-IDF GDB OpenOCD Debugging <https://github.com/espressif/idf-eclipse-plugin/blob/master/docs/OpenOCD%20Debugging.md#esp-idf-gdb-openocd-debugging>`_
2. GDB Hardware Debugging
1. `ESP-IDF GDB OpenOCD Debugging <https://github.com/espressif/idf-eclipse-plugin/blob/master/docs/OpenOCD%20Debugging.md#esp-idf-gdb-openocd-debugging>`_
2. GDB Hardware Debugging
By default, Eclipse supports OpenOCD Debugging via the GDB Hardware Debugging plugin, which requires starting the OpenOCD server from the command line and configuring the GDB client from Eclipse to start with the debugging. This approach can be time-consuming and error-prone.
@ -35,6 +35,7 @@ Therefore, it is recommended to use the `ESP-IDF GDB OpenOCD Debugging <https://
**GDB Hardware Debugging**
.. note::
This approach is recommended only if you are unable to debug using `ESP-IDF GDB OpenOCD Debugging <https://github.com/espressif/idf-eclipse-plugin/blob/master/docs/OpenOCD%20Debugging.md#esp-idf-gdb-openocd-debugging>`_ for some reason.
To install the ``GDB Hardware Debugging`` plugin, open Eclipse and select ``Help`` > ``Install`` New Software.
@ -75,14 +76,17 @@ After installation is complete, follow these steps to configure the debugging se
8. The last tab that requires changing the default configuration is ``Startup``. Under ``Initialization Commands`` uncheck ``Reset and Delay (seconds)`` and ``Halt``. Then, in the entry field below, enter the following lines:
::
.. code-block:: none
mon reset halt
maintenance flush register-cache
set remote hardware-watchpoint-limit 2
.. note::
To automatically update the image in the flash before starting a new debug session, add the following command lines to the beginning of the ``Initialization Commands`` textbox::
To automatically update the image in the flash before starting a new debug session, add the following command lines to the beginning of the ``Initialization Commands`` textbox:
.. code-block:: none
mon reset halt
mon program_esp ${workspace_loc:blink/build/blink.bin} 0x10000 verify
@ -108,16 +112,16 @@ After installation is complete, follow these steps to configure the debugging se
12. If you have completed the :ref:`jtag-debugging-configuring-target` steps described above, so the target is running and ready to talk to debugger, go right to debugging by pressing ``Debug`` button. Otherwise press ``Apply`` to save changes, go back to :ref:`jtag-debugging-configuring-target` and return here to start debugging.
Once all configuration steps 1-12 are satisfied, the new Eclipse perspective called "Debug" will open, as shown in the example picture below.
Once all configuration steps 1-12 are satisfied, the new Eclipse perspective called "Debug" will open, as shown in the example picture below.
.. figure:: ../../../_static/debug-perspective.jpg
:align: center
:alt: Debug Perspective in Eclipse
:figclass: align-center
.. figure:: ../../../_static/debug-perspective.jpg
:align: center
:alt: Debug Perspective in Eclipse
:figclass: align-center
Debug Perspective in Eclipse
Debug Perspective in Eclipse
If you are not quite sure how to use GDB, check :ref:`jtag-debugging-examples-eclipse` example debugging session in section :ref:`jtag-debugging-examples`.
If you are not quite sure how to use GDB, check :ref:`jtag-debugging-examples-eclipse` example debugging session in section :ref:`jtag-debugging-examples`.
.. _jtag-debugging-using-debugger-command-line:
@ -131,88 +135,70 @@ Command Line
2. Open a new terminal session and go to the directory that contains the project for debugging, e.g.,
::
.. code-block:: none
cd ~/esp/blink
.. highlight:: none
3. When launching a debugger, you will need to provide a couple of configuration parameters and commands. Instead of entering them one by one in the command line, create a configuration file and name it ``gdbinit``:
3. When launching a debugger, you will need to provide a couple of configuration parameters and commands. The build system generates several ``.gdbinit`` files to facilitate efficient debugging. Paths to these files can be found in the ``build/project_description.json``, under the ``gdbinit_files`` section. The paths to these files are defined as follows:
::
.. code-block:: json
target remote :3333
set remote hardware-watchpoint-limit 2
mon reset halt
maintenance flush register-cache
thb app_main
c
"gdbinit_files": {
"01_symbols": "application_path/build/gdbinit/symbols",
"02_prefix_map": "application_path/build/gdbinit/prefix_map",
"03_py_extensions": "application_path/build/gdbinit/py_extensions",
"04_connect": "application_path/build/gdbinit/connect"
}
Save this file in the current directory.
The ``XX_`` prefix in the JSON keys is included to have ability to sort them. Sorted fields indicate the recommended order in which to provide the data to GDB.
For more details on what is inside ``gdbinit`` file, see :ref:`jtag-debugging-tip-debugger-startup-commands`
Descriptions of the generated ``.gdbinit`` files:
* ``symbols`` - contains symbol sources for debugging.
* ``prefix_map`` - configures the prefix map to modify source paths in GDB. For more details, see :ref:`reproducible-builds-and-debugging`.
* ``py_extensions`` - initializes Python extensions in GDB. This requires Python built with ``libpython`` and a version supported by GDB. To verify compatibility, run ``{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb --batch-silent --ex "python import os"``, which should complete without errors.
* ``connect`` - contains commands necessary for establishing a connection to the target device.
To enhance your debugging experience, you can also create custom ``.gdbinit`` files, used either alongside or in place of the generated configurations.
.. highlight:: bash
4. Now you are ready to launch GDB. Type the following in terminal:
4. Now you are ready to launch GDB. Use the following example command to load symbols and connect to the target (``-q`` option added to minimize startup output):
::
.. code-block:: none
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -x gdbinit build/blink.elf
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -q -x build/gdbinit/symbols -x build/gdbinit/prefix_map -x build/gdbinit/connect build/blink.elf
.. highlight:: none
5. If the previous steps have been done correctly, you will see a similar log concluded with the ``(gdb)`` prompt:
::
.. code-block:: none
user-name@computer-name:~/esp/blink$ {IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -x gdbinit build/blink.elf
GNU gdb (crosstool-NG crosstool-ng-1.22.0-61-gab8375a) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-build_pc-linux-gnu --target={IDF_TARGET_TOOLCHAIN_PREFIX}".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from build/blink.elf...done.
0x400d10d8 in esp_vApplicationIdleHook () at /home/user-name/esp/esp-idf/components/{IDF_TARGET_PATH_NAME}/./freertos_hooks.c:52
52 asm("waiti 0");
JTAG tap: {IDF_TARGET_PATH_NAME}.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
JTAG tap: {IDF_TARGET_PATH_NAME}.slave tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
{IDF_TARGET_PATH_NAME}: Debug controller was reset (pwrstat=0x5F, after clear 0x0F).
{IDF_TARGET_PATH_NAME}: Core was reset (pwrstat=0x5F, after clear 0x0F).
Target halted. PRO_CPU: PC=0x5000004B (active) APP_CPU: PC=0x00000000
{IDF_TARGET_PATH_NAME}: target state: halted
{IDF_TARGET_PATH_NAME}: Core was reset (pwrstat=0x1F, after clear 0x0F).
Target halted. PRO_CPU: PC=0x40000400 (active) APP_CPU: PC=0x40000400
{IDF_TARGET_PATH_NAME}: target state: halted
Hardware assisted breakpoint 1 at 0x400db717: file /home/user-name/esp/blink/main/./blink.c, line 43.
0x0: 0x00000000
Target halted. PRO_CPU: PC=0x400DB717 (active) APP_CPU: PC=0x400D10D8
[New Thread 1073428656]
[New Thread 1073413708]
[New Thread 1073431316]
[New Thread 1073410672]
[New Thread 1073408876]
[New Thread 1073432196]
[New Thread 1073411552]
[Switching to Thread 1073411996]
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -q -x build/gdbinit/symbols -x build/gdbinit/prefix_map -x build/gdbinit/connect build/blink.elf
Temporary breakpoint 1, app_main () at /home/user-name/esp/blink/main/./blink.c:43
43 xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL);
user-name@computer-name:~/esp-idf/examples/get-started/blink$ {IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -q -x build/gdbinit/symbols -x build/gdbinit/connect build/blink.elf
Reading symbols from build/blink.elf...
add symbol table from file "/home/user-name/esp-idf/examples/get-started/blink/build/bootloader/bootloader.elf"
[Switching to Thread 1070141764]
app_main () at /home/user-name/esp-idf/examples/get-started/blink/main/blink_example_main.c:95
95 configure_led();
add symbol table from file "/home/alex/.espressif/tools/esp-rom-elfs/20241011/{IDF_TARGET_PATH_NAME}_rev0_rom.elf"
JTAG tap: {IDF_TARGET_PATH_NAME}.tap0 tap/device found: 0x00005c25 (mfg: 0x612 (Espressif Systems), part: 0x0005, ver: 0x0)
[{IDF_TARGET_PATH_NAME}] Reset cause (3) - (Software core reset)
Hardware assisted breakpoint 1 at 0x42009436: file /home/user-name/esp-idf/examples/get-started/blink/main/blink_example_main.c, line 92.
[Switching to Thread 1070139884]
Thread 2 "main" hit Temporary breakpoint 1, app_main () at /home/user-name/esp-idf/examples/get-started/blink/main/blink_example_main.c:92
92 {
(gdb)
Note the third-to-last line, which shows debugger halting at breakpoint established in ``gdbinit`` file at function ``app_main()``. Since the processor is halted, the LED should not be blinking. If this is what you see as well, you are ready to start debugging.
Note that the third-to-last line indicates the debugger has halted at a breakpoint established in ``build/gdbinit/connect`` file at function ``app_main()``. Since the processor is halted, the LED should not be blinking. If this matches what you observe, you are ready to start debugging.
If you are not sure how to use GDB, check :ref:`jtag-debugging-examples-command-line` example debugging session in section :ref:`jtag-debugging-examples`.
If you are not sure how to use GDB, check :ref:`jtag-debugging-examples-command-line` example debugging session in section :ref:`jtag-debugging-examples`.
.. _jtag-debugging-with-idf-py:
@ -236,10 +222,10 @@ It is also possible to execute the described debugging tools conveniently from `
2. ``idf.py gdb``
Starts the GDB the same way as the :ref:`jtag-debugging-using-debugger-command-line`, but generates the initial GDB scripts referring to the current project elf file.
Starts GDB in the same way as the :ref:`jtag-debugging-using-debugger-command-line`, using generated GDB scripts referring to the current project's ELF file. For more details, see :ref:`jtag-debugging-using-debugger-command-line`.
3. ``idf.py gdbtui``
3. ``idf.py gdbtui``
The same as `2`, but starts the gdb with ``tui`` argument, allowing for a simple source code view.
@ -251,7 +237,9 @@ It is also possible to execute the described debugging tools conveniently from `
You can combine these debugging actions on a single command line, allowing for convenient setup of blocking and non-blocking actions in one step. ``idf.py`` implements a simple logic to move the background actions (such as openocd) to the beginning and the interactive ones (such as gdb, monitor) to the end of the action list.
An example of a very useful combination is::
An example of a very useful combination is:
.. code-block:: none
idf.py openocd gdbgui monitor

View File

@ -48,6 +48,8 @@ ESP-IDF achieves reproducible builds using the following measures:
- Build date and time are not included into the :ref:`application metadata structure <app-image-format-application-description>` and :ref:`bootloader metadata structure <image-format-bootloader-description>` if :ref:`CONFIG_APP_REPRODUCIBLE_BUILD` is enabled.
- ESP-IDF build system ensures that source file lists, component lists and other sequences are sorted before passing them to CMake. Various other parts of the build system, such as the linker script generator also perform sorting to ensure that same output is produced regardless of the environment.
.. _reproducible-builds-and-debugging:
Reproducible Builds and Debugging
---------------------------------
@ -57,9 +59,9 @@ This issue can be solved using GDB ``set substitute-path`` command. For example,
set substitute-path /COMPONENT_FREERTOS_DIR /home/user/esp/esp-idf/components/freertos
ESP-IDF build system generates a file with the list of such ``set substitute-path`` commands automatically during the build process. The file is called ``prefix_map_gdbinit`` and is located in the project ``build`` directory.
ESP-IDF build system generates a file with the list of such ``set substitute-path`` commands automatically during the build process. The file is called ``prefix_map`` and is located in the project ``build/gdbinit`` directory.
When :ref:`idf.py gdb <jtag-debugging-with-idf-py>` is used to start debugging, this additional gdbinit file is automatically passed to GDB. When launching GDB manually or from and IDE, please pass this additional gdbinit script to GDB using ``-x build/prefix_map_gdbinit`` argument.
When :ref:`idf.py gdb <jtag-debugging-with-idf-py>` is used to start debugging, this additional ``gdbinit`` file is automatically passed to GDB. When launching GDB manually or from and IDE, please pass this additional gdbinit script to GDB using ``-x build/prefix_map_gdbinit`` argument.
Factors Which Still Affect Reproducible Builds
----------------------------------------------

View File

@ -117,13 +117,17 @@ JTAG 调试
.. highlight:: bash
如果已经按照 :doc:`快速入门 <../../get-started/index>` 完成了 ESP-IDF 及其 CMake 构建系统的安装,那么 OpenOCD 已经被默认安装到了你的开发系统中。在 :ref:`设置开发环境 <get-started-set-up-env>` 结束后,应该能够在终端中运行如下 OpenOCD 命令::
如果已经按照 :doc:`快速入门 <../../get-started/index>` 完成了 ESP-IDF 及其 CMake 构建系统的安装,那么 OpenOCD 已经被默认安装到了你的开发系统中。在 :ref:`设置开发环境 <get-started-set-up-env>` 结束后,应该能够在终端中运行如下 OpenOCD 命令
.. code-block:: none
openocd --version
.. highlight:: none
终端会输出以下信息(实际版本号可能会更新)::
终端会输出以下信息(实际版本号可能会更新)
.. code-block:: none
Open On-Chip Debugger v0.12.0-esp32-20240318 (2024-03-18-18:25)
Licensed under GNU GPL v2
@ -174,7 +178,7 @@ OpenOCD 安装完成后就可以配置 {IDF_TARGET_NAME} 目标(即带 JTAG
.. highlight:: bash
打开终端,按照快速入门指南中的 :ref:`设置开发环境 <get-started-set-up-env>` 章节进行操作,然后运行如下命令,以启动 OpenOCD该命令适用于 Windows、Linux 和 macOS:
打开终端,按照快速入门指南中的 :ref:`设置开发环境 <get-started-set-up-env>` 章节进行操作。运行 OpenOCD 时需要提供与目标开发板相关的配置文件。构建项目后ESP-IDF 会生成 ``build/project_description.json`` 文件,其中 ``debug_arguments_openocd`` 字段保存了默认的 OpenOCD 配置信息。请运行如下命令,以启动 OpenOCD该命令适用于 Windows、Linux 和 macOS
.. include:: {IDF_TARGET_PATH_NAME}.inc
:start-after: run-openocd
@ -318,7 +322,7 @@ Windows 用户的示例如下:
.. _jtag-debugging-tips-and-quirks:
注意事项和补充内容
--------------------
------------------
本节列出了上文中提到的所有注意事项和补充内容的链接。
@ -329,7 +333,7 @@ Windows 用户的示例如下:
相关文档
------------
--------
.. toctree::
:hidden:

View File

@ -23,8 +23,8 @@
作为一款集成开发环境 (IDE)Eclipse 提供了一套强大的工具,用于开发和调试软件应用程序。对于 ESP-IDF 应用程序,`IDF Eclipse 插件 <https://github.com/espressif/idf-eclipse-plugin>`_ 提供了两种调试方式:
1. `ESP-IDF GDB OpenOCD 调试 <https://github.com/espressif/idf-eclipse-plugin/blob/master/docs/OpenOCD%20Debugging.md#esp-idf-gdb-openocd-debugging>`_
2. GDB 硬件调试
1. `ESP-IDF GDB OpenOCD 调试 <https://github.com/espressif/idf-eclipse-plugin/blob/master/docs/OpenOCD%20Debugging.md#esp-idf-gdb-openocd-debugging>`_
2. GDB 硬件调试
默认情况下Eclipse 通过 GDB 硬件调试插件支持 OpenOCD 调试。该调试方式需要从命令行启动 OpenOCD 服务器,并在 Eclipse 中配置 GDB 客户端,整个过程耗时且容易出错。
@ -35,21 +35,22 @@
**GDB 硬件调试**
.. note::
只有在无法使用 `ESP-IDF GDB OpenOCD 调试 <https://github.com/espressif/idf-eclipse-plugin/blob/master/docs/OpenOCD%20Debugging.md#esp-idf-gdb-openocd-debugging>`_ 的情况下,才建议使用 GDB 硬件调试。
首先,打开 Eclipse选择 ``Help`` > ``Install New Software`` 来安装 ``GDB Hardware Debugging`` 插件。
安装完成后,按照以下步骤配置调试会话。请注意,一些配置参数是通用的,有些则针对特定项目。我们会通过配置 "blink" 示例项目的调试环境来进行展示,请先按照 `Eclipse Plugin <https://github.com/espressif/idf-eclipse-plugin/blob/master/README_CN.md>`_ 介绍的方法将该示例项目添加到 Eclipse 的工作空间。示例项目 :example:`get-started/blink` 的源代码可以在 ESP-IDF 仓库的 :idf:`examples` 目录下找到。
1. 在 Eclipse 中,进入 ``Run`` > ``Debug Configuration``,会出现一个新的窗口。在窗口的左侧窗格中,双击 ``GDB Hardware Debugging`` (或者选择 ``GDB Hardware Debugging`` 然后按下 ``New`` 按钮)来新建一个配置。
1. 在 Eclipse 中,进入 ``Run`` > ``Debug Configuration``,会出现一个新的窗口。在窗口的左侧窗格中,双击 ``GDB Hardware Debugging`` (或者选择 ``GDB Hardware Debugging`` 然后按下 ``New`` 按钮)来新建一个配置。
2. 在右边显示的表单中,``Name:`` 一栏中输入配置的名称,例如: “Blink checking”。
2. 在右边显示的表单中,``Name:`` 一栏中输入配置的名称,例如: “Blink checking”。
3. 在下面的 ``Main`` 选项卡中, 点击 ``Project:`` 边上的 ``Browse`` 按钮,然后选择当前的 ``blink`` 项目。
3. 在下面的 ``Main`` 选项卡中, 点击 ``Project:`` 边上的 ``Browse`` 按钮,然后选择当前的 ``blink`` 项目。
4. 在下一行的 ``C/C++ Application:`` 中,点击 ``Browse`` 按钮,选择 ``blink.elf`` 文件。如果 ``blink.elf`` 文件不存在,那么很有可能该项目还没有编译,请参考 `Eclipse Plugin <https://github.com/espressif/idf-eclipse-plugin/blob/master/README_CN.md>`_ 指南中的介绍。
4. 在下一行的 ``C/C++ Application:`` 中,点击 ``Browse`` 按钮,选择 ``blink.elf`` 文件。如果 ``blink.elf`` 文件不存在,那么很有可能该项目还没有编译,请参考 `Eclipse Plugin <https://github.com/espressif/idf-eclipse-plugin/blob/master/README_CN.md>`_ 指南中的介绍。
5. 最后,在 ``Build (if required) before launching`` 下面点击 ``Disable auto build``
5. 最后,在 ``Build (if required) before launching`` 下面点击 ``Disable auto build``
上述步骤 1 - 5 的示例输入如下图所示。
@ -60,9 +61,9 @@
GDB 硬件调试的配置 - Main 选项卡
6. 点击 ``Debugger`` 选项卡,在 ``GDB Command`` 栏中输入 ``{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb`` 来调用调试器。
6. 点击 ``Debugger`` 选项卡,在 ``GDB Command`` 栏中输入 ``{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb`` 来调用调试器。
7. 更改 ``Remote host`` 的默认配置,在 ``Port number`` 下面输入 ``3333``
7. 更改 ``Remote host`` 的默认配置,在 ``Port number`` 下面输入 ``3333``
上述步骤 6 - 7 的示例输入如下图所示。
@ -73,23 +74,26 @@
GDB 硬件调试的配置 - Debugger 选项卡
8. 最后一个需要更改默认配置的选项卡是 ``Startup`` 选项卡。在 ``Initialization Commands`` 下,取消选中 ``Reset and Delay (seconds)````Halt``,然后在下面一栏中输入以下命令:
8. 最后一个需要更改默认配置的选项卡是 ``Startup`` 选项卡。在 ``Initialization Commands`` 下,取消选中 ``Reset and Delay (seconds)````Halt``,然后在下面一栏中输入以下命令:
::
.. code-block:: none
mon reset halt
maintenance flush register-cache
set remote hardware-watchpoint-limit 2
.. note::
如果想在启动新的调试会话之前自动更新闪存中的镜像,请在 ``Initialization Commands`` 文本框的开头添加以下命令行::
如果想在启动新的调试会话之前自动更新闪存中的镜像,请在 ``Initialization Commands`` 文本框的开头添加以下命令行:
.. code-block:: none
mon reset halt
mon program_esp ${workspace_loc:blink/build/blink.bin} 0x10000 verify
有关 ``program_esp`` 命令的说明请参考 :ref:`jtag-upload-app-debug` 章节。
9.``Load Image and Symbols`` 下,取消选中 ``Load image`` 选项。
9. ``Load Image and Symbols`` 下,取消选中 ``Load image`` 选项。
10. 在同一个选项卡中继续往下浏览,建立一个初始断点用来在调试器复位后暂停 CPU。插件会根据 ``Set break point at:`` 一栏中输入的函数名,在该函数的开头设置断点。选中这一选项,并在相应的字段中输入 ``app_main``
@ -108,16 +112,16 @@
12. 如果前面已经完成 :ref:`jtag-debugging-configuring-target` 中介绍的步骤,目标正在运行并准备好与调试器进行对话,那么点击 ``Debug`` 按钮直接进行调试。如果尚未完成前面步骤,请点击 ``Apply`` 按钮保存配置,返回 :ref:`jtag-debugging-configuring-target` 章节进行配置,最后再回到这里开始调试。
一旦所有 1-12 的配置步骤都已经完成Eclipse 就会打开 ``Debug`` 视图,如下图所示。
一旦所有 1-12 的配置步骤都已经完成Eclipse 就会打开 ``Debug`` 视图,如下图所示。
.. figure:: ../../../_static/debug-perspective.jpg
:align: center
:alt: Debug Perspective in Eclipse
:figclass: align-center
.. figure:: ../../../_static/debug-perspective.jpg
:align: center
:alt: Debug Perspective in Eclipse
:figclass: align-center
Eclipse 中的调试视图
Eclipse 中的调试视图
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-eclipse` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-eclipse` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
.. _jtag-debugging-using-debugger-command-line:
@ -125,94 +129,76 @@
使用命令行调试
^^^^^^^^^^^^^^^^
1. 为了能够启动调试会话,需要先启动并运行目标,如果还没有完成,请按照 :ref:`jtag-debugging-configuring-target` 中的介绍进行操作。
1. 为了能够启动调试会话,需要先启动并运行目标,如果还没有完成,请按照 :ref:`jtag-debugging-configuring-target` 中的介绍进行操作。
.. highlight:: bash
2. 打开一个新的终端会话并前往待调试的项目目录,比如:
2. 打开一个新的终端会话并前往待调试的项目目录,比如:
::
.. code-block:: none
cd ~/esp/blink
.. highlight:: none
3. 启动调试器时,通常需要提供几个配置参数和命令,为了避免每次都在命令行中逐行输入这些命令,你可以新建一个配置文件,并将其命名为 ``gdbinit``:
3. 启动调试器时,通常需要提供一些配置参数和命令。构建系统会生成多个 ``.gdbinit`` 文件,以便进行高效调试。这些文件的路径存储在 ``build/project_description.json`` 文件的 ``gdbinit_files`` 字段部分,具体路径如下所示进行定义:
::
.. code-block:: json
target remote :3333
set remote hardware-watchpoint-limit 2
mon reset halt
maintenance flush register-cache
thb app_main
c
"gdbinit_files": {
"01_symbols": "application_path/build/gdbinit/symbols",
"02_prefix_map": "application_path/build/gdbinit/prefix_map",
"03_py_extensions": "application_path/build/gdbinit/py_extensions",
"04_connect": "application_path/build/gdbinit/connect"
}
将此文件保存在当前目录中
按照 JSON 键名中的 ``XX_`` 前缀进行排序,并以这种顺序将字段信息提供给 GDB
有关 ``gdbinit`` 文件内部的更多详细信息,请参阅 :ref:`jtag-debugging-tip-debugger-startup-commands` 章节。
生成的 ``.gdbinit`` 文件具有以下不同功能:
* ``symbols`` - 包含用于调试的符号来源。
* ``prefix_map`` - 配置前缀映射以修改 GDB 中的源路径。详情请参阅 :ref:`reproducible-builds-and-debugging`
* ``py_extensions`` - 初始化 GDB 中的 Python 扩展。请注意,应使用包含 ``libpython`` 库、且受 GDB 支持的 Python 版本。若运行以下命令时没有报错,则说明 GDB 和 Python 兼容:``{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb --batch-silent --ex "python import os"``
* ``connect`` - 包含与目标设备建立连接时所需的命令。
为增强调试体验,你还可以创建自定义的 ``.gdbinit`` 文件。自定义文件可以与生成的配置文件一起使用,也可以直替换它们。
.. highlight:: bash
4. 准备启动 GDB,请在终端中输入以下内容
4. 准备启动 GDB。请使用以下示例命令加载符号表并连接目标设备(命令中的 ``-q`` 选项用于减少启动输出)
::
.. code-block:: none
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -x gdbinit build/blink.elf
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -q -x build/gdbinit/symbols -x build/gdbinit/prefix_map -x build/gdbinit/connect build/blink.elf
.. highlight:: none
5. 如果前面的步骤已经正确完成,你会看到如下所示的输出日志,在日志的最后会出现 ``(gdb)`` 提示符:
::
.. code-block:: none
user-name@computer-name:~/esp/blink$ {IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -x gdbinit build/blink.elf
GNU gdb (crosstool-NG crosstool-ng-1.22.0-61-gab8375a) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-build_pc-linux-gnu --target={IDF_TARGET_TOOLCHAIN_PREFIX}".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from build/blink.elf...done.
0x400d10d8 in esp_vApplicationIdleHook () at /home/user-name/esp/esp-idf/components/{IDF_TARGET_PATH_NAME}/./freertos_hooks.c:52
52 asm("waiti 0");
JTAG tap: {IDF_TARGET_PATH_NAME}.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
JTAG tap: {IDF_TARGET_PATH_NAME}.slave tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
{IDF_TARGET_PATH_NAME}: Debug controller was reset (pwrstat=0x5F, after clear 0x0F).
{IDF_TARGET_PATH_NAME}: Core was reset (pwrstat=0x5F, after clear 0x0F).
{IDF_TARGET_PATH_NAME} halted. PRO_CPU: PC=0x5000004B (active) APP_CPU: PC=0x00000000
{IDF_TARGET_PATH_NAME}: target state: halted
{IDF_TARGET_PATH_NAME}: Core was reset (pwrstat=0x1F, after clear 0x0F).
Target halted. PRO_CPU: PC=0x40000400 (active) APP_CPU: PC=0x40000400
{IDF_TARGET_PATH_NAME}: target state: halted
Hardware assisted breakpoint 1 at 0x400db717: file /home/user-name/esp/blink/main/./blink.c, line 43.
0x0: 0x00000000
Target halted. PRO_CPU: PC=0x400DB717 (active) APP_CPU: PC=0x400D10D8
[New Thread 1073428656]
[New Thread 1073413708]
[New Thread 1073431316]
[New Thread 1073410672]
[New Thread 1073408876]
[New Thread 1073432196]
[New Thread 1073411552]
[Switching to Thread 1073411996]
{IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -q -x build/gdbinit/symbols -x build/gdbinit/prefix_map -x build/gdbinit/connect build/blink.elf
Temporary breakpoint 1, app_main () at /home/user-name/esp/blink/main/./blink.c:43
43 xTaskCreate(&blink_task, "blink_task", 512, NULL, 5, NULL);
user-name@computer-name:~/esp-idf/examples/get-started/blink$ {IDF_TARGET_TOOLCHAIN_PREFIX}-gdb -q -x build/gdbinit/symbols -x build/gdbinit/connect build/blink.elf
Reading symbols from build/blink.elf...
add symbol table from file "/home/user-name/esp-idf/examples/get-started/blink/build/bootloader/bootloader.elf"
[Switching to Thread 1070141764]
app_main () at /home/user-name/esp-idf/examples/get-started/blink/main/blink_example_main.c:95
95 configure_led();
add symbol table from file "/home/alex/.espressif/tools/esp-rom-elfs/20241011/{IDF_TARGET_PATH_NAME}_rev0_rom.elf"
JTAG tap: {IDF_TARGET_PATH_NAME}.tap0 tap/device found: 0x00005c25 (mfg: 0x612 (Espressif Systems), part: 0x0005, ver: 0x0)
[{IDF_TARGET_PATH_NAME}] Reset cause (3) - (Software core reset)
Hardware assisted breakpoint 1 at 0x42009436: file /home/user-name/esp-idf/examples/get-started/blink/main/blink_example_main.c, line 92.
[Switching to Thread 1070139884]
Thread 2 "main" hit Temporary breakpoint 1, app_main () at /home/user-name/esp-idf/examples/get-started/blink/main/blink_example_main.c:92
92 {
(gdb)
注意上面日志的倒数第三行显示调试器已``app_main()`` 函数的断点处停止,该断点在 ``gdbinit`` 文件中设定。由于处理器已暂停运行LED 不再闪烁。如果你的 LED 也停止了闪烁,则可以开始调试。
请注意,以上日志的倒数第三行显示调试器已在 ``build/gdbinit/connect`` 函数的断点处停止,该断点在 ``gdbinit`` 文件中设定。由于处理器已暂停LED 不再闪烁。如果你的 LED 也停止了闪烁,则可以开始调试。
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-command-line` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
如果不太了解 GDB 的常用方法,请查阅 :ref:`jtag-debugging-examples-command-line` 文章中的调试示例章节 :ref:`jtag-debugging-examples`
.. _jtag-debugging-with-idf-py:
@ -236,7 +222,7 @@
2. ``idf.py gdb``
根据当前项目的 elf 文件自动生成 GDB 启动脚本,然后按照 :ref:`jtag-debugging-using-debugger-command-line`描述的步骤启动 GDB。
根据当前项目的 ELF 文件自动生成 GDB 启动脚本,然后按照 :ref:`jtag-debugging-using-debugger-command-line` 中描述的步骤启动 GDB。详情请参阅 :ref:`jtag-debugging-using-debugger-command-line`
3. ``idf.py gdbtui``
@ -251,7 +237,9 @@
上述这些命令也可以合并到一起使用,``idf.py`` 会自动将后台进程(比如 openocd最先运行交互式进程比如 GDBmonitor最后运行。
常用的组合命令如下所示::
常用的组合命令如下所示
.. code-block:: none
idf.py openocd gdbgui monitor

View File

@ -46,6 +46,7 @@ class AppUploader(AppDownloader):
'bootloader/*.elf',
'*.map',
'*.elf',
'gdbinit/*',
],
ArtifactType.BUILD_DIR_WITHOUT_MAP_AND_ELF_FILES: [
'*.bin',

79
tools/cmake/gdbinit.cmake Normal file
View File

@ -0,0 +1,79 @@
# __generate_gdbinit
# Prepares gdbinit files to pass into the debugger.
function(__generate_gdbinit)
# Define static gdbinit commands
if(CONFIG_IDF_TARGET_LINUX)
set(gdbinit_connect
"# Run the application and stop on app_main()\n"
"break app_main\n"
"run\n")
else()
set(gdbinit_connect
"# Connect to the default openocd-esp port and stop on app_main()\n"
"set remotetimeout 10\n"
"target remote :3333\n"
"monitor reset halt\n"
"maintenance flush register-cache\n"
"thbreak app_main\n"
"continue\n")
endif()
set(gdbinit_py_extensions
"# Add Python GDB extensions\n"
"python\n"
"try:\n"
" import freertos_gdb\n"
"except ModuleNotFoundError:\n"
" print('warning: python extension \"freertos_gdb\" not found.')\n"
"end\n")
# Define paths
set(gdbinit_dir ${BUILD_DIR}/gdbinit)
set(gdbinit_rom_in_path ${gdbinit_dir}/rom.gdbinit.in)
set(gdbinit_rom_path ${gdbinit_dir}/rom.gdbinit)
set(symbols_gdbinit_path ${gdbinit_dir}/symbols)
set(py_extensions_gdbinit_path ${gdbinit_dir}/py_extensions)
set(connect_gdbinit_path ${gdbinit_dir}/connect)
idf_build_get_property(PROJECT_EXECUTABLE EXECUTABLE)
set(application_elf ${BUILD_DIR}/${PROJECT_EXECUTABLE})
file(TO_CMAKE_PATH $ENV{ESP_ROM_ELF_DIR} ESP_ROM_ELF_DIR)
file(MAKE_DIRECTORY ${gdbinit_dir})
# Get ROM ELFs gdbinit part
if(CONFIG_IDF_TARGET_LINUX)
set(rom_symbols)
else()
execute_process(
COMMAND python "${idf_path}/components/esp_rom/gen_gdbinit.py" ${IDF_TARGET}
OUTPUT_VARIABLE rom_symbols
RESULT_VARIABLE result
)
if(NOT result EQUAL 0)
set(rom_symbols)
message(WARNING "Error while generating esp_rom gdbinit")
endif()
endif()
# Check if bootloader ELF is defined and set symbol-file accordingly
if(DEFINED BOOTLOADER_ELF_FILE)
set(add_bootloader_symbols " add-symbol-file ${BOOTLOADER_ELF_FILE}")
else()
set(add_bootloader_symbols " # Bootloader elf was not found")
endif()
# Configure and generate files
configure_file(${idf_path}/tools/cmake/symbols.gdbinit.in ${symbols_gdbinit_path})
file(WRITE ${py_extensions_gdbinit_path} ${gdbinit_py_extensions})
file(WRITE ${connect_gdbinit_path} ${gdbinit_connect})
# TODO IDF-11667
file(WRITE ${gdbinit_dir}/gdbinit "source ${symbols_gdbinit_path}\n")
file(APPEND ${gdbinit_dir}/gdbinit "source ${connect_gdbinit_path}\n")
# NOTE: prefix_map gbinit file generates by prefix_map.cmake file
idf_build_set_property(GDBINIT_FILES_SYMBOLS ${symbols_gdbinit_path})
idf_build_set_property(GDBINIT_FILES_PY_EXTENSIONS ${py_extensions_gdbinit_path})
idf_build_set_property(GDBINIT_FILES_CONNECT ${connect_gdbinit_path})
endfunction()

View File

@ -48,6 +48,8 @@ if(NOT __idf_env_set)
include(ldgen)
include(dfu)
include(version)
include(gdbinit)
include(openocd)
__build_init("${idf_path}")

14
tools/cmake/openocd.cmake Normal file
View File

@ -0,0 +1,14 @@
# __get_openocd_options
# Prepares openocd default options for the target.
function(__get_openocd_options openocd_option_var)
if(CONFIG_IDF_TARGET STREQUAL "esp32")
set(${openocd_option_var} "-f board/esp32-wrover-kit-3.3v.cfg" PARENT_SCOPE)
elseif(CONFIG_IDF_TARGET STREQUAL "esp32s2")
set(${openocd_option_var} "-f board/esp32s2-kaluga-1.cfg" PARENT_SCOPE)
elseif(CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED)
set(${openocd_option_var} "-f board/${CONFIG_IDF_TARGET}-builtin.cfg" PARENT_SCOPE)
else()
set(${openocd_option_var}
"-f interface/ftdi/esp32_devkitj_v1.cfg -f target/${CONFIG_IDF_TARGET}.cfg" PARENT_SCOPE)
endif()
endfunction()

View File

@ -353,6 +353,13 @@ function(__project_info test_components)
idf_build_get_property(COMPONENT_KCONFIGS_PROJBUILD KCONFIG_PROJBUILDS)
idf_build_get_property(debug_prefix_map_gdbinit DEBUG_PREFIX_MAP_GDBINIT)
__generate_gdbinit()
idf_build_get_property(gdbinit_files_prefix_map GDBINIT_FILES_PREFIX_MAP)
idf_build_get_property(gdbinit_files_symbols GDBINIT_FILES_SYMBOLS)
idf_build_get_property(gdbinit_files_py_extensions GDBINIT_FILES_PY_EXTENSIONS)
idf_build_get_property(gdbinit_files_connect GDBINIT_FILES_CONNECT)
__get_openocd_options(debug_arguments_openocd)
if(CONFIG_APP_BUILD_TYPE_RAM)
set(PROJECT_BUILD_TYPE ram_app)
else()

View File

@ -1,5 +1,5 @@
{
"version": "1.1",
"version": "1.2",
"project_name": "${PROJECT_NAME}",
"project_version": "${PROJECT_VER}",
"project_path": "${PROJECT_PATH}",
@ -29,5 +29,12 @@
"build_component_paths" : ${build_component_paths_json},
"build_component_info" : ${build_component_info_json},
"all_component_info" : ${all_component_info_json},
"debug_prefix_map_gdbinit": "${debug_prefix_map_gdbinit}"
"debug_prefix_map_gdbinit": "${debug_prefix_map_gdbinit}",
"gdbinit_files": {
"01_symbols": "${gdbinit_files_symbols}",
"02_prefix_map": "${gdbinit_files_prefix_map}",
"03_py_extensions": "${gdbinit_files_py_extensions}",
"04_connect": "${gdbinit_files_connect}"
},
"debug_arguments_openocd": "${debug_arguments_openocd}"
}

View File

@ -0,0 +1,9 @@
${rom_symbols}
# Load bootloader symbols
set confirm off
${add_bootloader_symbols}
set confirm on
# Load application symbols
file ${application_elf}

View File

@ -1,14 +1,13 @@
# SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# General Workflow:
# 1. read all components dirs, a semicolon-separated string (cmake list)
# 2. map the component dir with a unique prefix /COMPONENT_<NAME>_DIR
# 2. write the prefix mapping file to $BUILD_DIR/prefix_map_gdbinit
# 3. print the unique prefix out, a space-separated string, will be used by the build system to add compile options.
import argparse
import os
import shutil
from typing import List
@ -20,12 +19,18 @@ GDB_SUBSTITUTE_PATH_FMT = 'set substitute-path {} {}\n'
def write_gdbinit(build_dir: str, folders: List[str]) -> None:
gdb_init_filepath = os.path.join(build_dir, 'prefix_map_gdbinit')
gdbinit_dir = os.path.join(build_dir, 'gdbinit')
gdbinit_filepath = os.path.join(gdbinit_dir, 'prefix_map')
with open(gdb_init_filepath, 'w') as fw:
if not os.path.exists(gdbinit_dir):
os.mkdir(gdbinit_dir)
with open(gdbinit_filepath, 'w') as fw:
for folder in folders:
fw.write(f'{GDB_SUBSTITUTE_PATH_FMT.format(component_name(folder), folder)}')
shutil.copy(gdbinit_filepath, os.path.join(build_dir, 'prefix_map_gdbinit'))
def main(build_dir: str, folders: List[str]) -> None:
write_gdbinit(build_dir, folders)
@ -34,7 +39,7 @@ def main(build_dir: str, folders: List[str]) -> None:
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='print the debug-prefix-map and write to '
'$BUILD_DIR/prefix_map_gdbinit file')
'$BUILD_DIR/gdbinit/prefix_map file')
parser.add_argument('build_dir',
help='build dir')

View File

@ -35,15 +35,3 @@ URL_TO_DOC = 'https://docs.espressif.com/projects/esp-idf'
SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4']
PREVIEW_TARGETS = ['linux', 'esp32c5', 'esp32c61']
OPENOCD_TAGET_CONFIG_DEFAULT = '-f interface/ftdi/esp32_devkitj_v1.cfg -f target/{target}.cfg'
OPENOCD_TAGET_CONFIG: Dict[str, str] = {
'esp32': '-f board/esp32-wrover-kit-3.3v.cfg',
'esp32s2': '-f board/esp32s2-kaluga-1.cfg',
'esp32c3': '-f board/esp32c3-builtin.cfg',
'esp32s3': '-f board/esp32s3-builtin.cfg',
'esp32c6': '-f board/esp32c6-builtin.cfg',
'esp32c5': '-f board/esp32c5-builtin.cfg',
'esp32h2': '-f board/esp32h2-builtin.cfg',
'esp32p4': '-f board/esp32p4-builtin.cfg',
}

View File

@ -4,12 +4,10 @@ import json
import os
import re
import shlex
import shutil
import subprocess
import sys
import threading
import time
from textwrap import indent
from threading import Thread
from typing import Any
from typing import Dict
@ -21,8 +19,6 @@ from typing import Union
from click import INT
from click.core import Context
from esp_coredump import CoreDump
from idf_py_actions.constants import OPENOCD_TAGET_CONFIG
from idf_py_actions.constants import OPENOCD_TAGET_CONFIG_DEFAULT
from idf_py_actions.errors import FatalError
from idf_py_actions.serial_ext import BAUD_RATE
from idf_py_actions.serial_ext import PORT
@ -33,53 +29,6 @@ from idf_py_actions.tools import get_sdkconfig_value
from idf_py_actions.tools import PropertyDict
from idf_py_actions.tools import yellow_print
PYTHON = sys.executable
ESP_ROM_INFO_FILE = 'roms.json'
GDBINIT_PYTHON_TEMPLATE = '''
# Add Python GDB extensions
python
import sys
sys.path = {sys_path}
import freertos_gdb
end
'''
GDBINIT_PYTHON_NOT_SUPPORTED = '''
# Python scripting is not supported in this copy of GDB.
# Please make sure that your Python distribution contains Python shared library.
'''
GDBINIT_BOOTLOADER_ADD_SYMBOLS = '''
# Load bootloader symbols
set confirm off
add-symbol-file {boot_elf}
set confirm on
'''
GDBINIT_BOOTLOADER_NOT_FOUND = '''
# Bootloader elf was not found
'''
GDBINIT_APP_ADD_SYMBOLS = '''
# Load application file
file {app_elf}
'''
GDBINIT_CONNECT = '''
# Connect to the default openocd-esp port and break on app_main()
set remotetimeout 10
target remote :3333
monitor reset halt
maintenance flush register-cache
thbreak app_main
continue
'''
GDBINIT_MAIN = '''
source {py_extensions}
source {symbols}
source {connect}
'''
def get_openocd_arguments(target: str) -> str:
default_args = OPENOCD_TAGET_CONFIG_DEFAULT.format(target=target)
return str(OPENOCD_TAGET_CONFIG.get(target, default_args))
def chip_rev_to_int(chip_rev: Optional[str]) -> Union[int, None]:
# The chip rev will be derived from the elf file if none are returned.
@ -223,103 +172,6 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
return subprocess.run([gdb, '--batch-silent', '--ex', 'python import os'],
stderr=subprocess.DEVNULL).returncode == 0
def get_normalized_path(path: str) -> str:
if os.name == 'nt':
return os.path.normpath(path).replace('\\', '\\\\')
return path
def get_rom_if_condition_str(date_addr: int, date_str: str) -> str:
r = []
for i in range(0, len(date_str), 4):
value = hex(int.from_bytes(bytes(date_str[i:i + 4], 'utf-8'), 'little'))
r.append(f'(*(int*) {hex(date_addr + i)}) == {value}')
return 'if ' + ' && '.join(r)
def generate_gdbinit_rom_add_symbols(target: str) -> str:
base_ident = ' '
rom_elfs_dir = os.getenv('ESP_ROM_ELF_DIR')
if not rom_elfs_dir:
raise FatalError(
'ESP_ROM_ELF_DIR environment variable is not defined. Please try to run IDF "install" and "export" scripts.')
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), ESP_ROM_INFO_FILE), 'r') as f:
roms = json.load(f)
if target not in roms:
msg_body = f'Target "{target}" was not found in "{ESP_ROM_INFO_FILE}". Please check IDF integrity.' # noqa: E713
if os.getenv('ESP_IDF_GDB_TESTING'):
raise FatalError(msg_body)
print(f'Warning: {msg_body}')
return f'# {msg_body}'
r = ['', f'# Load {target} ROM ELF symbols']
r.append('define target hookpost-remote')
r.append('set confirm off')
# Since GDB does not have 'else if' statement than we use nested 'if..else' instead.
for i, k in enumerate(roms[target], 1):
indent_str = base_ident * i
rom_file = get_normalized_path(os.path.join(rom_elfs_dir, f'{target}_rev{k["rev"]}_rom.elf'))
build_date_addr = int(k['build_date_str_addr'], base=16)
r.append(indent(f'# if $_streq((char *) {hex(build_date_addr)}, "{k["build_date_str"]}")', indent_str))
r.append(indent(get_rom_if_condition_str(build_date_addr, k['build_date_str']), indent_str))
r.append(indent(f'add-symbol-file {rom_file}', indent_str + base_ident))
r.append(indent('else', indent_str))
if i == len(roms[target]):
# In case no one known ROM ELF fits - print error and exit with error code 1
indent_str += base_ident
msg_body = f'unknown {target} ROM revision.'
if os.getenv('ESP_IDF_GDB_TESTING'):
r.append(indent(f'echo Error: {msg_body}\\n', indent_str))
r.append(indent('quit 1', indent_str))
else:
r.append(indent(f'echo Warning: {msg_body}\\n', indent_str))
# Close 'else' operators
for i in range(len(roms[target]), 0, -1):
r.append(indent('end', base_ident * i))
r.append('set confirm on')
r.append('end')
r.append('')
return '\n'.join(r)
raise FatalError(f'{ESP_ROM_INFO_FILE} file not found. Please check IDF integrity.')
def generate_gdbinit_files(gdb: str, gdbinit: Optional[str], project_desc: Dict[str, Any]) -> None:
app_elf = get_normalized_path(os.path.join(project_desc['build_dir'], project_desc['app_elf']))
if not os.path.exists(app_elf):
raise FatalError('ELF file not found. You need to build & flash the project before running debug targets')
# Recreate empty 'gdbinit' directory
gdbinit_dir = '/'.join([project_desc['build_dir'], 'gdbinit'])
if os.path.isfile(gdbinit_dir):
os.remove(gdbinit_dir)
elif os.path.isdir(gdbinit_dir):
shutil.rmtree(gdbinit_dir)
os.mkdir(gdbinit_dir)
# Prepare gdbinit for Python GDB extensions import
py_extensions = '/'.join([gdbinit_dir, 'py_extensions'])
with open(py_extensions, 'w') as f:
if is_gdb_with_python(gdb):
f.write(GDBINIT_PYTHON_TEMPLATE.format(sys_path=sys.path))
else:
f.write(GDBINIT_PYTHON_NOT_SUPPORTED)
# Prepare gdbinit for related ELFs symbols load
symbols = '/'.join([gdbinit_dir, 'symbols'])
with open(symbols, 'w') as f:
boot_elf = get_normalized_path(project_desc['bootloader_elf']) if 'bootloader_elf' in project_desc else None
if boot_elf and os.path.exists(boot_elf):
f.write(GDBINIT_BOOTLOADER_ADD_SYMBOLS.format(boot_elf=boot_elf))
else:
f.write(GDBINIT_BOOTLOADER_NOT_FOUND)
f.write(generate_gdbinit_rom_add_symbols(project_desc['target']))
f.write(GDBINIT_APP_ADD_SYMBOLS.format(app_elf=app_elf))
# Generate the gdbinit for target connect if no custom gdbinit is present
if not gdbinit:
gdbinit = '/'.join([gdbinit_dir, 'connect'])
with open(gdbinit, 'w') as f:
f.write(GDBINIT_CONNECT)
with open(os.path.join(gdbinit_dir, 'gdbinit'), 'w') as f:
f.write(GDBINIT_MAIN.format(py_extensions=py_extensions, symbols=symbols, connect=gdbinit))
def debug_cleanup() -> None:
print('cleaning up debug targets')
for t in processes['threads_to_join']:
@ -375,8 +227,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
project_desc = get_project_desc(args, ctx)
if openocd_arguments is None:
# use default value if commands not defined in the environment nor command line
target = project_desc['target']
openocd_arguments = get_openocd_arguments(target)
openocd_arguments = project_desc.get('debug_arguments_openocd', '')
print(
'Note: OpenOCD cfg not found (via env variable OPENOCD_COMMANDS nor as a --openocd-commands argument)\n'
'OpenOCD arguments default to: "{}"'.format(openocd_arguments))
@ -399,22 +250,56 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
processes['openocd_outfile_name'] = openocd_out_name
print('OpenOCD started as a background task {}'.format(process.pid))
def get_gdb_args(project_desc: Dict[str, Any]) -> List:
gdbinit = os.path.join(project_desc['build_dir'], 'gdbinit', 'gdbinit')
args = ['-x={}'.format(gdbinit)]
debug_prefix_gdbinit = project_desc.get('debug_prefix_map_gdbinit')
if debug_prefix_gdbinit:
args.append('-ix={}'.format(debug_prefix_gdbinit))
return args
def get_gdb_args(project_desc: Dict[str, Any], gdb_x: Tuple, gdb_ex: Tuple, gdb_commands: Optional[str]) -> List[str]:
# debugger application name (xtensa-esp32-elf-gdb, riscv32-esp-elf-gdb, ...)
gdb_name = project_desc.get('monitor_toolprefix', '') + 'gdb'
gdb_args = [gdb_name]
gdbinit_files = project_desc.get('gdbinit_files')
if not gdbinit_files:
raise FatalError('Please check if the project was configured correctly ("gdbinit_files" not found in "project_description.json").')
gdbinit_files = sorted(gdbinit_files.items())
gdb_x_list = list(gdb_x)
gdb_x_names = [os.path.basename(x) for x in gdb_x_list]
# compile predefined gdbinit files options.
for name, path in gdbinit_files:
name = name[len('xx_'):]
if name == 'py_extensions':
if not is_gdb_with_python(gdb_name):
continue
if os.name == 'nt': # set PYTHONPATH for windows (already done inside bin-wrappers in linux/macos)
os.environ['PYTHONPATH'] = os.pathsep.join(sys.path[1:])
# Replace predefined gdbinit with user's if passed with the same name.
if name in gdb_x_names:
gdb_x_index = gdb_x_names.index(name)
gdb_args.append(f'-x={gdb_x_list[gdb_x_index]}')
gdb_x_list.pop(gdb_x_index)
continue
if name == 'connect' and gdb_x_list: # TODO IDF-11692
continue
gdb_args.append(f'-x={path}')
# append user-defined gdbinit files
for x in gdb_x_list:
gdb_args.append(f'-x={x}')
# add user-defined commands
if gdb_ex:
for ex in gdb_ex:
gdb_args.append('-ex')
gdb_args.append(ex)
# add user defined options
if gdb_commands:
gdb_args += shlex.split(gdb_commands)
return gdb_args
def _get_gdbgui_version(ctx: Context) -> Tuple[int, ...]:
subprocess_success = False
try:
completed_process = subprocess.run(['gdbgui', '--version'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
subprocess_success = True
captured_output = completed_process.stdout.decode('utf-8', 'ignore')
subprocess_success = True
except FileNotFoundError:
# This is happening at least with Python 3.12 when gdbgui is not installed
subprocess_success = False
pass
if not subprocess_success or completed_process.returncode != 0:
if sys.version_info[:2] >= (3, 11) and sys.platform == 'win32':
@ -427,41 +312,41 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
'Please use "idf.py gdb" or debug in Eclipse/Vscode instead.')
raise FatalError('Error starting gdbgui. Please make sure gdbgui has been installed with '
'"install.{sh,bat,ps1,fish} --enable-gdbgui" and can be started. '
f'Error: {captured_output}', ctx)
f'Error: {captured_output if subprocess_success else "Unknown"}', ctx)
v = re.search(r'(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?', captured_output)
if not v:
raise SystemExit(f'Error: "gdbgui --version" returned "{captured_output}"')
return tuple(int(i) if i else 0 for i in (v[1], v[2], v[3], v[4]))
def gdbui(action: str, ctx: Context, args: PropertyDict, gdbgui_port: Optional[str], gdbinit: Optional[str],
require_openocd: bool) -> None:
def gdbui(action: str, ctx: Context, args: PropertyDict, gdbgui_port: Optional[str], gdbinit: Tuple,
ex: Tuple, gdb_commands: Optional[str], require_openocd: bool) -> None:
"""
Asynchronous GDB-UI target
"""
project_desc = get_project_desc(args, ctx)
local_dir = project_desc['build_dir']
gdb = project_desc['monitor_toolprefix'] + 'gdb'
generate_gdbinit_files(gdb, gdbinit, project_desc)
gdbgui_version = _get_gdbgui_version(ctx)
gdb_args_list = get_gdb_args(project_desc)
gdb_args = get_gdb_args(project_desc, gdbinit, ex, gdb_commands)
if gdbgui_version >= (0, 14, 0, 0):
# See breaking changes https://github.com/cs01/gdbgui/blob/master/CHANGELOG.md#01400, especially the
# replacement of command line arguments.
gdb_args = ' '.join(gdb_args_list)
args = ['gdbgui', '-g', ' '.join((gdb, gdb_args))]
gdbgui_args = ['gdbgui', '-g', ' '.join(gdb_args)]
else:
# this is a workaround for gdbgui
# gdbgui is using shlex.split for the --gdb-args option. When the input is:
# - '"-x=foo -x=bar"', would return ['foo bar']
# - '-x=foo', would return ['-x', 'foo'] and mess up the former option '--gdb-args'
# so for one item, use extra double quotes. for more items, use no extra double quotes.
gdb_args = '"{}"'.format(' '.join(gdb_args_list)) if len(gdb_args_list) == 1 else ' '.join(gdb_args_list)
args = ['gdbgui', '-g', gdb, '--gdb-args', gdb_args]
gdb = gdb_args[0]
gdb_args_list = gdb_args[1:]
gdb_args_str = '"{}"'.format(' '.join(gdb_args_list)) if len(gdb_args_list) == 1 else ' '.join(gdb_args_list)
gdbgui_args = ['gdbgui', '-g', gdb, '--gdb-args', gdb_args_str]
if gdbgui_port is not None:
args += ['--port', gdbgui_port]
gdbgui_args += ['--port', gdbgui_port]
gdbgui_out_name = os.path.join(local_dir, GDBGUI_OUT_FILE)
gdbgui_out = open(gdbgui_out_name, 'w')
env = os.environ.copy()
@ -470,8 +355,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
# pygdbmi).
env['PURE_PYTHON'] = '1'
try:
print('Running: ', args)
process = subprocess.Popen(args, stdout=gdbgui_out, stderr=subprocess.STDOUT, bufsize=1, env=env)
print('Running: ', gdbgui_args)
process = subprocess.Popen(gdbgui_args, stdout=gdbgui_out, stderr=subprocess.STDOUT, bufsize=1, env=env)
except (OSError, subprocess.CalledProcessError) as e:
print(e)
raise FatalError('Error starting gdbgui', ctx)
@ -509,14 +394,14 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
if task.name in ('gdb', 'gdbgui', 'gdbtui'):
task.action_args['require_openocd'] = True
def gdbtui(action: str, ctx: Context, args: PropertyDict, gdbinit: str, require_openocd: bool) -> None:
def gdbtui(action: str, ctx: Context, args: PropertyDict, gdbinit: Tuple, ex: Tuple, gdb_commands: str, require_openocd: bool) -> None:
"""
Synchronous GDB target with text ui mode
"""
gdb(action, ctx, args, False, 1, gdbinit, require_openocd)
gdb(action, ctx, args, False, 1, gdbinit, ex, gdb_commands, require_openocd)
def gdb(action: str, ctx: Context, args: PropertyDict, batch: bool, gdb_tui: Optional[int], gdbinit: Optional[str],
require_openocd: bool) -> None:
def gdb(action: str, ctx: Context, args: PropertyDict, batch: bool, gdb_tui: Optional[int], gdbinit: Tuple,
ex: Tuple, gdb_commands: Optional[str], require_openocd: bool) -> None:
"""
Synchronous GDB target
"""
@ -524,14 +409,12 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
watch_openocd.start()
processes['threads_to_join'].append(watch_openocd)
project_desc = get_project_desc(args, ctx)
gdb = project_desc['monitor_toolprefix'] + 'gdb'
generate_gdbinit_files(gdb, gdbinit, project_desc)
args = [gdb, *get_gdb_args(project_desc)]
gdb_args = get_gdb_args(project_desc, gdbinit, ex, gdb_commands)
if gdb_tui is not None:
args += ['-tui']
gdb_args += ['-tui']
if batch:
args += ['--batch']
p = subprocess.Popen(args)
gdb_args += ['--batch']
p = subprocess.Popen(gdb_args)
processes['gdb'] = p
while True:
try:
@ -600,9 +483,23 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
'default': False,
}
gdbinit = {
'names': ['--gdbinit'],
'names': ['--gdbinit', '--x', '-x'],
'help': 'Specify the name of gdbinit file to use\n',
'default': None,
'multiple': True,
}
ex = {
'names': ['--ex', '-ex'],
'help':
('Execute given GDB command.\n'),
'default': None,
'multiple': True,
}
gdb_commands = {
'names': ['--gdb-commands', '--gdb_commands'],
'help':
('Command line arguments for gdb.\n'),
'default': None,
}
debug_actions = {
'global_action_callbacks': [global_callback],
@ -642,7 +539,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
'names': ['--gdb-tui', '--gdb_tui'],
'help': ('run gdb in TUI mode\n'),
'default': None,
}, gdbinit, fail_if_openocd_failed
}, gdbinit, ex, gdb_commands, fail_if_openocd_failed
],
'order_dependencies': ['all', 'flash'],
},
@ -656,14 +553,14 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
('The port on which gdbgui will be hosted. Default: 5000\n'),
'default':
None,
}, gdbinit, fail_if_openocd_failed
}, gdbinit, ex, gdb_commands, fail_if_openocd_failed
],
'order_dependencies': ['all', 'flash'],
},
'gdbtui': {
'callback': gdbtui,
'help': 'GDB TUI mode.',
'options': [gdbinit, fail_if_openocd_failed],
'options': [gdbinit, ex, gdb_commands, fail_if_openocd_failed],
'order_dependencies': ['all', 'flash'],
},
'coredump-info': {

View File

@ -1,21 +1,15 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import json
import logging
import os
import re
import subprocess
import sys
import pexpect
import pytest
from pytest_embedded_idf import IdfDut
try:
from idf_py_actions.debug_ext import get_openocd_arguments
except ModuleNotFoundError:
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
from idf_py_actions.debug_ext import get_openocd_arguments
@pytest.mark.supported_targets
@pytest.mark.jtag
@ -27,8 +21,12 @@ def test_idf_gdb(dut: IdfDut) -> None:
# Don't need to have output from UART anymore
dut.serial.stop_redirect_thread()
desc_path = os.path.join(dut.app.binary_path, 'project_description.json')
with open(desc_path, 'r') as f:
project_desc = json.load(f)
with open(os.path.join(dut.logdir, 'ocd.txt'), 'w') as ocd_log:
cmd = ['openocd', *get_openocd_arguments(dut.target).split()]
cmd = ['openocd'] + project_desc['debug_arguments_openocd'].split()
openocd_scripts = os.getenv('OPENOCD_SCRIPTS')
if openocd_scripts:
cmd.extend(['-s', openocd_scripts])
@ -37,18 +35,14 @@ def test_idf_gdb(dut: IdfDut) -> None:
ocd = subprocess.Popen(cmd, stdout=ocd_log, stderr=ocd_log)
try:
gdb_env = os.environ.copy()
gdb_env['ESP_IDF_GDB_TESTING'] = '1'
with open(os.path.join(dut.logdir, 'gdb.txt'), 'w') as gdb_log, \
pexpect.spawn(f'idf.py -B {dut.app.binary_path} gdb --batch',
env=gdb_env,
timeout=60,
logfile=gdb_log,
encoding='utf-8',
codec_errors='ignore') as p:
p.expect(re.compile(r'add symbol table from file.*bootloader.elf'))
p.expect(re.compile(r'add symbol table from file.*rom.elf'))
p.expect(re.compile(r'add symbol table from file.*rom.elf')) # if fail here: add target support here https://github.com/espressif/esp-rom-elfs
p.expect_exact('hit Temporary breakpoint 1, app_main ()')
finally:
ocd.terminate()

View File

@ -13,9 +13,7 @@ from unittest import main
from unittest import mock
from unittest import TestCase
import elftools.common.utils as ecu
import jsonschema
from elftools.elf.elffile import ELFFile
try:
from StringIO import StringIO
@ -265,48 +263,6 @@ class TestHelpOutput(TestWithoutExtensions):
self.action_test_idf_py(['help', '--json', '--add-options'], schema_json)
class TestROMs(TestWithoutExtensions):
def get_string_from_elf_by_addr(self, filename: str, address: int) -> str:
result = ''
with open(filename, 'rb') as stream:
elf_file = ELFFile(stream)
ro = elf_file.get_section_by_name('.rodata')
ro_addr_delta = ro['sh_addr'] - ro['sh_offset']
cstring = ecu.parse_cstring_from_stream(ro.stream, address - ro_addr_delta)
if cstring:
result = str(cstring.decode('utf-8'))
return result
def test_roms_validate_json(self):
with open(os.path.join(py_actions_path, 'roms.json'), 'r') as f:
roms_json = json.load(f)
with open(os.path.join(py_actions_path, 'roms_schema.json'), 'r') as f:
schema_json = json.load(f)
jsonschema.validate(roms_json, schema_json)
def test_roms_check_supported_chips(self):
from idf_py_actions.constants import SUPPORTED_TARGETS
with open(os.path.join(py_actions_path, 'roms.json'), 'r') as f:
roms_json = json.load(f)
for chip in SUPPORTED_TARGETS:
self.assertTrue(chip in roms_json, msg=f'Have no ROM data for chip {chip}')
def test_roms_validate_build_date(self):
sys.path.append(py_actions_path)
rom_elfs_dir = os.getenv('ESP_ROM_ELF_DIR')
with open(os.path.join(py_actions_path, 'roms.json'), 'r') as f:
roms_json = json.load(f)
for chip in roms_json:
for k in roms_json[chip]:
rom_file = os.path.join(rom_elfs_dir, f'{chip}_rev{k["rev"]}_rom.elf')
build_date_str = self.get_string_from_elf_by_addr(rom_file, int(k['build_date_str_addr'], base=16))
self.assertTrue(len(build_date_str) == 11)
self.assertTrue(build_date_str == k['build_date_str'])
class TestFileArgumentExpansion(TestCase):
def test_file_expansion(self):
"""Test @filename expansion functionality"""