Crash 处理 ============ 本章节介绍在 WQ SDK 启动及运行中出现 Crash 后,如何通过 log 定位问题,包括 log 分析、BackTrace 分析、反汇编分析及 GDB 与 CoreDump 分析。 Log 分析 -------- 发生 crash 后, crash 相关信息会通过 LOG 通道输出。 WQ 下载调试工具接收的 log ,存放在工具软件目录下的 **Log\UART** 文件夹中的 **.log** 文件。详细信息如下: :: [auto]crash_test.c:140 Asserted! //crash 发生的代码位置 0x0412006e:0x020093f8 //crash 发生时,调用栈信息 0x041179cc:0x02009438 0x02005f10:0x02009448 0x0412006e:0x00000000 [auto]mcause: 0x03[Breakpoint] //assert 类型的 crash [auto]core0: exception registers dump //crash 发生在 core0 ra = 0x0200005a sp = 0x020092b0 t0 = 0x0000002a t1 = 0x00000000 t2 = 0x00000023 fp = 0x020093c8 s1 = 0x02006970 a0 = 0x00000000 a1 = 0x000000d2 a2 = 0x00000030 a3 = 0x00e88f1f a4 = 0x00000002 a5 = 0x00000000 a6 = 0x02009158 a7 = 0x20303030 s2 = 0x02006970 s3 = 0x04116000 s4 = 0x00000000 s5 = 0x00000001 s6 = 0xa5a5a5a5 s7 = 0xa5a5a5a5 s8 = 0xa5a5a5a5 s9 = 0xa5a5a5a5 s10 = 0xa5a5a5a5 s11 = 0xa5a5a5a5 t3 = 0x00000039 t4 = 0x00000009 t5 = 0x0000002e t6 = 0x00000025 0x0412006e:0x020093f8 //crash 发生时,调用栈信息 0x041179cc:0x02009438 0x02005f10:0x02009448 0x0412006e:0x00000000 [auto]mepc: 0x02000076, mbadaddr: 0x00000000 //crash 发生时的代码位置 coredump_callback address: 0x0411 6c84 Current task RA/SP/A0/FP/EPC 2000 05a 20092b0 0 20093c8 2000076 Core dump len = 4192 (8 0) ================= CORE DUMP START ================= YBAAAAEAAAAIAAAAfAAAAA== WJQAAjiEAAKkkgACSJQAAg== sJIAAiROAABQcwACUHMAAliUAAJIcwACC AAAAEvrlycWXxZ/WJQAAgAAAAAHAAAA ...... 用户可以通过 mcause 判断 crash 类型,初步分析 crash 原因。表 5 1 列举了常见的 mcause 和相应的分析策略。 .. list-table:: 常见 mcause 与分析策略 * - mcause - carsh 原因 - 分析策略 * - 0x8000000b - 外部中断 - | 外部中断可能导致 crash dump 的只有 watchdog timeout 中 | 断。因此,可以判定为代码存在死循环 * - 1 - 数据访问异常 - - | 中断服务函数代码在 Flash 里面,找到 mepc 即可。 | mepc 所在的函数必须加上IRAM TEXT(func xxxx) - | 野指针数据访问。如果 mbadaddr 不是 Flash 的地址而 | 是一个野指针,则需要根据汇编找到对应的变量,再进 | 一步推测野指针产生的原因。 * - 2 - 非法指令异常 - 首先根据 crash dump内容排查指令是否被修改。 - 被修改:说明存在内存踩踏。 - 未被修改:检查指令自身问题。 * - 3 - assert 对应的异常 - 同样看log 和 BackTrace 定位问题。 * - 5 - 非对齐访问的异常 - 根据 mepc 定位代码和变量,一般都是地址不对齐导致的。 * - 7 - 0地址访问对应的异常 - | 根据上面的办法找到对应的变量和代码,推测 0 地址访问产 | 生的原因。 | 根据上文中的 mcause: 0x03 ,可以判断 Crash 原因为 assert 对应的异常。 BackTrace 分析 --------------- 用户可以通过运行 BackTrace 命令查看调用栈信息,进而定位 Crash 原因。BackTrace 命令可以使用 WQ 下载调试工具运行或通过编译环境运行。 运行 BackTrace 命令需要 log 和相应 core 的 **.elf** 文件。ELF 文件在编译时生成,存放于 **build** 目录下相应 core 的文件夹中。如: **//application/target_a/build/acore/target_a_acore.elf** 。 使用 WQ 下载调试工具 ^^^^^^^^^^^^^^^^^^^^ 用户可以使用 WQ 下载调试工具运行 BackTrace 命令,查看调用栈信息。操作步骤如下: 1. 打开 WQ 下载调试工具,点击 :guilabel:`Log&Debug` 菜单栏。 2. 在左侧 :guilabel:`LOG FILE` 区域,选择 UART 输出的 **.log** 文件。 .. admonition:: 说明 WQ 下载调试工具接收的 log 存放于工具软件目录下的 **Log\UART** 文件夹下的 **.log** 文件。 1. 在左侧 :guilabel:`APP ELF FILE` 区域,选择发生 crash 的 core 所对应的 **.elf** 文件。 2. 确保串口处于关闭状态。点击 :guilabel:`START`,即可生成调用栈信息。 .. image:: ../../_static/SDK_5-1_log_debug.png :align: center | 使用 BackTrace 命令 ^^^^^^^^^^^^^^^^^^^^ 在编译环境下运行 BackTrace 命令可以查看调用栈信息。命令如下: .. code-block:: shell riscv64-unknown-elf-addr2line -f -e build/acore/target_a_acore.elf 0x0412006e:0x020093f8 0x041179cc:0x02009438 0x02005f10:0x02009448 0x0200005a:0x00000000 命令解析: - ``riscv64-unknown-elf-addr2line -f -e`` 为指令和参数。 - ``build/acore/target_a_acore.elf`` 为 **.elf** 文件相对路径。 - ``0x0412006e:0x020093f8 0x041179cc:0x02009438 0x02005f10:0x02009448 0x0200005a:0x00000000`` 为 Crash Log 中的栈调用信息。 输出结果如下: :: crash_test /wqcore/components/share_task_2/src/crash_test.c:141 app_entry /wqcore/application/target_a/acore/app.c:221 (discriminator 3) wrapper_task_func /wqcore/os/os_shim/src/freertos_10_2_1/os_task.c:44 wq_debug_assert /wqcore/components/wq_debug/src/wq_debug.c:608 查看反汇编 ------------ 汇编文件可以用于 Crash 原因定位。 WQ SDK 编译时会在相应 core 的 **.elf** 文件目录下生成.asm 汇编文件,如 **//application/target_a/build/acore/target_a_acore.asm** 。 用户也可以通过 **.elf** 文件和反汇编指令生成汇编文件,方便排查定位故障。反汇编指令如下: .. code-block:: shell riscv64-unknown-elf-objdump -S -x target_a_acore.elf > target_a_acore.asm GDB 与 CoreDump 分析 ----------------------- Log 中 **"=== CORE DUMP START ==="** 与 **"=== CORE DUMP END ==="** 之间的内容(不包含上述两行)为 CoreDump 信息。详细信息如下: :: ...... Current task RA/SP/A0/FP/EPC 2000 05a 20092b0 0 20093c8 2000076 Core dump len = 4192 (8 0) ================= CORE DUMP START ================= YBAAAAEAAAAIAAAAfAAAAA== WJQAAjiEAAKkkgACSJQAAg== sJIAAiROAABQcwACUHMAAliUAAJIcwACC AAAAEvrlycWXxZ/WJQAAgAAAAAHAAAA ...... 7x6iiRJ1QtSefzCbz7Zzd9XaBM//nGsb3VEXELWWS7GOaIW74j9QmNww5zqssBvg e/dpClD6j9zc8AiMGnAcYESv5ejPgdHcNJJwqhaA4uRfqwWGKn481s+sNxJKvhU/ yHitWLoqXhC7HmjaAAAAAAAAAAA= ================= CORE DUMP END ================= 将 **CoreDump** 信息拷贝到一个文件中,重命名以 **.b64** 做后缀,如 **coredump.b64** 。 可以通过 **wq_coredump.py** 脚本执行 CoreDump 指令: .. code-block:: shell python3 tools/wq_coredump.py dbg_corefile --gdb ~/xxx/riscv64-unknown-elf-gdb -c dump/coredump.b64 -t b64 elf/xcore.elf 指令解析: - ``tools/wq_coredump.py`` 为 **coredump** 脚本路径 - ``~/xxx/riscv64-unknown-elf-gdb`` 为 **GDB** 脚本绝对路径 - ``dump/coredump.b64`` 为 **coredump** 文件路径 - ``elf/xcore.elf`` 为 **.elf** 文件路径 执行完成后,进入 GDB 调试状态。可以执行一些 GDB 指令查看 **backtrace(bt)** 、thread 信息(**info thread** )、全局变量( **p** )等信息,如下图所示。 .. image:: ../../_static/SDK_5-2_GDB_debug.png :align: center