ESP8266/ESP32 程序崩溃 (crash) 时的定位方法一:寄存器和调用栈 (backtrace)
简介应用代码不太健壮时, 会遇到程序 crash 情况, 通常可通过 PC 寄存器, A0 寄存器, EXCCAUSE 寄存器和 backtrace 来初步定位问题.例如截图中 crash 信息:通过如下方式定位分析问题时,旨在让读者能够初步感知 crash 时的现场. 不代表程序 100%crash 在分析出的位置上.通过 backtrace 定位如上截图所示, 黄色打印...
简介
应用代码不太健壮时, 会遇到程序 crash
情况, 通常可通过 PC
寄存器, A0
寄存器, EXCCAUSE
寄存器和 backtrace
来初步定位问题.
例如截图中 crash
信息:
通过如下方式定位分析问题时, 旨在让读者能够初步感知
crash
时的现场. 不代表程序 100% crash 在分析出的位置上.
通过 backtrace 定位
如上截图所示, 黄色打印是 backtrace
信息, 即程序执行时的调用栈.
从 backtrace
中初步定位到问题出现在 user_main.c
的 21 行附近, 即如图的 printf
可能会有问题.
通过 PC/A0 寄存器定位
通过 PC/A0
寄存器中信息通常也可以初步看到 crash
附近代码位置.
PC
判断 crash
原因参考:
1.PC
寄存器值若为 0x00000000
, 可能是代码中执行某个空 callback
, 或执行调用某个为空的函数指针.
2.PC
寄存器值若为野指针, 例如 0x80001210
, 可能是内存踩踏. 一旦踩踏到栈底的 PC
, 形如 retw.n
就会弹出野指针.
PC 野指针: 笔者认为指不在正常
IRAM
范围内的指针.
ESP8266
正常IRAM
地址为0x40100000
-0x40201010+appsize
(可在esp8266.ld
中查看);
ESP32
正常IRAM
地址为0x40080000
-0x40400000
(可在esp32.ld
中查看).
内存踩踏原因很多, 例如:
- 使用不安全的
APIs
,strcat()
,strcpy()
,sprintf()
等 - 内存操作越界,
memset
,memcpy
,memmove
- 边界问题: 数组操作越界,
strlen
写错为sizeof
task stack
过小, 导致程序执行时越界- 函数返回局部变量引起的惨案
- 操作已经
free
过的指针
3.PC
寄存器值看起来正常, 例如 0x40201234
第一种是 PC
寄存器值真的正常, 这种情况需看下 PC 寄存器值对应的函数.
第二种是 PC
寄存器值看起来正常, 实际 PC 寄存器值无效.
例如 PC
指向一個 flash
上函数, 而刚好在执行此函数前, cache 被禁用(通常是 flash 相关操作), 则也会出现 因无法访问此函数而 crash
.
通过 PC/A0 寄存器定位具体方法如下:
进入应用编译目录, 通过下面命令进行反汇编.
ESP8266:
cd build
xtensa-lx106-elf-objdump -S build/xxx.elf > a.S
ESP32:
cd build
xtensa-esp32-elf-objdump -S build/xxx.elf > a.S
-S
: 展示源代码和反汇编代码
- 对
PC/A0
寄存器作用了解不深读者, 可以看下计算机组成原理或相关书籍.xtensa-lx106-elf-objdump
/xtensa-esp32-elf-objdump
和linux
上objdump
是几乎一致的.- 可通过
xtensa-lx106-elf-objdump --help
/xtensa-esp32-elf-objdump --help
查看详细说明.
通过查看 a.S
文件中的 PC
寄存器和 A0
寄存器信息, 这里以 PC: 0x40228115
为例:
如图所示:
PC: 0x40228115
指向 printf
的反汇编代码, 前两条汇编指令对应: 从内存地址 0 读取值到寄存器 a3
.
按照 C 入参规则, 即对应 *p 入参错误
, 由于 p 为 NULL
, 而读取 0x0
地址内容是属于非法读取, 因此程序 crash
.
通过 EXCCAUSE 寄存器定位
EXCCAUSE
寄存器: 即 exception causes
, 程序异常时, 该寄存器会保存 crash
原因, 供读者参考.
如本文简介中截图所示:
EXCCAUSE: 0x0000001c
对应的是 LoadProhibitedCause
, 即一个加载引用了一个映射了不允许加载的属性的页.
其他 EXCCAUSE Code
可通过查看如下 Exception Causes
.
Exception Causes
EXCCAUSE Code | Cause Name | Cause Description [Required Option] | EXCVADDR Loaded |
---|---|---|---|
0 | IllegalInstructionCause | Illegal instruction [Exception Option] | No |
1 | SyscallCause | SYSCALL instruction [Exception Option] | No |
2 | InstructionFetchErrorCause | Processor internal physical address or data error during instruction fetch [Exception Option] | Yes |
3 | LoadStoreErrorCause | Processor internal physical address or data error during load or store [Exception Option] | Yes |
4 | Level1InterruptCause | Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register [ Interrupt Option ] | No |
5 | AllocaCause | MOVSP instruction, if caller’s registers are not in the register file [ Windowed Register Option] | No |
6 | IntegerDivideByZeroCause | QUOS, QUOU, REMS, or REMU divisor operand is zero [ 32-bit Integer Divide Option] | No |
7 | / | Reserved for Tensilica | / |
8 | PrivilegedCause | Attempt to execute a privileged operation when CRING ≠ 0 [MMU Option] | No |
9 | LoadStoreAlignmentCause | Load or store to an unaligned address [Unaligned Exception Option] | Yes |
10…11 | / | Reserved for Tensilica | / |
12 | InstrPIFDataErrorCause | PIF data error during instruction fetch [Processor Interface Option] | Yes |
13 | LoadStorePIFDataErrorCause | Synchronous PIF data error during LoadStore access [Processor Interface Option] | Yes |
14 | InstrPIFAddrErrorCause | PIF address error during instruction fetch [Processor Interface Option] | Yes |
15 | LoadStorePIFAddrErrorCause | Synchronous PIF address error during LoadStore access [Processor Interface Option] | Yes |
16 | InstTLBMissCause | Error during Instruction TLB refill [MMU Option] | Yes |
17 | InstTLBMultiHitCause | Multiple instruction TLB entries matched [MMU Option] | Yes |
18 | InstFetchPrivilegeCause | An instruction fetch referenced a virtual address at a ring level less than CRING [MMU Option] | Yes |
19 | / | Reserved for Tensilica | / |
20 | InstFetchProhibitedCause | An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch [Region Protection Option or MMU Option] | Yes |
21…23 | / | Reserved for Tensilica | / |
24 | LoadStoreTLBMissCause | Error during TLB refill for a load or store [MMU Option] | Yes |
25 | LoadStoreTLBMultiHitCause | Multiple TLB entries matched for a load or store [MMU Option] | Yes |
26 | LoadStorePrivilegeCause | A load or store referenced a virtual address at a ring level less than CRING [MMU Option] | Yes |
27 | / | Reserved for Tensilica | / |
28 | LoadProhibitedCause | A load referenced a page mapped with an attribute that does not permit loads [Region Protection Option or MMU Option] | Yes |
29 | StoreProhibitedCause | A store referenced a page mapped with an attribute that does not permit stores [Region Protection Option or MMU Option] | Yes |
30…31 | / | Reserved for Tensilica | / |
32…39 | CoprocessornDisabled | Coprocessor n instruction when cpn disabled. n varies 0…7 as the cause varies 32…39 [Coprocessor Option] | No |
40…63 | / | Reserved for Tensilica | / |
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)