Merge branch 'docs/monitor_rom_address_decoding' into 'master'

esp_idf_monitor: ROM ELF address decoding docs and tests

Closes IDF-372

See merge request espressif/esp-idf!24271
This commit is contained in:
Roland Dobai 2023-07-17 18:52:29 +08:00
commit 30672c7c65
7 changed files with 137 additions and 17 deletions

View File

@ -114,7 +114,7 @@ Whenever the chip outputs a hexadecimal address that points to executable code,
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
If an ESP-IDF app crashes and panics, a register dump and backtrace is produced, such as the following::
If an ESP-IDF app crashes and panics, a register dump and backtrace are produced, such as the following::
abort() was called at PC 0x42067cd5 on core 0
@ -180,6 +180,47 @@ To decode each address, IDF Monitor runs the following command in the background
{IDF_TARGET_TOOLCHAIN_PREFIX}-addr2line -pfiaC -e build/PROJECT.elf ADDRESS
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
If an address is not matched in the app source code, IDF monitor also checks the ROM code. Instead of printing the source file name and line number, only the function name followed by ``in ROM`` is displayed::
abort() was called at PC 0x40007c69 on core 0
0x40007c69: ets_write_char in ROM
Backtrace: 0x40081656:0x3ffb4ac0 0x40085729:0x3ffb4ae0 0x4008a7ce:0x3ffb4b00 0x40007c69:0x3ffb4b70 0x40008148:0x3ffb4b90 0x400d51d7:0x3ffb4c20 0x400e31bc:0x3ffb4c50 0x40087bc5:0x3ffb4c80
0x40081656: panic_abort at /Users/espressif/esp-idf/components/esp_system/panic.c:452
0x40085729: esp_system_abort at /Users/espressif/esp-idf/components/esp_system/port/esp_system_chip.c:90
0x4008a7ce: abort at /Users/espressif/esp-idf/components/newlib/abort.c:38
0x40007c69: ets_write_char in ROM
0x40008148: ets_printf in ROM
0x400d51d7: app_main at /Users/espressif/esp-idf/examples/get-started/hello_world/main/hello_world_main.c:49
0x400e31bc: main_task at /Users/espressif/esp-idf/components/freertos/app_startup.c:208 (discriminator 13)
0x40087bc5: vPortTaskWrapper at /Users/espressif/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162
.....
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
If an address is not matched in the app source code, IDF monitor also checks the ROM code. Instead of printing the source file name and line number, only the function name followed by ``in ROM`` is displayed::
abort() was called at PC 0x400481c1 on core 0
0x400481c1: ets_rsa_pss_verify in ROM
Stack dump detected
Core 0 register dump:
MEPC : 0x4038051c RA : 0x40383840 SP : 0x3fc8f6b0 GP : 0x3fc8b000
0x4038051c: panic_abort at /Users/espressif/esp-idf/components/esp_system/panic.c:452
0x40383840: __ubsan_include at /Users/espressif/esp-idf/components/esp_system/ubsan.c:313
TP : 0x3fc8721c T0 : 0x37363534 T1 : 0x7271706f T2 : 0x33323130
S0/FP : 0x00000004 S1 : 0x3fc8f714 A0 : 0x3fc8f6dc A1 : 0x3fc8f712
A2 : 0x00000000 A3 : 0x3fc8f709 A4 : 0x00000001 A5 : 0x3fc8c000
A6 : 0x7a797877 A7 : 0x76757473 S2 : 0x00000000 S3 : 0x3fc8f750
S4 : 0x3fc8f7e4 S5 : 0x00000000 S6 : 0x400481b0 S7 : 0x3c025841
0x400481b0: ets_rsa_pss_verify in ROM
.....
The ROM ELF file is automatically loaded from a location based on the ``IDF_PATH`` and ``ESP_ROM_ELF_DIR`` environment variables. This can be overridden by calling ``esp_idf_monitor`` and providing a path to a specific ROM ELF file: ``python -m esp_idf_monitor --rom-elf-file [path to ROM ELF file]``.
.. note::
Set environment variable ``ESP_MONITOR_DECODE`` to ``0`` or call esp_idf_monitor with specific command line option: ``python -m esp_idf_monitor --disable-address-decoding`` to disable address decoding.

View File

@ -180,6 +180,47 @@ IDF 监视器在后台运行以下命令,解码各地址::
{IDF_TARGET_TOOLCHAIN_PREFIX}-addr2line -pfiaC -e build/PROJECT.elf ADDRESS
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
如果在应用程序源代码中找不到匹配的地址IDF 监视器还会检查 ROM 代码。此时不会打印源文件名和行号,只显示 ``函数名 in ROM``::
abort() was called at PC 0x40007c69 on core 0
0x40007c69: ets_write_char in ROM
Backtrace: 0x40081656:0x3ffb4ac0 0x40085729:0x3ffb4ae0 0x4008a7ce:0x3ffb4b00 0x40007c69:0x3ffb4b70 0x40008148:0x3ffb4b90 0x400d51d7:0x3ffb4c20 0x400e31bc:0x3ffb4c50 0x40087bc5:0x3ffb4c80
0x40081656: panic_abort at /Users/espressif/esp-idf/components/esp_system/panic.c:452
0x40085729: esp_system_abort at /Users/espressif/esp-idf/components/esp_system/port/esp_system_chip.c:90
0x4008a7ce: abort at /Users/espressif/esp-idf/components/newlib/abort.c:38
0x40007c69: ets_write_char in ROM
0x40008148: ets_printf in ROM
0x400d51d7: app_main at /Users/espressif/esp-idf/examples/get-started/hello_world/main/hello_world_main.c:49
0x400e31bc: main_task at /Users/espressif/esp-idf/components/freertos/app_startup.c:208 (discriminator 13)
0x40087bc5: vPortTaskWrapper at /Users/espressif/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:162
.....
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
如果在应用程序源代码中找不到匹配的地址IDF 监视器还会检查 ROM 代码。此时不会打印源文件名和行号,只显示 ``函数名 in ROM``::
abort() was called at PC 0x400481c1 on core 0
0x400481c1: ets_rsa_pss_verify in ROM
Stack dump detected
Core 0 register dump:
MEPC : 0x4038051c RA : 0x40383840 SP : 0x3fc8f6b0 GP : 0x3fc8b000
0x4038051c: panic_abort at /Users/espressif/esp-idf/components/esp_system/panic.c:452
0x40383840: __ubsan_include at /Users/espressif/esp-idf/components/esp_system/ubsan.c:313
TP : 0x3fc8721c T0 : 0x37363534 T1 : 0x7271706f T2 : 0x33323130
S0/FP : 0x00000004 S1 : 0x3fc8f714 A0 : 0x3fc8f6dc A1 : 0x3fc8f712
A2 : 0x00000000 A3 : 0x3fc8f709 A4 : 0x00000001 A5 : 0x3fc8c000
A6 : 0x7a797877 A7 : 0x76757473 S2 : 0x00000000 S3 : 0x3fc8f750
S4 : 0x3fc8f7e4 S5 : 0x00000000 S6 : 0x400481b0 S7 : 0x3c025841
0x400481b0: ets_rsa_pss_verify in ROM
.....
ROM ELF 文件会根据 ``IDF_PATH````ESP_ROM_ELF_DIR`` 环境变量的路径自动加载。如需覆盖此行为,可以通过调用 ``esp_idf_monitor`` 并指定特定的 ROM ELF 文件路径:``python -m esp_idf_monitor --rom-elf-file [ROM ELF 文件的路径]``
.. note::
将环境变量 ``ESP_MONITOR_DECODE`` 设置为 ``0`` 或者调用 esp_idf_monitor 的特定命令行选项 ``python -m esp_idf_monitor --disable-address-decoding`` 来禁止地址解码。

View File

@ -0,0 +1,7 @@
config TEST_ADDR_LOOKUP_IN_APP
bool "TEST_ADDR_LOOKUP_IN_APP"
default n
config TEST_ADDR_LOOKUP_IN_ROM
bool "TEST_ADDR_LOOKUP_IN_ROM"
default n

View File

@ -8,6 +8,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#if CONFIG_TEST_ADDR_LOOKUP_IN_APP
static volatile bool s_initialization_done = false;
static void initialize(void)
@ -23,9 +24,13 @@ static int get_random_number(void)
}
return rand();
}
#endif // CONFIG_TEST_ADDR_LOOKUP_IN_APP
void app_main(void)
{
printf("app_main is running from 0x%x\n", (int) app_main);
#if CONFIG_TEST_ADDR_LOOKUP_IN_APP
volatile int number = get_random_number();
int *n = malloc(sizeof(int));
@ -33,10 +38,17 @@ void app_main(void)
*n = number;
printf("app_main is running from 0x%x\n", (int) app_main);
printf("Initializer function at 0x%x\n", (int) initialize);
printf("Got %d stored at 0x%x and 0x%x from a function from 0x%x\n", *n, (int) n, (int) (&number), (int) get_random_number);
printf("This is the end of the report\n");
free(n);
#endif // CONFIG_TEST_ADDR_LOOKUP_IN_APP
#if CONFIG_TEST_ADDR_LOOKUP_IN_ROM
printf("Crashing now!\n");
esp_rom_install_channel_putc(1, (void (*)(char)) abort);
esp_rom_printf("a");
#endif // CONFIG_TEST_ADDR_LOOKUP_IN_ROM
}

View File

@ -11,6 +11,10 @@ from pytest_embedded import Dut
@pytest.mark.generic
@pytest.mark.supported_targets
@pytest.mark.parametrize('config', [
'addr_lookup_in_app',
'addr_lookup_in_ROM',
], indirect=True)
def test_monitor_addr_lookup(config: str, dut: Dut) -> None:
# The port needs to be closed because esp_idf_monitor will connect to it
dut.serial.stop_redirect_thread()
@ -22,22 +26,35 @@ def test_monitor_addr_lookup(config: str, dut: Dut) -> None:
with open(monitor_log_path, 'w') as log, pexpect.spawn(monitor_cmd, logfile=log, timeout=5, encoding='utf-8', codec_errors='ignore') as p:
p.expect_exact('main_task: Calling app_main()')
ADDRESS = '0x[a-f0-9]{8}'
ADDRESS = r'0x[a-f0-9]{8}'
FUNC_NAME = r'[a-zA-Z_][\w]*'
p.expect(re.compile(r'app_main is running from ({})'.format(ADDRESS)))
a = p.match.group(1)
p.expect_exact('{}: app_main at'.format(a))
p.expect(re.compile(rf'app_main is running from ({ADDRESS})'))
addr = p.match.group(1)
p.expect_exact(f'{addr}: app_main at')
p.expect(re.compile(r'Initializer function at ({})'.format(ADDRESS)))
a = p.match.group(1)
p.expect_exact('{}: initialize at'.format(a))
if config == 'addr_lookup_in_app':
p.expect(re.compile(rf'Initializer function at ({ADDRESS})'))
addr = p.match.group(1)
p.expect_exact(f'{addr}: initialize at')
p.expect(re.compile(r'Got \d+ stored at ({}) and ({}) from a function from ({})'.format(ADDRESS, ADDRESS, ADDRESS)))
var1 = p.match.group(1)
var2 = p.match.group(2)
func = p.match.group(3)
match_index = p.expect([str(var1), str(var2), pexpect.TIMEOUT])
assert match_index == 2 # should be TIMEOUT because addr2line should not match addresses of variables
p.expect_exact('{}: get_random_number at'.format(func))
p.expect(re.compile(rf'Got \d+ stored at ({ADDRESS}) and ({ADDRESS}) from a function from ({ADDRESS})'))
var1 = p.match.group(1)
var2 = p.match.group(2)
func = p.match.group(3)
match_index = p.expect([str(var1), str(var2), pexpect.TIMEOUT])
assert match_index == 2 # should be TIMEOUT because addr2line should not match addresses of variables
p.expect_exact(f'{func}: get_random_number at')
p.expect_exact('This is the end of the report')
p.expect_exact('This is the end of the report')
elif config == 'addr_lookup_in_ROM':
p.expect_exact('Crashing now!')
p.expect(re.compile(rf'abort\(\) was called at PC ({ADDRESS}) on core 0'))
addr = p.match.group(1)
p.expect_exact(f'abort() was called at PC {addr} on core 0')
p.expect(re.compile(rf'({ADDRESS}): ({FUNC_NAME}) in ROM'))
addr, func = p.match.group(1), p.match.group(2)
p.expect_exact(f'{addr}: {func} in ROM')

View File

@ -0,0 +1 @@
CONFIG_TEST_ADDR_LOOKUP_IN_ROM=y

View File

@ -0,0 +1 @@
CONFIG_TEST_ADDR_LOOKUP_IN_APP=y