严重错误

[English]

概述

在某些情况下,程序并不会按照我们的预期运行,在 ESP-IDF 中,这些情况包括:

  • CPU 异常:非法指令,加载/存储时的内存对齐错误,加载/存储时的访问权限错误。

  • 系统级检查错误:

  • 使用 assertconfigASSERT 等类似的宏断言失败。

本指南会介绍 ESP-IDF 中这类错误的处理流程,并给出对应的解决建议。

紧急处理程序

概述 中列举的所有错误都会由 紧急处理程序(Panic Handler) 负责处理。

紧急处理程序首先会将出错原因打印到控制台,例如 CPU 异常的错误信息通常会类似于

Guru Meditation Error: Core 0 panic'ed (Illegal instruction). Exception was unhandled.

对于一些系统级检查错误(如中断看门狗超时,高速缓存访问错误等),错误信息会类似于

Guru Meditation Error: Core 0 panic'ed (Cache error). Exception was unhandled.

不管哪种情况,错误原因都会被打印在括号中。请参阅 Guru Meditation 错误 以查看所有可能的出错原因。

紧急处理程序接下来的行为将取决于 CONFIG_ESP_SYSTEM_PANIC 的设置,支持的选项包括:

  • 打印 CPU 寄存器,然后重启(CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT)- 默认选项

    打印系统发生异常时 CPU 寄存器的值,打印回溯,最后重启芯片。

  • 打印 CPU 寄存器,然后暂停(CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT

    与上一个选项类似,但不会重启,而是选择暂停程序的运行。重启程序需要外部执行复位操作。

  • 静默重启(CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT

    不打印 CPU 寄存器的值,也不打印回溯,立即重启芯片。

  • 调用 GDB Stub(CONFIG_ESP_SYSTEM_PANIC_GDBSTUB

    启动 GDB 服务器,通过控制台 UART 接口与 GDB 进行通信。详细信息请参阅 GDB Stub

紧急处理程序的行为还受到另外两个配置项的影响:

  • 如果 CONFIG_ESP32C3_DEBUG_OCDAWARE 被使能了(默认),紧急处理程序会检测 ESP32-C3 是否已经连接 JTAG 调试器。如果检测成功,程序会暂停运行,并将控制权交给调试器。在这种情况下,寄存器和回溯不会被打印到控制台,并且也不会使用 GDB Stub 和 Core Dump 的功能。

  • 如果使能了 Core Dump 功能(CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 或者 CONFIG_ESP_COREDUMP_ENABLE_TO_UART 选项),系统状态(任务堆栈和寄存器)会被转储到 Flash 或者 UART 以供后续分析。

下图展示了紧急处理程序的行为:

紧急处理程序流程图(点击放大)

寄存器转储与回溯

除非启用了 CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT 否则紧急处理程序会将 CPU 寄存器和回溯打印到控制台

Core  0 register dump:
MEPC    : 0x420048b4  RA      : 0x420048b4  SP      : 0x3fc8f2f0  GP      : 0x3fc8a600
TP      : 0x3fc8a2ac  T0      : 0x40057fa6  T1      : 0x0000000f  T2      : 0x00000000
S0/FP   : 0x00000000  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001
A2      : 0x00000064  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x00000000
A6      : 0x42001fd6  A7      : 0x00000000  S2      : 0x00000000  S3      : 0x00000000
S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000
MSTATUS : 0x00001881  MTVEC   : 0x40380001  MCAUSE  : 0x00000007  MTVAL   : 0x00000000
MHARTID : 0x00000000

仅会打印异常帧中 CPU 寄存器的值,即引发 CPU 异常或者其它严重错误时刻的值。

紧急处理程序如果是因 abort() 而调用,则不会打印寄存器转储。

如果使用了 IDF 监视器,该工具会将程序计数器的值转换为对应的代码位置(函数名,文件名,行号),并加以注释

Core  0 register dump:
MEPC    : 0x420048b4  RA      : 0x420048b4  SP      : 0x3fc8f2f0  GP      : 0x3fc8a600
0x420048b4: app_main at /Users/user/esp/example/main/hello_world_main.c:20

0x420048b4: app_main at /Users/user/esp/example/main/hello_world_main.c:20

TP      : 0x3fc8a2ac  T0      : 0x40057fa6  T1      : 0x0000000f  T2      : 0x00000000
S0/FP   : 0x00000000  S1      : 0x00000000  A0      : 0x00000001  A1      : 0x00000001
A2      : 0x00000064  A3      : 0x00000004  A4      : 0x00000001  A5      : 0x00000000
A6      : 0x42001fd6  A7      : 0x00000000  S2      : 0x00000000  S3      : 0x00000000
0x42001fd6: uart_write at /Users/user/esp/esp-idf/components/vfs/vfs_uart.c:201

S4      : 0x00000000  S5      : 0x00000000  S6      : 0x00000000  S7      : 0x00000000
S8      : 0x00000000  S9      : 0x00000000  S10     : 0x00000000  S11     : 0x00000000
T3      : 0x00000000  T4      : 0x00000000  T5      : 0x00000000  T6      : 0x00000000
MSTATUS : 0x00001881  MTVEC   : 0x40380001  MCAUSE  : 0x00000007  MTVAL   : 0x00000000
MHARTID : 0x00000000

若要查找发生严重错误的代码位置,请查看 “Backtrace” 的后面几行,发生严重错误的代码显示在顶行,后续几行显示的是调用堆栈。

GDB Stub

如果启用了 CONFIG_ESP_SYSTEM_PANIC_GDBSTUB 选项,在发生严重错误时,紧急处理程序不会复位芯片,相反,它将启动 GDB 远程协议服务器,通常称为 GDB Stub。发生这种情况时,可以让主机上运行的 GDB 实例通过 UART 端口连接到 ESP32。

如果使用了 IDF 监视器,该工具会在 UART 端口检测到 GDB Stub 提示符后自动启动 GDB,输出会类似于:

Entering gdb stub now.
$T0b#e6GNU gdb (crosstool-NG crosstool-ng-1.22.0-80-gff1f415) 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_apple-darwin16.3.0 --target=riscv32-esp-elf".
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 /Users/user/esp/example/build/example.elf...done.
Remote debugging using /dev/cu.usbserial-31301
0x400e1b41 in app_main ()
    at /Users/user/esp/example/main/main.cpp:36
36      *((int*) 0) = 0;
(gdb)

在 GDB 会话中,我们可以检查 CPU 寄存器,本地和静态变量以及内存中任意位置的值。但是不支持设置断点,改变 PC 值或者恢复程序的运行。若要复位程序,请退出 GDB 会话,在 IDF 监视器 中连续输入 Ctrl-T Ctrl-R,或者按下开发板上的复位按键也可以重新运行程序。

Guru Meditation 错误

本节将对打印在 Guru Meditation Error: Core panic'ed 后面括号中的致错原因进行逐一解释。

注解

想要了解 “Guru Meditation” 的历史渊源,请参阅 维基百科

Illegal instruction

此 CPU 异常表示当前执行的指令不是有效指令,引起此错误的常见原因包括:

  • FreeRTOS 中的任务函数已返回。在 FreeRTOS 中,如果想终止任务函数,需要调用 vTaskDelete() 函数释放当前任务的资源,而不是直接返回。

  • 无法从 SPI Flash 中加载下一条指令,这通常发生在:

    • 应用程序将 SPI Flash 的引脚重新配置为其它功能(如 GPIO,UART 等等)。有关 SPI Flash 引脚的详细信息,请参阅硬件设计指南和芯片/模组的数据手册。

    • 某些外部设备意外连接到 SPI Flash 的引脚上,干扰了 ESP32-C3 和 SPI Flash 之间的通信。

Instruction address misaligned

此 CPU 异常表示要执行的指令地址非 2 字节对齐。

Instruction access fault, Load access fault, Store access fault

当应用程序尝试读取或写入无效的内存位置时,会发生此类 CPU 异常。此类无效内存地址可以在寄存器转储的 MTVAL 中找到。如果该地址为零,通常意味着应用程序正尝试解引用一个 NULL 指针。如果该地址接近于零,则通常意味着应用程序尝试访问某个结构体的成员,但是该结构体的指针为 NULL。如果该地址是其它非法值(不在 0x3fxxxxxx - 0x6xxxxxxx 的范围内),则可能意味着用于访问数据的指针未初始化或者已经损坏。

Breakpoint

当执行 EBREAK 指令时,会发生此 CPU 异常。

Load address misaligned, Store address misaligned

应用程序尝试读取/写入的内存位置不符合加载/存储指令对字节对齐大小的要求,例如,32 位加载指令只能访问 4 字节对齐的内存地址,而 16 位加载指令只能访问 2 字节对齐的内存地址。

Interrupt wdt timeout on CPU0 / CPU1

这表示发生了中断看门狗超时,详细信息请查阅 看门狗 文档。

Cache error

在某些情况下,ESP-IDF 会暂时禁止通过高速缓存访问外部 SPI Flash 和 SPI RAM,例如在使用 spi_flash API 读取/写入/擦除/映射 SPI Flash 的时候。在这些情况下,任务会被挂起,并且未使用 ESP_INTR_FLAG_IRAM 注册的中断处理程序会被禁用。请确保任何使用此标志注册的中断处理程序所访问的代码和数据分别位于 IRAM 和 DRAM 中。更多详细信息请参阅 SPI Flash API 文档

其它严重错误

欠压

ESP32-C3 内部集成掉电检测电路,并且会默认启用。如果电源电压低于安全值,掉电检测器可以触发系统复位。掉电检测器可以使用 CONFIG_ESP32C3_BROWNOUT_DETCONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL 这两个选项进行设置。

当掉电检测器被触发时,会打印如下信息:

Brownout detector was triggered

芯片会在该打印信息结束后复位。

请注意,如果电源电压快速下降,则只能在控制台上看到部分打印信息。

Heap 不完整

ESP-IDF 堆的实现包含许多运行时的堆结构检查,可以在 menuconfig 中开启额外的检查(“Heap Poisoning”)。如果其中的某项检查失败,则会打印类似如下信息:

CORRUPT HEAP: Bad tail at 0x3ffe270a. Expected 0xbaad5678 got 0xbaac5678
assertion "head != NULL" failed: file "/Users/user/esp/esp-idf/components/heap/multi_heap_poisoning.c", line 201, function: multi_heap_free
abort() was called at PC 0x400dca43 on core 0

更多详细信息,请查阅 堆内存调试 文档。

Stack 粉碎

Stack 粉碎保护(基于 GCC -fstack-protector* 标志)可以通过 ESP-IDF 中的 CONFIG_COMPILER_STACK_CHECK_MODE 选项来开启。如果检测到 Stack 粉碎,则会打印类似如下的信息:

Stack smashing protect failure!

abort() was called at PC 0x400d2138 on core 0

Backtrace: 0x4008e6c0:0x3ffc1780 0x4008e8b7:0x3ffc17a0 0x400d2138:0x3ffc17c0 0x400e79d5:0x3ffc17e0 0x400e79a7:0x3ffc1840 0x400e79df:0x3ffc18a0 0x400e2235:0x3ffc18c0 0x400e1916:0x3ffc18f0 0x400e19cd:0x3ffc1910 0x400e1a11:0x3ffc1930 0x400e1bb2:0x3ffc1950 0x400d2c44:0x3ffc1a80
0

回溯信息会指明发生 Stack 粉碎的函数,建议检查函数中是否有代码访问本地数组时发生了越界。