dump 死机调试指南

本节介绍发生死机后如何导出现场并恢复,所用工具以 Trace32 为主。

Dump 内存方法

Trace32 恢复与解析

用 Trace32 恢复 HCPU 死机现场

  1. 参照上文的导出方法将导出的内存文件和对应的 axf 放到同一目录。更加详细的死机现场保存方法可以查看保存现场方法

  2. 运行 SIFLI-SDK/tools/crash_dump_analyser/simarm/t32marm.exe工具。如下图:

  1. 打开工具后选择 HCPU assertion(HA)进行恢复。如果某些 bin 文件不存在(例如没有 PSRAM2),可取消勾选对应文件。

alt text

  1. 点击 “run_next_step” 加载,加载完成后会显示现场信息:

alt text

  1. 如果内存与地址映射建立成功,可参考芯片手册的 Memory 地址空间查看现场。如果未恢复,请检查 bin 是否有效,或通过修改 jlink/dump 脚本增加或减少需要 dump 的地址空间(例如 sf32lb58x.jlink)。

alt text

  1. 在 Window 菜单中可以切换不同显示窗口。

alt text

B::v.f /l /c 窗口是死机现场的函数调用栈

heapAllocation 窗口显示了系统中所有 heap pool 的分配情况,包括 system heap 以及各 memheap_pool

  • system heap:rt_malloclv_mem_alloc 使用的池

  • memheap_pool:由 rt_memheap_init 创建,分配/释放使用 rt_memheap_alloc / rt_memheap_free

分配信息字段含义:

BLOCK_ADDR: 分配的内存块的起始地址,包括管理项
BLOCK_SIZE: 申请的内存大小,不包括管理项长度
USED: 是否已分配,1 表示已分配,0 表示未分配
TICK: 申请时间,单位为 OS tick(通常 1 ms)
RETURN ADDR: 申请者地址

现场未显示异常栈的处理

如果无法显示死机时的调用栈,可能是 dump 中未保存寄存器或保存异常。可尝试以下方法:

  1. 从 J-Link halt 的 log 信息加载现场寄存器(HR / HCPU Registers)。点击相应按钮,选择导出的 log.txt,工具会将其中 HCPU 的寄存器回填到 Trace32。

alt text

  1. 手动将 log 中打印出的 16 个寄存器值回填到 Trace32 的寄存器窗口(ARM 寄存器对应关系:SP <-> R13,LR <-> R14,PC <-> R15)。

gcc编译的可以尝试将PC修改成和r14的值一样

  1. 在自动恢复现场不成功时,也可以直接按照 hardfault 现场手动恢复。恢复死机现场中断发生时(hardfault 也是中断)的中断函数:

HardFault_Handler->rt_hw_hard_fault_exception->handle_exception

函数内会把寄存器 R0-R4、R12、R14(LR)、PC 压栈到 saved_stack_framesaved_stack_pointer 变量中。

alt text

压栈的寄存器可以看下图二。如下图一的死机现场,地址 0x20054998 上是 R0,地址 0x200549AC 是 LR,地址 0x200549B0 是 PC: 0x10CD6602。

  • 寄存器 PC:存放死机前的程序 PC 指针。

  • 寄存器 LR:存放程序执行完要返回的程序指针。

  • 全局变量 saved_stack_pointer 存的是压栈的基地址。

  • 全局变量 saved_stack_frame 存的是压栈的数据。

alt text alt text

  1. 在 hardfault RT_ERROR_HW_EXCEPTION 死机的情况下,要特别留意出问题的 PC 汇编指令,考虑是否出现异常地址或异常指令,如下图:

alt text

用 Trace32 恢复 LCPU 死机现场

LCPU 恢复与 HCPU 类似,需将 lcpu.axf 和对应的 rom_axf 文件拷贝到脚本目录中:

注意:

  • Keil 编译生成的 LCPU 文件后缀为 .axf,GCC 编译生成的为 .elf

  • 选择 rom_axf 时请根据板子型号选择正确文件 。

alt text

alt text

打开 Trace32 并选择 LA(LCPU assertion)执行对应配置:

Trace32 常用脚本与命令

常用查看命令:

  • L <addr>:例如 L 0x10063c,查看某处 PC 对应源码或反汇编。

  • v.v *:打开变量窗口,支持通配符搜索全局变量。

  • data.dump <addr>:查看内存地址。

  • frame /locals /caller:查看调用栈与局部变量。

  • 在控制台输入 (struct rt_pm *)0x101fa2b9 可把内存按结构体格式解析。

系统状态内置脚本(使用 do 运行):

  • do show_tasks:显示系统中所有线程及状态、栈地址和优先级。

  • do switch_to 0x200A2F7C:在不同线程栈间强制切换上下文。

  • do show_timer / do show_switch_history:查看定时器队列与线程切换历史。

Trace32 show tasks


Heap 分析示例

下图为检测到 heap 泄漏的现场:callstack 窗口显示断言处的调用栈,heapAllocation 窗口的 system heap 列表显示由 rt_malloc 申请的内存块,RETURN ADDR 为调用 rt_malloc 的函数地址,TICK 为申请时间(rt_tick_get)。

System heap 管理项结构示意:第一个 uint16 为特殊标记 0x1EA0,若被修改则表示被非法改写;第二个 uint16 为 used 标志,1 表示已分配,0 表示未分配,其他值意味着内存项被破坏。

例如地址 0x200A27EC 的内存块由 rt_serial_open 前的指令申请,申请大小为 4108 字节。System heap 管理项长度为 28 字节,因此实际使用地址为 block_addr + 28

在出现内存泄漏时,可结合申请者地址与申请时间定位未释放代码位置。