gdb调试信息分离
Table of Contents
调试信息分离
GDB的调试信息分离机制
GDB 允许程序的调试信息和可执行文件分离。当可执行程序出现问题时,GDB 会自动查找和加载调试信息。
- 这种机制的实现原理是,GDB 通过两种方式来指定独立调试信息文件:
- 一种是可执行文件包含一个调试链接,它会指定独立调试信息文件的名称;
- 另一种是可执行文件包含一个构建标识符,它是一个唯一的位串,在相应的调试信息文件中也包含这个位串?
调试链接详解
- 调试链接包含两个关键信息:
- 调试信息文件的名称和 CRC 校验和。 调试信息文件的名称通常为executable.debug,其中executable是相应的可执行文件的名称,不带有前导的目录名,方便查找。 CRC 校验和的目的是为了验证可执行程序和调试文件是否匹配。
- 在生成调试链接时,会将调试信息文件的名称和 CRC 校验和添加到可执行文件中
- 当 GDB 调试时,会根据这些信息来查找和加载调试信息。
- 例如,在编译生成release with debug info时,它包含调试信息,然后把它进行调试信息分离,生成一个可执行程序(用a代替)和调试信息文件(用b代替)。
- 接下来把调试链接做出来,就是在可执行程序a当中,加上一个链接,这个链接信息包含调试信息文件b的名字和 CRC 校验和 。
以 Redis 为例的调试流程
编译带调试信息的可执行文件
在编译 Redis 时,我们可以使用-O2或-O3优化选项来优化代码,提高运行效率,同时使用-g选项添加调试信息,-DENDEBUG用于禁用断言(在 Release 版本中通常会禁用断言以提高性能)。
make CFLAGS="-O2 -g -DENDEBUG"
这样就会生成一个包含调试信息的 Redis 可执行程序,虽然文件大小可能会有所增加,但为后续的调试提供了便利。
转存调试信息
我们使用objcopy工具将调试信息转存到一个新的文件中。命令如下:
objcopy --only-keep-debug ./src/redis-server redis-server.debug
这条命令会把./src/redis-server中的调试信息提取出来,保存到redis-server.debug文件中。
此时,redis-server.debug装满调试信息,里面包含了变量名、函数名、行号等重要信息。
去除可执行程序调试信息
为了得到一个 “干净” 的、不包含调试信息的可执行程序,我们使用strip命令:
strip ./src/redis-server -o redis-server
经过这一步,redis-server就变成了一个体积更小、运行效率更高的裸可执行程序, 但缺少了调试所需的信息。
添加调试链接
最后,我们要为这个裸可执行程序添加调试链接,让它能够找到对应的调试信息文件。使用objcopy命令:
objcopy --add-gnu-debuglink=redis-server.debug redis-server
这条命令会在redis-server中添加一个调试链接,链接到redis-server.debug文件。
此时,redis-server虽然不包含调试信息,但它知道从哪里获取调试信息,为后续的调试做好了准备。
用 GDB 调试
现在,我们就可以使用 GDB 来调试这个带有调试链接的可执行程序了。
启动 GDB 并加载redis-server:
gdb redis-server
GDB 会自动根据调试链接找到redis-server.debug文件,并加载其中的调试信息。
在 GDB 中,我们可以使用各种调试命令,如设置断点(break)、查看变量值(print)、单步执行(step)等,从而定位和解决程序中的问题。例如,我们可以在某个函数处设置断点,查看程序执行到该点时变量的值,分析程序的执行逻辑是否正确。