.. _gdb_debug: ================ GDB 调试指南 ================ 本文档介绍如何使用 GDB 调试 ARCS SDK 应用程序,包括环境配置、调试方式和常用命令。 .. note:: ARCS SDK 使用 RISC-V 架构的 Nuclei 工具链,调试时需要使用对应的 GDB 工具。 .. _gdb_overview: 调试概述 ======== GDB(GNU Debugger)是一个功能强大的调试工具,支持断点、单步执行、变量查看等调试功能。在嵌入式开发中,通常使用 GDB 配合 JTAG/SWD 调试器进行远程调试。 支持的调试方式 -------------- - **JTAG 调试**:通过 JTAG 接口连接调试器 - **远程 GDB 调试**:使用 GDB Server 进行远程调试 - **命令行调试**:使用 GDB 命令行界面进行调试 .. _gdb_preparation: 环境准备 ======== 安装调试工具 ------------ 1. **确认工具链安装** 确保已按照 :ref:`getting_started` 中的步骤安装了 Nuclei 工具链。 2. **安装 J-Link 软件** 从 `J-LINK `_ 下载对应平台的 J-Link 软件包进行安装,推荐安装 V7.98 及以上版本。 安装完成后,检查 J-Link 是否正常安装: .. code-block:: shell JLinkGDBServerCLExe --version 3. **配置 J-Link 设备支持** 下载并安装 ARCS J-Link 设备配置文件: .. code-block:: shell curl -L -o /tmp/JLinkDevices.zip \ http://listenai-firmware-delivery.oss-cn-beijing.aliyuncs.com/ARCS/tools/JLinkDevices.zip && \ unzip -o /tmp/JLinkDevices.zip -d $HOME/.config/SEGGER 检查 ARCS 设备是否正常识别: .. code-block:: shell JLinkGDBServerCLExe -device ARCS -if cJTAG -speed 4000 -port 2331 \ -jlinkscriptfile $HOME/.config/SEGGER/JLinkDevices/scripts/arcs/jtagscan1.JLinkScript .. important:: $HOME/.config/SEGGER/JLinkDevices/scripts/arcs目录下有两个脚本文件, jtagscan0.JLinkScript: 连接core0(AP核心) jtagscan1.JLinkScript: 连接core1(CP核心) 请根据实际情况选择使用哪一个。 .. note:: SDK的示例代码如无特别说明外,默认都是在core1(CP核心)上运行的. 4. **验证 GDB 工具** 检查 GDB 工具是否可用: .. code-block:: shell ${NUCLEI_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gdb --version 应该看到类似以下输出: .. code-block:: text GNU gdb (GDB) 13.2.90.20230712-git Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. 硬件要求 -------- .. important:: 需要 J-Link 仿真器 V11 或更高版本。 准备调试文件 ------------ 编译时需要生成包含调试信息的 ELF 文件: .. code-block:: shell ./build.sh -S samples/helloworld -DBOARD=arcs_evb 编译成功后,在 ``build`` 目录下会生成: - ``helloworld``:包含调试符号的可执行文件 - ``helloworld.bin``:用于烧录的二进制文件 .. note:: 默认情况下,SDK 编译生成的 ELF 文件已包含调试信息(``-g`` 选项)。 .. _gdb_debugging: 开始调试 ======== 硬件连接 -------- 将 J-Link 仿真器连接到 ARCS 开发板: .. image:: ../assets/ARCS-JLINK调试器连接图.png :alt: ARCS J-Link 调试器连接示意图 调试步骤 -------- 完整的 GDB 调试流程如下: 1. **启动 J-Link GDB Server** 在终端中启动 J-Link GDB Server: .. code-block:: shell JLinkGDBServerCLExe -device ARCS -if cJTAG -speed 4000 -port 2331 \ -jlinkscriptfile $HOME/.config/SEGGER/JLinkDevices/scripts/arcs/jtagscan1.JLinkScript 成功启动后,GDB Server 会监听在 2331 端口。 2. **启动 GDB 并加载 ELF 文件** 在另一个终端中启动 GDB: .. code-block:: shell ${NUCLEI_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gdb build/helloworld 启动后会进入 GDB 命令行界面: .. code-block:: text GNU gdb (GDB) 13.2.90.20230712-git ... Reading symbols from build/helloworld... (gdb) 3. **连接到 GDB Server** 在 GDB 命令行中连接到 J-Link GDB Server: .. code-block:: text (gdb) target remote localhost:2331 .. note:: - 如果 GDB Server 运行在其他机器上,将 ``localhost`` 替换为对应的 IP 地址 - 默认端口为 2331,如果使用其他端口,请相应修改 4. **加载程序到目标设备** 连接成功后,将程序加载到目标设备: .. code-block:: text (gdb) load Loading section .text, size 0x1234 lma 0x20000000 ... Start address 0x20000000, load size 4660 Transfer rate: 1165 bytes/sec, 1165 bytes/write. 5. **开始调试** 设置断点并运行程序: .. code-block:: text (gdb) break main Breakpoint 1 at 0x20000a34: file main.c, line 15. (gdb) continue Continuing. 快速调试脚本 ------------ 为了简化调试流程,可以创建一个 ``.gdbinit`` 文件来自动执行常用命令: .. code-block:: text # 连接到 GDB Server target remote localhost:2331 .. note:: 出于安全考虑,GDB 可能不会自动加载当前目录的 ``.gdbinit`` 文件。可以在用户主目录的 ``~/.gdbinit`` 中添加: .. code-block:: text set auto-load safe-path / .. _gdb_commands: 常用 GDB 命令 ============= 本节介绍嵌入式调试中最常用的 GDB 命令。完整的 GDB 命令参考请查阅 :ref:`gdb_references`。 基本调试命令 ------------ .. list-table:: :header-rows: 1 :widths: 30 70 * - 命令 - 说明 * - ``break main`` - 在 main 函数设置断点 * - ``break main.c:25`` - 在 main.c 第 25 行设置断点 * - ``info breakpoints`` - 查看所有断点 * - ``delete 1`` - 删除编号为 1 的断点 * - ``continue`` (``c``) - 继续执行 * - ``next`` (``n``) - 单步执行(不进入函数) * - ``step`` (``s``) - 单步执行(进入函数) * - ``finish`` - 执行完当前函数并返回 查看变量和内存 -------------- .. code-block:: text (gdb) print variable_name # 打印变量值(简写: p) (gdb) print /x variable_name # 以十六进制打印 (gdb) x/10xw 0x20000000 # 查看内存(10个字,十六进制) (gdb) display variable_name # 每次停止时自动显示变量 调用栈和寄存器 -------------- .. code-block:: text (gdb) backtrace # 显示调用栈(简写: bt) (gdb) info locals # 显示局部变量 (gdb) info registers # 显示所有寄存器 (gdb) print $pc # 打印程序计数器 实用命令 -------- .. code-block:: text (gdb) list # 查看源代码(简写: l) (gdb) info threads # 显示所有线程(RTOS 环境) (gdb) help # 显示帮助信息 (gdb) quit # 退出 GDB(简写: q) .. _gdb_examples: 调试示例 ======== 以调试 helloworld 示例为例,展示完整的调试流程: 1. **编译项目** .. code-block:: shell ./build.sh -C -S samples/helloworld -DBOARD=arcs_evb 2. **启动 J-Link GDB Server** 参考 :ref:`gdb_debugging` .. code-block:: shell JLinkGDBServerCLExe -device ARCS -if cJTAG -speed 4000 -port 2331 -jlinkscriptfile $HOME/.config/SEGGER/JLinkDevices/scripts/arcs/jtagscan1.JLinkScript 3. **启动 GDB 并调试** .. code-block:: shell ${NUCLEI_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gdb build/helloworld 在 GDB 中执行: .. code-block:: text (gdb) target remote localhost:2331 (gdb) break main (gdb) continue (gdb) next # 单步执行 (gdb) print variable_name # 查看变量 (gdb) backtrace # 查看调用栈 (gdb) info registers # 查看寄存器 常见调试场景 ------------ **内存问题调试** .. code-block:: text (gdb) x/10xw $sp # 查看栈顶内存 (gdb) x/20i $pc # 查看当前指令 (gdb) watch *0x20001000 # 监视内存变化 **函数调用跟踪** .. code-block:: text (gdb) break func_name (gdb) backtrace # 查看调用栈 (gdb) info args # 查看函数参数 (gdb) finish # 执行完函数 .. _gdb_tips: 调试技巧 ======== 条件断点和监视点 ---------------- 条件断点只在满足条件时才停止,监视点可以监视变量或内存的变化: .. code-block:: text (gdb) break main.c:30 if counter > 100 # 条件断点 (gdb) watch variable_name # 监视变量变化 (gdb) watch *(int *)0x20000100 # 监视内存地址 查看汇编代码 ------------ 对于底层调试,可以查看汇编代码: .. code-block:: text (gdb) disassemble main # 反汇编 main 函数 (gdb) disassemble /m main # 混合显示源码和汇编 (gdb) stepi # 汇编级单步执行 .. _gdb_troubleshooting: 常见问题 ======== 1. **找不到调试符号** 问题现象: .. code-block:: text Reading symbols from build/helloworld... (No debugging symbols found) 解决方法: - 确保编译时包含调试信息(``-g`` 选项) - 检查是否误用了 ``.bin`` 文件而非 ``.elf`` 文件 2. **无法连接到 GDB Server** 问题现象: .. code-block:: text (gdb) target remote localhost:2331 localhost:2331: Connection refused. 解决方法: - 确认 J-Link GDB Server 已正确启动 - 检查端口号是否正确(默认 2331) - 检查防火墙设置 - 确认 J-Link 仿真器已正确连接到开发板 3. **断点无法命中** 可能原因: - 代码被优化掉(使用 ``-O0`` 编译选项禁用优化) - 断点位置不正确 - 程序未正确加载到目标设备 解决方法: .. code-block:: text (gdb) info breakpoints # 检查断点状态 (gdb) break *0x20000a34 # 使用绝对地址设置断点 4. **程序执行位置与源代码不对应** 解决方法: - 确保 ELF 文件与烧录的 BIN 文件版本一致 - 重新编译并烧录程序 - 检查是否有多个版本的源文件 5. **查看变量显示 optimized out** 问题原因: 编译优化导致变量被优化掉。 解决方法: - 使用 ``-O0`` 编译选项禁用优化(仅用于调试) - 在 CMakeLists.txt 中添加: .. code-block:: cmake add_compile_options(-O0 -g) .. _gdb_references: 参考资料 ======== GDB 文档 -------- - `GDB 官方文档 `_ - GDB 完整用户手册 - `GDB 快速参考卡片 `_ - 常用命令速查(PDF) RISC-V 相关 ----------- - `RISC-V GDB 使用指南 `_ - RISC-V 工具链文档 - `Nuclei RISC-V 工具链 `_ - Nuclei 官方工具链文档 J-Link 文档 ----------- - `J-Link / J-Trace 用户手册 `_ - J-Link 官方用户手册 - `J-Link GDB Server 文档 `_ - GDB Server 配置和使用 ARCS SDK 相关 ------------- - ARCS SDK 快速入门::ref:`getting_started`