ESP32-严重错误-崩溃分析系列-01

1. 崩溃堆栈信息

Guru Meditation Error: Core  0 panic'ed (StoreProhibited). Exception was unhandled.
Core 0 register dump:
PC      : 0x40090ad1  PS      : 0x00060130  A0      : 0x80090640  A1      : 0x3ffbbe80  
A2      : 0x3ff00004  A3      : 0x00000000  A4      : 0x3ffbb094  A5      : 0x00000000  
A6      : 0x00000000  A7      : 0x00000000  A8      : 0x00000000  A9      : 0x00000000  
A10     : 0x00000000  A11     : 0x00060123  A12     : 0x00060120  A13     : 0x3f408329  
A14     : 0x3ffc2138  A15     : 0x00060023  SAR     : 0x00000020  EXCCAUSE: 0x0000001d  
EXCVADDR: 0x00000008  LBEG    : 0x4000c2e0  LEND    : 0x4000c2f6  LCOUNT  : 0x00000000  

ELF file SHA256: 75b6d69fc791b5e9

Backtrace: 0x40090ace:0x3ffbbe80 0x4009063d:0x3ffbbea0

I (22620) esp_core_dump_flash: Save core dump to flash...
I (22626) esp_core_dump_common: Found tasks: (17)!
I (22772) esp_core_dump_flash: Core dump has been saved to flash.

为什么会打印这一串信息出来?

紧急处理程序会将 CPU 寄存器和回溯打印到控制台。

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

上面的芯片使用的是ESP32D模块,实际上ESP32 采用两个哈佛结构 Xtensa LX6 CPU 构成双核系统。但是在小米原生SDK上,只开启了single core。

所以上面的堆栈信息打印的就是CPU0的崩溃信息。 

这一串东西该如何解析出来呢,这个是CPU寄存器的信息呢?

2. 崩溃原因定位

LoadProhibited, StoreProhibited 当应用程序尝试读取或写入无效的内存位置时,会发生此类 CPU 异常。此类无效内存地址可以在寄存器转储的 EXCVADDR 中找到。

  • 如果该地址为零,通常意味着应用程序正尝试解引用一个 NULL 指针。
  • 如果该地址接近于零,则通常意味着应用程序尝试访问某个结构体的成员,但是该结构体的指针为 NULL。  
  • 如果该地址是其它非法值(不在 0x3fxxxxxx - 0x6xxxxxxx 的范围内),则可能意味着用于访问数据的指针未初始化或者已经损坏。

此处崩溃中 EXCVADDR: 0x00000008  符合第二种情况。 说明是结构体的指针为NULL。就是空指针崩溃了。

3. 崩溃信息解析

如果你已经安装了esp的编译tool,并且已经加载到path路径下,可以直接执行:

xtensa-esp32-elf-addr2line -pfiaC -e meizu_light.elf 0x40090ace:0x3ffbbe80 0x4009063d:0x3ffbbea0

得出了堆栈详情信息:

0x40090ace: uxListRemove at /home/zy/esp/esp-idf-xiaomi/components/freertos/list.c:214
0x4009063d: prvProcessExpiredTimer at /home/zy/esp/esp-idf-xiaomi/components/freertos/timers.c:495
 (inlined by) prvProcessTimerOrBlockTask at /home/zy/esp/esp-idf-xiaomi/components/freertos/timers.c:571
 (inlined by) prvTimerTask at /home/zy/esp/esp-idf-xiaomi/components/freertos/timers.c:544

4. 源码分析

static void prvTimerTask( void *pvParameters )
{
TickType_t xNextExpireTime;
BaseType_t xListWasEmpty;

	/* Just to avoid compiler warnings. */
	( void ) pvParameters;

	for( ;; )
	{
		/* Query the timers list to see if it contains any timers, and if so,
		obtain the time at which the next timer will expire. */
		xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty );

		/* If a timer has expired, process it.  Otherwise, block this task
		until either a timer does expire, or a command is received. */
		prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );

		/* Empty the command queue. */
		prvProcessReceivedCommands();
	}
}

prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );

调用了 prvProcessExpiredTimer( xNextExpireTime, xTimeNow );

调用了 ( void ) uxListRemove( &( pxTimer->xTimerListItem ) );

调用了 pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious;

定位到此处,应该就是 pxItemToRemove->pxNext 为空指针,而去调用一个空指针的成员变量时,就会出现空指针崩溃问题。

接着去自己业务代码里面查看 timer定时器相关的代码。

5.业务代码自查

static void cancel_long_block_timer() {
  if (long_block_timer != NULL) {
    xTimerStop(long_block_timer, 0);
    long_block_timer = NULL;
    MZ_WARN(TAG, "stop block check");
  }
}

发现了在频繁的创建timer,删除timer。最值得怀疑的地方。 最后改成只创建一次timer。在不需要的地方stop timer即可。满足业务需求。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

专注和坚持是最好的导师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值