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