ESP32之嚴重錯誤

嚴重錯誤

概述

在某些情況下,程序的執行,沒有按照定義的方式持續執行.在 ESP-IDF 中,這些情況包括:

  • CPU 異常:Illegal Instruction, Load/Store Alignment Error, Load/Store Prohibited error, Double Exception.(非法指令,加載/存儲對齊錯誤,加載/存儲禁止錯誤,雙重異常)
  • 系統級別檢查和安全措施:
    • Interrupt watchdog timeout 中斷看門狗超時
    • Task watchdog timeout 任務監視程序超時(如果設置了 CONFIG_TASK_WDT_PANIC,則僅 fatal)
    • Cache access error 緩存訪問錯誤
    • Brownout detection event 掉電檢測事件
    • Stack overflow 堆棧溢出
    • Stack smashing protection check 堆棧粉碎保護檢查
    • Heap integrity check 堆完整性檢查
  • Failed assertions 斷言失敗,通過 assert ,configASSERT 和類似的宏.

本指南介紹了 ESP-IDF 中用於處理這些錯誤的過程,並提供了有關錯誤故障排除的建議.

Panic 處理

概述中列出的每個錯誤原因都將由 Panic 處理程序處理.

Panic 處理程序將首先將錯誤原因打印到控制檯. 對於 CPU 異常,消息類似於:

Guru Meditation Error: Core 0 panic'ed (IllegalInstruction). Exception was unhandled.
  • 1

對於某些系統級別檢查(中斷監視程序,緩存訪問錯誤),該消息將類似於:

Guru Meditation Error: Core 0 panic'ed (Cache disabled but cached memory region accessed)
  • 1

在所有情況下,錯誤原因將打印在括號中. 有關可能的錯誤原因列表,請參閱 Guru Meditation Errors.

可以使用 CONFIG_ESP32_PANIC 配置選項設置 Panic 處理程序的後續行爲. 可用選項包括:

  • 打印寄存器並重新啓動(CONFIG_ESP32_PANIC_PRINT_REBOOT) - 默認選項.
    這將在異常點打印寄存器值,打印回溯,然後重新啓動芯片.

  • 打印寄存器並暫停(CONFIG_ESP32_PANIC_PRINT_HALT)
    與上述選項類似,但暫停而不是重新啓動. 重啓程序需要外部重置.

  • 無提示重啓(CONFIG_ESP32_PANIC_SILENT_REBOOT)
    不要打印寄存器或回溯,立即重啓芯片.

  • 調用 GDB 存根(CONFIG_ESP32_PANIC_GDBSTUB)
    啓動 GDB 服務器,它可以通過控制檯 UART 端口與 GDB 通信. 有關詳細信息,請參閱 GDB 存根.

Panic 處理程序的行爲受另外兩個配置選項的影響.

  • 如果啓用了 CONFIG_ESP32_DEBUG_OCDAWARE (這是默認設置),則 Panic 處理程序將檢測 JTAG 調試器是否已連接. 如果是,則執行將暫停,控制權將傳遞給調試器. 在這種情況下,寄存器和回溯不會轉儲到控制檯,並且不使用 GDBStub/Core Dump 功能.
  • 如果啓用了核心轉儲功能(CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASHCONFIG_ESP32_ENABLE_COREDUMP_TO_UART 選項),則系統狀態(任務堆棧和寄存器)將被轉儲到 Flash 或 UART,以供以後分析.

下圖說明了 Panic 處理程序的行爲:

![Panic 處理程序流程圖](https://img-blog.csdn.net/20180901140838695?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI3MTE0Mzk3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

寄存器轉儲和回溯

除非啓用了 CONFIG_ESP32_PANIC_SILENT_REBOOT 選項,否則 Panic 處理程序會將一些 CPU 寄存器和回溯打印到控制檯:

Core 0 register dump:
PC      : 0x400e14ed  PS      : 0x00060030  A0      : 0x800d0805  A1      : 0x3ffb5030
A2      : 0x00000000  A3      : 0x00000001  A4      : 0x00000001  A5      : 0x3ffb50dc
A6      : 0x00000000  A7      : 0x00000001  A8      : 0x00000000  A9      : 0x3ffb5000
A10     : 0x00000000  A11     : 0x3ffb2bac  A12     : 0x40082d1c  A13     : 0x06ff1ff8
A14     : 0x3ffb7078  A15     : 0x00000000  SAR     : 0x00000014  EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000  LBEG    : 0x4000c46c  LEND    : 0x4000c477  LCOUNT  : 0xffffffff

Backtrace: 0x400e14ed:0x3ffb5030 0x400d0802:0x3ffb5050

打印的寄存器值是異常幀中的寄存器值,即 CPU 異常或其他嚴重錯誤發生時的值.

如果由於 abort() 調用而執行了 Panic 處理程序,則不會打印寄存器轉儲.

在某些情況下,例如中斷看門狗超時, Panic 處理程序可能會打印額外的 CPU 寄存器 (EPC1-EPC4) 以及在另一個 CPU 上運行的代碼的寄存器/回溯.

Backtrace 行包含 PC:SP 對,其中 PC 是程序計數器,SP 是堆棧指針,用於當前任務的每個堆棧幀. 如果在 ISR 內發生嚴重錯誤,則回溯可能包括來自被中斷的任務和來自 ISR 的 PC:SP 對.

如果使用 IDF Monitor,程序計數器值將轉換爲代碼位置(函數名稱,文件名和行號),輸出將與其他行進行註釋:

Core 0 register dump:
PC      : 0x400e14ed  PS      : 0x00060030  A0      : 0x800d0805  A1      : 0x3ffb5030
0x400e14ed: app_main at /Users/user/esp/example/main/main.cpp:36

A2 : 0x00000000 A3 : 0x00000001 A4 : 0x00000001 A5 : 0x3ffb50dc
A6 : 0x00000000 A7 : 0x00000001 A8 : 0x00000000 A9 : 0x3ffb5000
A10 : 0x00000000 A11 : 0x3ffb2bac A12 : 0x40082d1c A13 : 0x06ff1ff8
0x40082d1c: _calloc_r at /Users/user/esp/esp-idf/components/newlib/syscalls.c:51

A14 : 0x3ffb7078 A15 : 0x00000000 SAR : 0x00000014 EXCCAUSE: 0x0000001d
EXCVADDR: 0x00000000 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0xffffffff

Backtrace: 0x400e14ed:0x3ffb5030 0x400d0802:0x3ffb5050
0x400e14ed: app_main at /Users/user/esp/example/main/main.cpp:36

0x400d0802: main_task at /Users/user/esp/esp-idf/components/esp32/cpu_start.c:470

要查找發生嚴重錯誤的位置,請查看“Backtrace”的下一行. 嚴重錯誤位置是頂行,後續行顯示調用堆棧.

GDB 存根

如果啓用了 CONFIG_ESP32_PANIC_GDBSTUB 選項,則發生嚴重錯誤時, Panic 處理程序不會重置芯片. 相反,它將啓動 GDB 遠程協議服務器,通常稱爲 GDB Stub. 發生這種情況時,可以指示主機上運行的 GDB 實例連接到 ESP32 UART 端口.

如果使用 IDF Monitor,則在 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=xtensa-esp32-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 Monitor 中的 Ctrl-T Ctrl-R,或使用開發板上的外部重置按鈕.

Guru Meditation 錯誤

本節解釋了不同錯誤原因的含義,打印在 Guru Meditation Error:Core panic'ed message 之後的括號中.

有關“Guru Meditation”的歷史淵源,請參閱 Wikipedia 文章.

IllegalInstruction (非法指令)

該 CPU 異常表示執行的指令不是有效指令. 此錯誤的最常見原因有:

  • FreeRTOS 任務功能已經返回. 在 FreeRTOS 中,如果任務函數需要終止,它應該調用 vTaskDelete() 函數並刪除它自己,而不是返回.
  • 無法從 SPI Flash 加載下一條指令. 這種情況通常發生在:
    • 應用程序已將 SPI Flash 引腳重新配置爲其他功能(GPIO,UART 等). 有關 SPI Flash 引腳的詳細信息,請參閱硬件設計指南和芯片或模塊的數據表.
    • 某些外部設備意外連接到 SPI Flash 引腳,干擾了 ESP32 和 SPI Flash 之間的通信.

InstrFetchProhibited (禁止指令加載)

此 CPU 異常表示 CPU 無法加載指令,因爲指令的地址不屬於指令 RAM 或 ROM 中的有效區域.

通常這意味着嘗試調用函數指針,該指針不指向有效代碼. PC (程序計數器)寄存器可用作指示器:它將爲零或將包含垃圾值(不是 0x4xxxxxxx).

LoadProhibited,StoreProhibited(禁止加載,禁止存儲)

當應用程序嘗試讀取或寫入無效的內存位置時,會發生此 CPU 異常. 寫入/讀取的地址可在寄存器轉儲中的 EXCVADDR 寄存器中找到. 如果此地址爲零,則通常表示應用程序嘗試取消引用 NULL 指針. 如果此地址接近於零,則通常意味着應用程序嘗試訪問結構的成員,但指向該結構的指針爲 NULL. 如果該地址是別的(垃圾值,不在 0x3fxxxxxx - 0x6xxxxxxx 範圍內),則可能意味着用於訪問數據的指針未初始化或已損壞.

IntegerDivideByZero(除以 0)

應用程序嘗試將整數除以零.

LoadStoreAlignment(對齊方式不對)

應用程序嘗試讀取或寫入內存位置,並且地址對齊與加載/存儲大小不匹配. 例如,32 位加載只能從 4 字節對齊的地址完成,而 16 位加載只能從 2 字節的對齊地址完成.

LoadStoreError(加載/存儲錯誤)

應用程序嘗試從僅支持 32 位加載/存儲的內存區域進行 8 位或 16 位加載/存儲. 例如,取消引用指向內存存儲器的 char * 指針將導致這樣的錯誤.

Unhandled debug exception(堆棧錯誤)

通常會出現以下消息:

Debug exception reason: Stack canary watchpoint triggered (task_name)

此錯誤表示應用程序已寫入 task_name 任務堆棧的末尾. 請注意,並非每個堆棧溢出都可以保證觸發此錯誤. 任務可能會在堆棧 canary 位置之外寫入堆棧,在這種情況下,不會觸發觀察點.

Interrupt wdt timeout on CPU0 / CPU1(看門狗超時)

表示發生了中斷看門狗超時. 有關詳細信息,請參閱看門狗.

Cache disabled but cached memory region accessed(Cache 禁止)

在某些情況下,ESP-IDF 將暫時禁止通過高速緩存訪問外部 SPI Flash 和 SPI RAM. 例如,spi_flash API 用於讀取/寫入/擦除/mmap SPI Flash 區域. 在這些情況下,任務被掛起,並且未註冊 ESP_INTR_FLAG_IRAM 的中斷處理程序被禁用. 確保使用此標誌註冊的任何中斷處理程序都具有 IRAM/DRAM 中的所有代碼和數據. 有關更多詳細信息,請參閱 SPI Flash API 文檔.

其他嚴重錯誤

Brownout(欠壓)

ESP32 有一個內置的掉電檢測器,默認啓用. 如果電源電壓低於安全水平,掉電檢測器可以觸發系統復位. 可以使用 CONFIG_BROWNOUT_DETCONFIG_BROWNOUT_DET_LVL_SEL 選項配置掉電檢測器. 當掉電檢測器觸發時,將打印以下消息:

Brownout detector was triggered
  • 1

打印消息後,芯片將復位.

請注意,如果電源電壓快速下降,則控制檯上只能看到部分消息.

Corrupt Heap

ESP-IDF 堆實現包含許多堆結構的運行時檢查. 可以在 menuconfig 中啓用其他檢查(“Heap Stisoning”). 如果其中一項檢查失敗,將打印類似於以下內容的消息:

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 Smashing

可以使用 CONFIG_STACK_CHECK_MODE 選項在 ESP-IDF 中啓用 Stack Smashing 保護(基於 GCC -fstack-protector * 標誌). 如果檢測到 Stack Smashing,將打印類似於以下內容的消息:

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

回溯應該指向 Stack Smashing 發生的函數. 檢查功能代碼以獲得對本地陣列的無限制訪問.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章