使用 GDB 进行调试
1. 技术要求
为了能够顺利跟随示例进行操作,你需要确保具备以下条件:
- 基于 Linux 的主机系统,至少有 60GB 的可用磁盘空间。
- Buildroot 2020.02.9 LTS 版本。
- Yocto 3.1 (Dunfell) LTS 版本。
- 适用于 Linux 的 Etcher。
- MicroSD 卡读卡器和卡。
- USB 转 TTL 3.3V 串口线。
- 树莓派 4。
- 5V 3A USB - C 电源。
- 用于网络连接的以太网线和端口。
- BeagleBone Black。
- 5V 1A 直流电源。
如果你还未安装 Buildroot 2020.02.9 LTS 版本,可参考 Buildroot 用户手册的系统要求部分(https://buildroot.org/downloads/manual/manual.html),并按照相关说明在 Linux 主机上进行安装。同样,若未安装 Yocto 3.1 (Dunfell) LTS 版本,可参考 Yocto 项目快速构建指南的兼容 Linux 发行版和构建主机软件包部分(https://www.yoctoproject.org/docs/current/brief-yoctoprojectqs/brief-yoctoprojectqs.html),然后在 Linux 主机上安装。
本章的所有代码可在 GitHub 仓库的 Chapter19 文件夹中找到:https://github.com/PacktPublishing/Mastering-Embedded-Linux-Programming-Third-Edition。
2. GNU 调试器(GDB)
GDB 是一种用于编译型语言的源代码级调试器,主要用于 C 和 C++,不过也支持多种其他语言,如 Go 和 Objective - C。你可以阅读所使用的 GDB 版本的相关说明,以了解其对各种语言的支持现状。
GDB 的项目网站是 https://www.gnu.org/software/gdb/,该网站包含许多有用的信息,包括 GDB 用户手册《Debugging with GDB》。
GDB 自带命令行用户界面,有些人可能会觉得不太友好,但实际上,稍加练习就很容易上手。如果你不喜欢命令行界面,有很多 GDB 的前端用户界面可供选择,后续会介绍其中三种。
3. 调试前的准备
在调试代码之前,需要使用调试符号来编译要调试的代码。GCC 为此提供了两个选项:-g 和 -ggdb。后者添加了特定于 GDB 的调试信息,而前者则以适合目标操作系统的格式生成信息,因此是更具可移植性的选项。在我们的案例中,目标操作系统通常是 Linux,使用 -g 或 -ggdb 差别不大。这两个选项都允许指定调试信息的级别,从 0 到 3:
| 级别 | 说明 |
| ---- | ---- |
| 0 | 不生成任何调试信息,相当于省略 -g 或 -ggdb 开关。 |
| 1 | 生成最少的信息,但包含函数名和外部变量,足以生成回溯信息。 |
| 2 | 默认级别,包含局部变量和行号信息,可进行源代码级调试和单步执行代码。 |
| 3 | 包含额外信息,例如 GDB 能正确处理宏展开。 |
在大多数情况下,-g 就足够了。如果在单步执行代码时遇到问题,尤其是代码中包含宏,可使用 -g3 或 -ggdb3。
另一个需要考虑的问题是代码优化级别。编译器优化往往会破坏源代码行与机器代码之间的关系,导致单步执行源代码时出现不可预测的情况。如果遇到此类问题,可能需要在不进行优化的情况下编译代码,即省略 -O 编译开关,或使用 -Og,它允许进行不影响调试的优化。
相关的问题是栈帧指针,GDB 需要它来生成函数调用的回溯信息。在某些架构上,GCC 在较高的优化级别(-O2 及以上)下不会生成栈帧指针。如果你必须使用 -O2 进行编译,但仍希望生成回溯信息,可以使用 -fno - omit - frame - pointer 来覆盖默认行为。同时,要留意那些通过添加 -fomit - frame - pointer 手动优化以省略帧指针的代码,可能需要暂时移除这些部分。
4. 调试应用程序
使用 GDB 调试应用程序有两种方式:
- 如果你正在开发在桌面和服务器上运行的代码,或者在编译和运行代码的同一台机器上进行开发,自然可以本地运行 GDB。
- 大多数嵌入式开发使用交叉工具链,因此你希望调试在设备上运行的代码,但从拥有源代码和工具的交叉开发环境进行控制。这里将重点介绍后一种情况。
5. 使用 gdbserver 进行远程调试
远程调试的关键组件是调试代理 gdbserver,它运行在目标设备上,控制被调试程序的执行。gdbserver 通过网络连接或串口接口与运行在主机上的 GDB 副本进行通信。
通过 gdbserver 进行调试与本地调试几乎相同,但由于涉及两台计算机,调试时需要确保它们处于正确的状态。以下是一些需要注意的事项:
- 调试会话开始时,需要使用 gdbserver 在目标设备上加载要调试的程序,然后在主机上单独加载交叉工具链中的 GDB。
- GDB 和 gdbserver 需要相互连接后才能开始调试会话。
- 运行在主机上的 GDB 需要知道在哪里查找调试符号和源代码,特别是对于共享库。
- GDB 的 run 命令可能无法按预期工作。
- 调试会话结束时,gdbserver 会终止,如果需要再次进行调试会话,需要重新启动它。
- 你需要在主机上拥有要调试的二进制文件的调试符号和源代码,但目标设备上不需要。通常,目标设备上没有足够的存储空间来存储这些信息,因此在部署到目标设备之前需要剥离它们。
- GDB/gdbserver 组合并不支持本地运行 GDB 的所有功能,例如,gdbserver 无法跟踪 fork 后的子进程,而本地 GDB 可以。
- 如果 GDB 和 gdbserver 来自不同版本的 GDB,或者虽然版本相同但配置不同,可能会出现奇怪的问题。理想情况下,应该使用你喜欢的构建工具从相同的源代码构建它们。
调试符号会显著增加可执行文件的大小,有时会增加 10 倍。可以使用交叉工具链中 binutils 包的 strip 工具在不重新编译所有代码的情况下移除调试符号。可以使用以下开关控制 strip 级别:
- –strip - all:移除所有符号(默认)。
- –strip - unneeded:移除重定位处理不需要的符号。
- –strip - debug:仅移除调试符号。
需要注意的是,对于应用程序和共享库,使用 –strip - all(默认)即可,但对于内核模块,使用 –strip - all 会导致模块无法加载,应使用 –strip - unneeded。
6. 为远程调试设置 Yocto 项目
使用 Yocto 项目进行远程调试应用程序时,需要完成两件事:
- 将 gdbserver 添加到目标镜像中。可以通过在 conf/local.conf 中添加以下内容来显式添加该包:
IMAGE_INSTALL_append = " gdbserver"
如果没有串口控制台,还需要添加 SSH 守护进程,以便在目标设备上启动 gdbserver:
EXTRA_IMAGE_FEATURES ?= "ssh - server - openssh"
或者,你可以将 tools - debug 添加到 EXTRA_IMAGE_FEATURES 中,这将向目标镜像中添加 gdbserver、本地 gdb 和 strace:
EXTRA_IMAGE_FEATURES ?= "tools - debug ssh - server - openssh"
- 创建一个包含 GDB 并具有要调试的可执行文件的调试符号的 SDK。只需按照相关说明构建 SDK 即可:
$ bitbake -c populate_sdk <image>
SDK 包含 GDB 副本、目标设备的 sysroot(包含目标镜像中所有程序和库的调试符号)以及可执行文件的源代码。例如,为树莓派 4 构建的 SDK 安装在 /opt/poky/3.1.5/,目标设备的 sysroot 位于 /opt/poky/3.1.5/sysroots/aarch64 - poky - linux/。程序位于 /bin/、/sbin/、/usr/bin/ 和 /usr/sbin/ 中,库位于 /lib/ 和 /usr/lib/ 中。每个目录下都有一个名为 .debug/ 的子目录,包含每个程序和库的符号。GDB 在搜索符号信息时会自动查找 .debug/ 目录。可执行文件的源代码存储在 /usr/src/debug/ 中。
7. 为远程调试设置 Buildroot
Buildroot 不区分构建环境和应用程序开发环境,没有 SDK。假设你使用 Buildroot 内部工具链,需要启用以下选项来为主机构建交叉 GDB 并为目标设备构建 gdbserver:
- 在 Toolchain | Build cross gdb for the host 中启用 BR2_PACKAGE_HOST_GDB。
- 在 Target packages | Debugging, profiling and benchmark | gdb 中启用 BR2_PACKAGE_GDB。
- 在 Target packages | Debugging, profiling and benchmark | gdbserver 中启用 BR2_PACKAGE_GDB_SERVER。
此外,还需要启用 BR2_ENABLE_DEBUG 来构建带有调试符号的可执行文件,这将在 output/host/usr/ /sysroot 中创建带有调试符号的库。
8. 开始调试
现在你已经在目标设备上安装了 gdbserver,并在主机上安装了交叉 GDB,可以开始调试会话了。
8.1 连接 GDB 和 gdbserver
GDB 和 gdbserver 之间的连接可以通过网络或串口接口实现。
网络连接
:
在目标设备上,启动 gdbserver 并指定要监听的 TCP 端口号,可选地指定允许连接的 IP 地址。在大多数情况下,你可能不关心哪个 IP 地址连接,因此只需提供端口号即可。例如,让 gdbserver 在端口 10000 上等待任何主机的连接:
# gdbserver :10000 ./hello - world
Process hello - world created; pid = 103
Listening on port 10000
在主机上,启动交叉工具链中的 GDB 副本,并指向未剥离符号的程序副本,以便 GDB 加载符号表:
$ aarch64 - poky - linux - gdb hello - world
在 GDB 中,使用 target remote 命令连接到 gdbserver,指定目标设备的 IP 地址或主机名以及它正在等待的端口:
(gdb) target remote 192.168.1.101:10000
当 gdbserver 看到来自主机的连接时,会输出以下信息:
Remote debugging from host 192.168.1.1
串口连接
:
在目标设备上,告诉 gdbserver 使用哪个串口:
# gdbserver /dev/ttyO0 ./hello - world
你可能需要事先使用 stty(1) 或类似程序配置端口波特率,例如:
# stty -F /dev/ttyO0 115200
在主机上,使用 target remote 命令加上串口设备连接到 gdbserver。大多数情况下,需要先使用 GDB 命令 set serial baud 设置主机串口的波特率:
(gdb) set serial baud 115200
(gdb) target remote /dev/ttyUSB0
8.2 设置 sysroot
GDB 需要知道在哪里查找要调试的程序和共享库的调试信息和源代码。本地调试时,路径是已知的且内置于 GDB 中,但使用交叉工具链时,GDB 无法猜测目标文件系统的根目录,因此需要提供这些信息。
如果你使用 Yocto 项目 SDK 构建应用程序,可以在 GDB 中设置 sysroot 如下:
(gdb) set sysroot /opt/poky/3.1.5/sysroots/aarch64 - poky - linux
如果你使用 Buildroot,sysroot 位于 output/host/usr/ /sysroot,并且在 output/staging 中有一个符号链接指向它。因此,对于 Buildroot,可在 GDB 中设置 sysroot 如下:
(gdb) set sysroot /home/chris/buildroot/output/staging
GDB 还需要找到要调试的文件的源代码。可以使用 show directories 命令查看 GDB 的源代码搜索路径:
(gdb) show directories
Source directories searched: $cdir:$cwd
默认情况下,$cwd 是主机上运行的 GDB 实例的当前工作目录,$cdir 是源代码编译的目录,该信息编码在目标文件中,带有标签 DW_AT_comp_dir。可以使用 objdump –dwarf 查看这些标签,例如:
$ aarch64 - poky - linux - objdump --dwarf ./helloworld | grep DW_AT_comp_dir
在大多数情况下,默认的 $cdir 和 $cwd 就足够了,但如果在编译和调试之间移动了目录,可能会出现问题。例如,使用 Yocto 项目 SDK 编译的程序,查看 DW_AT_comp_dir 标签时可能会发现多个对 /usr/src/debug/glibc/2.31 - r0/git 目录的引用,实际上它位于 SDK 的 sysroot 中,完整路径是 /opt/poky/3.1.5/sysroots/aarch64 - poky - linux/usr/src/debug/glibc/2.31 - r0/git。GDB 可以使用 substitute - path 命令处理整个目录树的移动问题。因此,在使用 Yocto 项目 SDK 调试时,需要使用以下命令:
(gdb) set sysroot /opt/poky/3.1.5/sysroots/aarch64 - poky - linux
(gdb) set substitute path /usr/src/debug/opt/poky/3.1.5/sysroots/aarch64 - poky - linux/usr/src/debug
如果你有存储在 sysroot 之外的额外共享库,可以使用 set solib - search - path 命令,该命令可以包含一个用冒号分隔的目录列表,用于搜索共享库。GDB 只有在 sysroot 中找不到二进制文件时才会搜索 solib - search - path。
还可以使用 directory 命令告诉 GDB 在哪里查找源代码,例如:
(gdb) directory /home/chris/MELP/src/lib_mylib
Source directories searched: /home/chris/MELP/src/lib_mylib:$cdir:$cwd
通过这种方式添加的路径具有更高的优先级,因为它们会在搜索 sysroot 或 solib - search - path 之前被搜索。
8.3 GDB 命令文件
每次运行 GDB 时,有些操作是必须要做的,例如设置 sysroot。将这些命令放在一个命令文件中,每次启动 GDB 时运行它们会很方便。GDB 会依次从 $HOME/.gdbinit、当前目录下的 .gdbinit 以及命令行中使用 -x 参数指定的文件中读取命令。但由于安全原因,最新版本的 GDB 会拒绝加载当前目录下的 .gdbinit。可以在 $HOME/.gdbinit 中添加以下内容来覆盖此行为:
set auto - load safe - path /
或者,如果你不想全局启用自动加载,可以指定特定的目录:
add - auto - load safe - path /home/chris/myprog
个人建议使用 -x 参数指向命令文件,这样可以明确文件的位置,避免遗忘。
Buildroot 会在 output/staging/usr/share/buildroot/gdbinit 中创建一个包含正确 sysroot 命令的 GDB 命令文件,例如:
set sysroot /home/chris/buildroot/output/host/usr/aarch64 - buildroot - linux - gnu/sysroot
8.4 GDB 常用命令概述
GDB 有许多命令,在线手册和相关资源中有详细描述。为了帮助你尽快上手,以下是一些最常用命令的列表:
| 分类 | 命令 | 说明 |
| ---- | ---- | ---- |
| 断点管理 | break | 设置断点 |
| | delete | 删除断点 |
| | disable | 禁用断点 |
| | enable | 启用断点 |
| 程序执行控制 | run | 开始执行程序 |
| | next | 单步执行下一行代码(不进入函数) |
| | step | 单步执行下一行代码(进入函数) |
| | continue | 继续执行程序直到下一个断点 |
| 获取调试信息 | info breakpoints | 显示断点信息 |
| | info registers | 显示寄存器信息 |
| | backtrace | 显示函数调用栈回溯信息 |
在开始单步执行程序之前,需要设置一个初始断点。
8.5 运行到断点
gdbserver 将程序加载到内存中,并在第一条指令处设置断点,然后等待 GDB 的连接。连接建立后,进入调试会话。但如果你立即尝试单步执行,会收到以下消息:
Cannot find bounds of current function
这是因为程序在汇编代码中暂停,汇编代码为 C/C++ 程序创建运行时环境。C/C++ 代码的第一行是 main() 函数。如果你想在 main() 函数处停止,可以在那里设置断点,然后使用 continue 命令(缩写为 c)让 gdbserver 从程序开始处的断点继续执行,直到 main() 函数处停止:
(gdb) break main
Breakpoint 1, main (argc = 1, argv = 0xbefffe24) at helloworld.c:8
printf("Hello, world!\n");
(gdb) c
此时,你可能会看到以下信息:
Reading /lib/ld - linux.so.3 from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
在较旧版本的 GDB 中,可能会看到:
warning: Could not load shared library symbols for 2 libraries, e.g. /lib/libc.so.6.
这两种情况都表明你忘记设置 sysroot 了,需要回顾前面关于 sysroot 的部分。
这与本地启动程序不同,本地启动只需输入 run 命令。在远程调试会话中尝试输入 run 命令时,你可能会看到消息提示远程目标不支持 run 命令,或者在较旧版本的 GDB 中,程序会无响应。
9. 使用 Python 扩展 GDB
可以将完整的 Python 解释器嵌入到 GDB 中以扩展其功能。在构建 GDB 之前,使用 –with - python 选项进行配置即可实现。GDB 有一个 API,它将其内部状态的大部分作为 Python 对象暴露出来。通过这个 API,我们可以用 Python 脚本定义自己的自定义 GDB 命令,这些额外的命令可能包括有用的调试辅助工具,如跟踪点和漂亮的打印机,这些在 GDB 中并非内置功能。
9.1 构建支持 Python 的 GDB
在已经完成远程调试的 Buildroot 设置基础上,要启用 GDB 对 Python 的支持,还需要一些额外的步骤。目前,Buildroot 仅支持在 GDB 中嵌入 Python 2.7,虽然不太理想,但总比完全不支持 Python 要好。由于 Buildroot 生成的工具链缺少一些必要的线程支持,因此不能使用它来构建支持 Python 的 GDB。
要为主机构建支持 Python 的交叉 GDB,请按照以下步骤操作:
1. 导航到安装 Buildroot 的目录:
$ cd buildroot
- 复制要构建镜像的开发板的配置文件:
$ cd configs
$ cp raspberrypi4_64_defconfig rpi4_64_gdb_defconfig
$ cd ..
- 清理输出目录中的先前构建工件:
$ make clean
- 激活配置文件:
$ make rpi4_64_gdb_defconfig
- 开始自定义镜像:
$ make menuconfig
- 导航到 Toolchain | Toolchain type | External toolchain,选择使用外部工具链:
在 menuconfig 中选择 External toolchain
- 退出 External toolchain 菜单,打开 Toolchain 子菜单,选择一个已知可用的工具链,如 Linaro AArch64 2018.05 作为外部工具链:
在 Toolchain 子菜单中选择 Linaro AArch64 2018.05
- 在 Toolchain 页面中选择 Build cross gdb for the host,并启用 TUI 支持和 Python 支持:
在 Toolchain 页面勾选 Build cross gdb for the host,同时启用 TUI 和 Python 支持
- 深入到 Toolchain 页面的 GDB debugger Version 子菜单,选择 Buildroot 中可用的最新版本的 GDB:
在 GDB debugger Version 子菜单中选择最新版本的 GDB
- 退出 Toolchain 页面,深入到 Build options,选择 build packages with debugging symbols:
在 Build options 中选择 build packages with debugging symbols
- 退出 Build options 页面,深入到 System Configuration,选择 Enable root login with password,打开 Root password 并在文本字段中输入非空密码:
在 System Configuration 中选择 Enable root login with password,设置密码
- 退出 System Configuration 页面,深入到 Target packages | Debugging, profiling and benchmark,选择 gdb 包以将 gdbserver 添加到目标镜像中:
在 Target packages 的 Debugging 部分选择 gdb 包
- 退出 Debugging, profiling and benchmark 菜单,深入到 Target packages | Networking applications,选择 dropbear 包以启用对目标设备的 scp 和 ssh 访问。注意,dropbear 在没有密码的情况下不允许 root 用户进行 scp 和 ssh 访问:
在 Networking applications 中选择 dropbear 包
- 在 Target packages | Miscellaneous 中添加 haveged 熵守护进程,以便在启动时更快地启用 SSH:
在 Miscellaneous 中选择 haveged
- 向镜像中添加另一个包,以便有东西可以调试。选择 bsdiff 二进制补丁/差异工具,它用 C 语言编写,可在 Target packages | Development tools 中找到:
在 Development tools 中选择 bsdiff
- 保存更改并退出 Buildroot 的 menuconfig:
在 menuconfig 中保存并退出
- 保存配置文件的更改:
$ make savedefconfig
- 为目标设备构建镜像:
$ make
如果想跳过上述 menuconfig 步骤,可以在本章的代码存档中找到适用于树莓派 4 的现成 rpi4_64_gdb_defconfig 文件。将该文件从 MELP/Chapter19/buildroot/configs/ 复制到你的 buildroot/configs 目录,然后运行 make 即可。
构建完成后,在 output/images/ 中应该会有一个可引导的 sdcard.img 文件,你可以使用 Etcher 将其写入 MicroSD 卡。将该 MicroSD 卡插入目标设备并启动它。使用以太网线将目标设备连接到本地网络,使用 arp - scan 查找其 IP 地址。以 root 用户身份通过 SSH 登录到设备,并输入在配置镜像时设置的密码。例如,在 rpi4_64_gdb_defconfig 镜像中设置的 root 密码为 temppwd。
9.2 使用 GDB 远程调试 bsdiff
现在可以使用 GDB 远程调试 bsdiff 了,步骤如下:
1. 在目标设备上导航到 /usr/bin 目录:
# cd /usr/bin
- 像之前调试 hello - world 一样,使用 gdbserver 启动 bsdiff:
# gdbserver :10000 ./bsdiff pcregrep pcretest out
Process ./bsdiff created; pid = 169
Listening on port 10000
- 在主机上,导航到 output/build/bsdiff - 4.3 目录,启动交叉工具链中的 GDB 副本,并指向未剥离符号的 bsdiff 程序副本,以便 GDB 加载符号表:
$ cd output/build/bsdiff - 4.3
$ ~/buildroot/output/host/bin/aarch64 - linux - gdb bsdiff
- 在 GDB 中设置 sysroot:
(gdb) set sysroot ~/buildroot/output/staging
- 使用 target remote 命令连接到 gdbserver,指定目标设备的 IP 地址或主机名以及它正在等待的端口:
(gdb) target remote 192.168.1.101:10000
通过以上步骤,你就可以使用 GDB 对 bsdiff 进行远程调试了。在实际调试过程中,你可以结合前面介绍的 GDB 常用命令,如设置断点、单步执行、查看变量值等,来定位和解决代码中的问题。同时,利用 Python 扩展 GDB 的功能,可以根据具体需求编写自定义的调试命令,提高调试效率。希望这些内容能帮助你更好地使用 GDB 进行调试工作。
使用 GDB 进行调试
10. 调试流程总结
为了更清晰地展示使用 GDB 进行调试的整个流程,下面给出一个 mermaid 格式的流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{选择调试方式}:::decision
B -->|本地调试| C(本地运行 GDB):::process
B -->|远程调试| D(设置目标环境):::process
D --> E(添加 gdbserver 到目标镜像):::process
E --> F(创建包含 GDB 和调试符号的 SDK):::process
F --> G(连接 GDB 和 gdbserver):::process
G --> H(设置 sysroot):::process
H --> I(设置初始断点):::process
I --> J(运行到断点):::process
J --> K(单步执行或继续执行):::process
C --> K
K --> L([结束]):::startend
这个流程图展示了使用 GDB 进行调试的主要步骤,无论是本地调试还是远程调试,都有清晰的流程指引。
11. 调试过程中的常见问题及解决方法
在使用 GDB 进行调试的过程中,可能会遇到一些常见问题,下面以表格的形式列出这些问题及相应的解决方法:
| 问题描述 | 可能原因 | 解决方法 |
| ---- | ---- | ---- |
| 单步执行时提示 “Cannot find bounds of current function” | 程序在汇编代码中暂停,汇编代码为 C/C++ 程序创建运行时环境 | 设置初始断点在 main() 函数处,使用 continue 命令继续执行到 main() 函数 |
| 提示 “Could not load shared library symbols” | 忘记设置 sysroot | 在 GDB 中使用 set sysroot 命令设置正确的 sysroot |
| GDB 的 run 命令无响应或提示不支持 | 远程调试时 run 命令可能无法按预期工作 | 使用 continue 命令代替 run 命令 |
| 栈帧指针问题导致无法生成回溯信息 | 在某些架构上,GCC 在较高的优化级别(-O2 及以上)下不会生成栈帧指针 | 使用 -fno - omit - frame - pointer 覆盖默认行为,或暂时移除手动优化以省略帧指针的代码 |
12. 高级调试技巧
除了前面介绍的基本调试方法,还有一些高级调试技巧可以帮助你更高效地进行调试。
12.1 条件断点
在某些情况下,你可能只希望在满足特定条件时才触发断点。可以使用条件断点来实现这一需求。例如,在 GDB 中设置一个条件断点,当变量 x 的值等于 10 时触发:
(gdb) break my_function if x == 10
12.2 观察点
观察点用于监视变量的变化。当变量的值发生改变时,程序会暂停执行。例如,监视变量 y 的变化:
(gdb) watch y
12.3 信号处理
在调试过程中,程序可能会收到各种信号,如 SIGSEGV(段错误)。可以使用 GDB 来处理这些信号,例如,在收到 SIGSEGV 信号时暂停程序:
(gdb) handle SIGSEGV stop
13. 总结
使用 GDB 进行调试是一项强大的技能,无论是在本地开发环境还是远程嵌入式开发环境中,都能帮助你快速定位和解决代码中的问题。通过本文的介绍,你了解了调试前的准备工作,包括编译代码时添加调试符号和选择合适的优化级别;掌握了使用 GDB 调试应用程序的两种方式,特别是远程调试的详细步骤,包括设置 Yocto 项目和 Buildroot 环境、连接 GDB 和 gdbserver、设置 sysroot 等;还学习了一些常用的 GDB 命令和高级调试技巧。
在实际应用中,你可以根据具体的调试需求选择合适的方法和技巧。同时,要注意调试过程中的一些常见问题,并根据本文提供的解决方法进行处理。希望这些内容能帮助你更好地使用 GDB 进行调试工作,提高开发效率和代码质量。
最后,再次强调调试符号的重要性,它是进行源代码级调试的基础。在编译代码时,合理选择调试符号的级别,可以在不影响程序性能的前提下,为调试提供足够的信息。同时,对于不同类型的文件(如应用程序、共享库和内核模块),要选择合适的 strip 级别来移除调试符号,以节省存储空间。
通过不断实践和积累经验,你将能够熟练掌握 GDB 的使用,成为一名高效的调试高手。
超级会员免费看
69

被折叠的 条评论
为什么被折叠?



