GDB调试学习(以C++项目为例)
背景
之前在做csapp课程实验的时候用到了部分linux环境下gcc调试的命令,现在由于实习要进行cpp开发,所以更需要系统的学习一下调试,之所以选择gdb,主要是因为 gdb功能强大,可以调试ada, c, c++, asm, minimal, d, fortran, objective-c, go, java,pascal 多种语言。
这里还抛出一个问题:为什么我们在已有非常好用的IDE调试的情况下还要去学习gdb调试?gdb调试更适用于什么样的场景?这是我目前还没有得到答案的,欢迎大家讨论。
启动调试
gdb test.cc
真正启动gdb调试的就是上面这个命令,但是你可能会遇到以下几个问题:
① gdb调试的对象是 编译后的可执行文件 而不是.cc文件
如果你操作的对象不是可执行文件,会给出提示:not in executable format: File truncated;
在linux中,可以通过”ll“ 命令查看目录下所有文件的详细信息,可执行文件后面会带有*
没有生成可执行的文件一般都是 编译的问题 ,建议仔细查看编译的过程日志来分析,还有一个可能的原因是你 项目的可执行文件输出目录和读取目录不对应 ,可以检查下输出的路径
② 使用 GDB 调试某个可执行文件,该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等)
对于这种问题,一般会给出提示:no debugging symbols found,最常见的原因是 文件编译没有加 -g
解决方案: 对于文件的编译,需要在编译命令最后加上-g ,比如:
gcc main.cc -o main.exe -g
③ 检查文件是否能被调试的两种其他方法:
方法一: not stripped表示可以调试,stripped表示不可调试
file nlu_test
方法二: readelf查看段信息,如果不包含任何debug就是有问题的
readelf -S nlu_test | grep debug
运行调试
run <要读取的输入/参数>
PS:可以在run的时候带参数,也可以在gdb的时候就提前set args,这取决于项目需求和自己的习惯
调试已经在运行的程序:先根据进程名查找编号再针对编号进行调试
$ ps -ef|grep 进程名
$ gdb
(gdb) attach <上面查询到的进程编号>
断点设置
常用操作
info breakpoints # 查看已经设置的所有断点信息
b 9 # 根据行号设置断点
b test.cc:9 # 根据行号设置断点
b file_load # 根据函数名设置断点
tb 9 # 根据行号设置临时断点
tb test.cc:9 # 根据行号设置临时断点
watch a # 根据表达式(a)变化设置断点
disable # 禁用所有断点
disable bnum # 禁用标号为bnum的断点
enable # 启用所有断点
enable bnum # 启用标号为bnum的断点
enable delete bnum # 启动标号为bnum的断点,并且在此之后删除该断点
clear # 删除当前行所有breakpoints
clear function # 删除函数名为function处的断点
clear filename:function # 删除文件filename中函数function处的断点
clear lineNum # 删除行号为lineNum处的断点
clear filename:lineNum # 删除文件filename中行号为lineNum处的断点
delete # 删除所有breakpoints,watchpoints和catchpoints
delete bnum # 删除断点号为bnum的断点
跳过多次设置断点(适合循环)
ignore 1 30 # 跳过断点编号为1的断点30次
根据条件设置断点
b test.cc:9 if b == 0 # 如果b等于0就在第9行停住
根据规则设置断点
rbreak regex* # 对所有调用regex函数的地方都打上断点
需要注意的是,此处打断点只有一个标号,但是会有多个断点,*有点像是断点指针的意思
变量查看
p a # 查看变量a的值
p test.cc:a # 查看特定文件下的a的值(防止命令重复)
p *point # 打印指针指向的内容
p *point@10 # 如果打印的是一个数组,可以@后面跟长度
display a # 只要有断点,就会打印a的值
info display # 查看.....
info register # 查看寄存器的内容
按照特定格式打印变量
- x 按十六进制格式显示变量
- d 按十进制格式显示变量
- u 按十六进制格式显示无符号整型
- o 按八进制格式显示变量
- t 按二进制格式显示变量
- a 按十六进制格式显示变量
- c 按字符格式显示变量
- f 按浮点数格式显示变量
单步调试
n # 单步调试
n 2 # 2次单步调试
s # 单步进入——会进入到内层的函数中
finish # 如果没有函数源码,需要跳过该函数执行,可使用finish命令,继续后面的执行
c # 继续到下一个断点
u 29 # 直接断点到29行
skip function swap # 跳过所有的swap函数
info skip # 查看所有的skip
源码的查看
set listsize 20 # 设置源码一次列出的行数