杰理可视化SDK--系统死机异常调试
- 系统异常原因
- 杰理SDK异常调试准备工作
- 杰理SDK系统异常定位
- 异常代码示例1
- 异常代码示例2
在使用杰理可视化SDK进行软件开发时,往往会遇到一些系统异常问题,系统异常是指芯片在运行代码时,由于软件或硬件状态出错,当错误状态未在硬件或软件程序设计覆盖的容错范围,就会引起系统处于未知状态的异常。异常的结果是触发了“系统复位”或者进入“系统异常中断函数”。本篇文章介绍了使用杰理SDK定位系统运行过程中出现死机异常的一些方法。
系统异常原因
常见的系统异常原因主要有以下原因:
- CPU内部触发的异常
程序跑飞:CPU运行到程序非指定地址软件流程,该异常通常由一些没有正确初始化的函数指针变量引起。
除0异常:CPU在除法运算中除数为0,就会触发除0异常。
非对齐内存访问:CPU运行WORD字节内存访问指令时,寻址寄存器不是4对齐的值,或者运行HALF-WORD内存访问指令时,寻址寄存器不是2对齐的值,就会触发非对齐内存访问异常。
堆栈溢出:当CPU的堆栈超出所设定的限制范围时,就会出现堆栈溢出异常,该异常通常与任务用到的堆栈大于配置的堆栈大小引起。 - 内存相关异常
读写非法内存地址:当CPU读写到非法的内存地址,非法内存地址主要包括:空白地址,只读地址、程序员使用MPU保护的内存地址。该异常主要由于buff野指针引起,或者动态申请内存在free buff后,还在访问buff的数据。 - 外设触发的异常
看门狗超时:系统由于长时间没有清看门狗导致的系统异常,该问题常见原因是CPU负载过高,长时间没有进idle任务,或者在某个软件流程死循环,导致系统没有清看门狗。
杰理SDK异常调试准备工作
-
可视化配置工具配置打印异常信息
在“板级配置”->“调试串口”选项中打开“打印异常信息”。
-
生成lst文件
.lst文件是由ELF (Executable and Linkable Format) 文件通过objdump工具反汇编得到的列表文件。通过objdump命令生成的.lst文件可以显示源代码对应的汇编指令,这在软件调试和死机问题定位时非常有用。
SDK默认不会生成.lst文件,需要手动修改download的脚本代码自动调用objdump来生成lst文件,这里是修改SDK目录下download.c文件(SDK/cpu/brxx/tools),修改如下图,表示将使用sdk.elf文件生成sdk.lst文件。
上面修改后每次编译工程代码,tools目录下会生成一个sdk.lst文件。
lst文件它通常包含了由软件生成的各种信息的列表, 是由objdump工具反汇编得到, 它包含了比.map文件更详细的调试信息,如每条指令的地址等。 在出现问题时提供重要的调试线索。 例如通过查看栈帧结构和在.lst文件中查找特定寄存器的地址,开发者可以快速定位到问题所在。
杰理SDK系统异常定位
异常代码示例1
如下程序中在其中的一个函数中插入了一段死循环。
当程序运行到这个位置时,可以看到程序日志打印中刷屏打印timer_no_response最后死机。如下图
具体来说timer_no_response是由于某个定时器没有响应而导致。需要排查是否是程序中的某个函数运行占用了太大CPU资源没有释放导致定时器的处理没有能正常运行响应导致,从而导致系统崩溃死机。
这里我们可以借助lst文件和死机打印时的backtrace,定位到造成异常死机的位置。
这里我们看RETS和RETI寄存器
RETI(Return from Interrupt):这是中断返回指令的寄存器,它将堆栈中保存的程序计数器的地址取出并送回PC,使程序从主程序的中断处继续执行。需要注意的是RETI所在位置的指令未被执行。
RETS(Return from Subroutine):这是从子程序返回的指令,通常用在非中断的子程序调用中。它的主要作用是将堆栈中的返回地址弹出到程序计数器(PC),以便程序可以继续执行调用子程序之后的下一条指令。
根据上面RETI信息排查到代码action_a2dp_play_next()函数中出现的问题,即定位到了异常代码的位置。
异常代码示例2
如下代码中演示了空指针引用造成的系统异常。如下图代码中定义了一个指向函数的指针,并将其初始化为NULL。接下来,我们尝试通过调用ptr()来执行该空函数指针,但由于ptr是空指针,因此调用了之后会导致程序崩溃并死机。
从耳机运行日志log中发现当代码运行到这段程序的时候发生了死机。
由前面的方法查看lst文件和RETS寄存器可以定位到出问题代码发生在
action_enter_low_latency_mode()函数中。