gdb 显示具体代码
若想 gdb 调试时可以显示代码,需满足以下两个条件:
- 二进制文件中带有调试信息
- 机器上有源代码
1. 二进制文件带有调试信息
编译代码时,使用 -g
或 -gz
标志,用于生成调试信息。
注:-gz
表示压缩调试信息。
查看一个二进制文件是否带有调试信息:
1 | objdump -S /path/to/bin |
若输出中含有 .debug_xxx
段或 .zdebug_xxx
段,则表示该二进制文件含调试信息。
2. 机器上有源代码
二进制文件中有调试信息后,gdb 就知道了 xxx 地址的代码对应于 /path/of/code/xxx
文件中的 yy 行,然后 gdb 就会根据路径去机器上查看这里的代码。这也就意味着,使用 gdb 调试的机器上必须要有源代码!
一般情况下,将代码上传线上服务肯定是不现实的,因此我们一般是将 core 文件下载到开发机上进行调试。请注意,要保证本地使用的二进制程序的源代码和 core 文件使用的二进制的源代码是相同的版本!
我们可以使用在 gdb 中使用 dir 设置代码查找路径(当调试信息中的文件路径为相对地址时)。比如 gdb 显示代码路径为 cika/cika_server/logic/xxx.cc
,我们源代码的根路径为 /data/code/main
,使用 dir /data/code/main
就可以为 gdb 搜索代码时指明去该路径下查找代码。
调试的原理
使用 -g
标志进行编译后,编译器 / 链接器会在生成的二进制文件上加入 .debug_xxx
段,这里面便存在着相关的调试信息。
调试信息一般以 DWARF(Debugging With Attributed Record Formats)
格式存储。
DWARF 基本结构
DWARF 调试信息通常包含以下几部分:
- 编译单元(Compilation Unit, CU):每个编译单元对应一个源文件及其包含的所有调试信息。
- 调试信息条目(Debugging Information Entry, DIE):每个 DIE 描述一个调试信息实体,如变量、函数、类型等。
- 属性(Attributes):每个 DIE 包含若干属性,描述该实体的详细信息,如名称、类型、地址等。
- 行号信息(Line Number Information):记录源代码行号与机器代码地址的对应关系。
简单的 DWARF 例子
假设我们有一个简单的 C++ 程序 example.cpp:
1 |
|
编译这个程序并生成调试信息:
1 | g++ -g -o example example.cpp |
我们可以使用 readelf 工具查看生成的 DWARF 调试信息:
1 | readelf --debug-dump=info example |
输出可能会非常详细,但我们可以简化并解释其中的一部分。假设输出的一部分如下:
1 | <1><0x0000000b> DW_TAG_compile_unit |
解释
- 编译单元(CU):
- <1><0x0000000b> DW_TAG_compile_unit:表示一个编译单元。
- DW_AT_producer:编译器信息。
- DW_AT_language:编程语言。
- DW_AT_name:源文件名。
- DW_AT_stmt_list:行号信息的偏移。
- DW_AT_comp_dir:编译目录。
- DW_AT_low_pc 和 DW_AT_high_pc:该编译单元的代码地址范围。
- 子程序(Subprogram):
- <2><0x0000001b> DW_TAG_subprogram:表示一个子程序(函数)。
- DW_AT_name:函数名。
- DW_AT_decl_file:声明该函数的源文件索引。
- DW_AT_decl_line:声明该函数的源代码行号。
- DW_AT_prototyped:是否有原型。
- DW_AT_low_pc 和 DW_AT_high_pc:该函数的代码地址范围。
行号信息
行号信息记录了源代码行号与机器代码地址的对应关系。可以使用 readelf –debug-dump=decodedline example 查看行号信息:
1 | readelf --debug-dump=decodedline example |
输出可能如下:
1 | CU: example.cpp: |
扩展
GNU binutils (GNU 二进制工具集)
GNU binutilds:https://www.gnu.org/software/binutils/
常用的:
- ld
- as
- gold
- c++filt
- nm
- addr2line
- objdump
- objcopy
- size
- strings
- strip
- gprof
线上的 C++ 服务 coredump 了,其使用的二进制没有带 debug 调试信息,我可以用本地相同代码编译出来的带 debug 信息的二进制文件来调试吗?
可以。
注意事项:
确保代码一致性:确保本地编译的代码与线上运行的代码完全一致,包括源代码、编译选项、依赖库等。
地址空间布局随机化(ASLR):ASLR 可能会影响调试过程。如果遇到问题,可以在调试时临时禁用 ASLR:
1
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
确保 core dump 文件和本地二进制文件的内存地址匹配。 如果不匹配,可能需要使用
set solib-absolute-prefix
或set solib-search-path
等 gdb 命令来调整。