gdb 入门
简介
gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具。
一般来说,GDB主要帮助你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。
gdb可以调试的对象
gdb 可以调试的语言
Ada, Assembly, C, C++, D, Fortran, Go, Objective-C, OpenCL, Modula-2, Pascal, Rust
gdb 可以调试的文件类型
注意在编译时需要加上-g
选项。gdb可以调试的文件类型有三种:
-
二进制文件。
gdb a.out
-
core文件。通常,我们在遇到错误时得到一个core文件(如果没有,请通过
ulimit -a
命令查看core file siez
是否为0,若为0,通过ulimit -c unlimited
改为unlimited
即可)gdb a.out core.12345
-
运行中的进程,其中
[1234]
时要调试的进程号gdb -p 1234
gdb的常用命令(入门)
本小节仅介绍入门级的常用命令,稍微进阶的用法会慢慢补全。
以下索引表解释常用命令基本的含义,有些细节会在下一小节具体说。
命令(简写) | 含义 |
---|---|
help (h) | 查看命令帮助 |
run (r) | 重新开始运行文件 |
run argv[1] argv[2] | 调试时命令行传参 |
start | 单步执行,运行程序,停在第一执行语句 |
list (l) | 查看源代码 |
set | 设置变量的值 |
next (n) | 单步调试(逐过程,函数直接执行) |
step (s) | 单步调试(逐语句,跳入自定义函数内部执行) |
braktrace (bt) | 查看函数的调用的栈帧和层级关系 |
frame (f) | 切换函数的栈桢 |
info (i) | 查看函数内部局部变量的值 |
finish | 结束当前函数,返回到调用点 |
continue (c) | 继续运行(至下一个断点或程序结束) |
print (p) | 打印值 |
quit (q) | 退出gdb |
break (b) | 设置断点 |
info breakpoints(i break) | 查看当前设置的所有断点 |
enable/disable breakpoints | 启用 / 禁用断点 |
delete num(d num) | 删除第num个断点 |
display | 追踪查看具体的变量值 |
undisplay | 取消追踪观察变量 |
watch | 被设置观察点的变量发生修改时,打印显示 |
info watchpoints(i watch) | 显示观察点 |
x | 查看内存x/20xw 显示20个单元,16进制,4字节每单元 |
几个常用的命令详解
list
list
可简写为l
,可以列出所调试程序的代码
list linenumber
:列出linenumber附近的代码list function
:列出某个函数附近的源代码
break
gdb调试时使用break命令来设置断点,简写为b,有如下几种下断点地方法:
-
break function
:在进入指定的函数function处打断点,C++中可以使用class::function或function(type, type)格式来指定函数名称break filename:function
:在指定文件的指定函数处打断点 -
break linenumber
:在指定的行数打断点break filename:linenumber
:在指定文件的指定行数打断点 -
break +/- offeset
:在当前行的前面或后面打断点 -
break *address
:在程序运行的指定地址出打断点 -
break
:在下一条命令处停止运行 -
break .. if condition
:在在处理某些循环体中可使用此方法进行调试,其中…可以是上述的break lineNumber
、break +/-offset
中的参数,其中condition表示条件,在条件成立时程
序即停止运行,如设置break if i=100
表示当i为100时程序停止运行。
查看断点时,也可以使用info
命令如info breakpoints [n]、info break [n]
其中n
表示断点号来查看断点信息。
可以通过delete
命令删除所有的断点
next
使用next命令单步执行程序代码,next的单步不会进入函数的内部,与next对应的step命令则在单步执行一个函数时进入函数内部,类似于VC++中的step into,其用法为next count
,单步跟踪,如果有函数调用不会进入函数,如果后面不加count表示一条一条的执行,加count表示执行后面的count条指令。
continue
continue
:当程序遇到断点停下来之后,可以执行continue继续执行到下一个断点或到程序结束。
简写为p,可以通过print命令查看参数或程序运行数据
值得注意的是print输出可以指定显示变量的输出格式:
符号 | 输出格式 |
---|---|
x | 十六进制 |
d | 十进制 |
u | 十六进制无符号数 |
o | 八进制 |
t | 二进制 |
c | 字符格式 |
f | 浮点数格式 |
print可以输出东西可多:全局变量,静态全局变量,局部变量,如果你的局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量。
-
全局变量利用
::
,例如在1.c中看x:(gdb)p "1.c"::x
-
数组(动):
p *array@len
array:数组的首地址,len:数据的长度 -
数组(静):直接p数组名
-
所有寄存器的值:
info registers
-
查看指定的寄存器的值:
p $eip
-
结构体。如果你想很漂亮的输出结构体请设置
set print pretty on
,打开print pretty这个东西,没错,输出很漂亮滴。
disassemble
用disassemble function
来查看汇编代码,如下图示某个main函数的反汇编代码:
我们可以通过反汇编代码的偏移量来在汇编代码中打上断点,比如我们想要在图中红色箭头处打断点,只需b *main+45
。
我们可以通过disassemble /m function
指令,来将C/C++源代码和其反汇编一起显示,这样会更加直观:
backtrace
可以简写为bt,功能为显示函数的栈。
当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入“栈”(Stack)中。你可以用这条命令来查看当前的栈中的所有信息。
在递归时可以很方便地查看栈上各个递归函数的栈帧:
图中的Delete函数会被递归调用,功能时删除二叉搜索树种的某个节点,具体功能不重要,这里是为了展示递归函数多层递归时通过backtrace
来查看栈帧信息。
显示栈顶的几个层的信息:bt n
显示栈底下的几层信息:bt -n
但是,如果要查看某一层的信息,你需要在切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。这就要用到下面的frame
命令。
frame
可以简写为f,n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,即当前函数的栈帧,frame 1,表示栈的第二层,即调用当前函数的函数的栈帧。
up n
:表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down n
:表示向栈的下面移动n层,可以不打n,表示向下移动一层。
info f
:会打印出更为详细的当前栈层的信息。
layout
用于分割窗口,可以一边查看代码,一边测试。如下图是layout src
的窗口展示:
命令及参数 | 功能 |
---|---|
layout src | 显示源代码窗口 |
layout asm | 显示汇编窗口 |
layout regs | 显示源代码/汇编和寄存器窗口 |
layout split | 显示源代码和汇编窗口 |
layout next | 显示下一个layout |
layout prev | 显示上一个layout |
另外我们可以通过一些功能键调整窗口选项:
Ctrl + L,刷新窗口
Ctrl + x,再按1:单窗口模式,显示一个窗口
Ctrl + x,再按2:双窗口模式,显示两个窗口
Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。
Ref
https://www.gnu.org/software/gdb/
https://blog.csdn.net/awm_kar98/article/details/82840811
http://blog.chinaunix.net/uid-29611934-id-5168746.html