一. gdb编译选项(gcc/g++ 在编译时加入-g来加入debug信息)
- -g0等于不加-g,即不包含任何调试信息
- -g1包含最小的调试信息,一般来说只有在你不需要调试信息,之需要backtrack信息,并且很在意程序大小,或者有其他保密/特殊需求是才会使用-g1
- -g2为gdb默认,包含绝大多数你需要的调试信息
- -g3包含额外的调试信息,例如包含宏定义信息
二. gdb常见用法
- 调试程序
1.gdb ${application} 进入gdb后,输入run(简写r) ${arg1} ${arg2}...${argN}
2.gdb --args ${application} ${arg1}${arg2}...${argN},进入gdb后运行run
3.gdb进入gdb,输入file ${application}。然后使用set args ${arg1}${arg2}...${argN}设定好程序的参数然后再run - 调试正在运行的程序
gdb ${application} ${pid} - 调试core文件
gdb ${application} ${core文件}
三. 常用命令 - backtrack:显示栈信息,简写为bt
- frame x: 切换到第x帧。其中x会在bt命令中显示,从0开始,0表示栈顶,简写为f
- up/down x往栈顶/栈底移动x帧,当省略x时默认为1
4 print x打印x信息,x可以是变量、对象或者数组,简写为p - print */&x打印出x的内容或者地址
- call调用函数,注意该命令需要一个正在运行的程序
- set substitute-path from_path to_path替换源代码文件。当编译机与运行机代码路径不同是需要用该命令替换代码路径,否则你无法看到源代码
- break x.cpp:n在x.cpp第n行设置断点,然后gdb会给出断点编号m。命令看简写为b后面会有break命令的详细解释
- command m设置程序执行到断点m处要查看的内容如:
command n
>printf "x is %d \n", x
>c
>end
//如果command命令后面没有n则默认为最后一 个breakpoint - x /nfu ${addr}打印addr的内容。addr可以是任何合法的地址表达式,如0x562fb3d、一个有效的指针p或者有效的变量地址(&var),/nfu是格式n表示要查看的长度,f表示格式(例如16进制/10进制),u表示单位(例如单字节b,双字h,四字w)一个例子:
(gdb) x /3xw 0x562fb3d //即以16进制显示地址0x562fb3d处的3个单位,每个单位4字节内容 - continue继续运行程序,可简写为c
- until直到当前循环完成,可简写为u
- step单步调试,步入当前函数,可简写为s
- next单步调试,布过当前函数,可简写为n
- finish执行到当前函数返回,步出当前函数
- set var x=2改变变量x的值,也可以这样使用:set {int}0x32075598 = 2把内存0x32075598的内容解释为int并修改其内容为2
- info locals打印当前栈帧的本地变量
- jump使当前执行程序调整到某一行,或者跳转到某个地址。由于只会使程序跳转而不会改变栈值,因此若跳出函数到另外的地方 会导致return出错。另外,熟悉汇编的人都知道,程序运行时,有一个寄存器用于保存当前代码所在的内存地址。所以,jump命令也就是改变了这个寄存器中的值。于是,你可以使用“set $pc”来更改跳转执行的地址。如: set $pc = 0x485
- return强制函数返回可以指定返回值
四. 程序中断机制:监视点(watchpoint)和捕捉点(catchpoint)
- 监视点:监视内存某个地址,一旦该地址的内容被改变,程序就进入调试器。监视点又分为软件模式和硬件模式:软件监视是gdb单步执行程序的同时测试变量的值,所以速度会变慢。同时软件监视只在当前线程有效,幸运的是32位intelx86提供了4个特殊的调试寄存器用来方便调试程序,gdb可以使用这些寄存器建立硬件监视然而,可用的(enable的)硬件监视点的个数是有限的。如果你设置了过多的硬件监视点,当程序从中断的状态变为执行的状态(例如continue,until或者finish)时,GDB 可能无法把它们全部激活。另外,活动的硬件监视点的数量只有在试图继续执行程序时才能知道,也就是说,即使你设置了过多的硬件监视点,gdb在你运行程序之前也不会警告你。设置监视点的命令有3个:watch(写监视)、rwatch(读监视)以及awatch(读写监视),他们的使用方法一样都为以下几种:
1.(r/a)watch x:x为变量,当x的值改变/被读取时,程序进入调试器
2.(r/a)watch 0xN:N为有效地址
3.(r/a)watch (int)0xN:N为有效地址当该地址int型指针指向的内容改变时进入调试器
4.(r/a)watch -l (int)0xN:较上一个,该地址本身变化也会进入调试器 - 断点,断点break有一些变体:tbreak、hbreak、thbreak与rbreak。tbreak和break功能相同,只是所设置的断点在触发一次后自动删除,hbreak是一个硬件断点,thbreak是一个临时硬件断点。注意硬件断点需要硬件支持,某些硬件可能不支持这类断点。rbreak稍稍特殊些,它会在匹配正则表达式的全部位置加上断点,该断点后面还有详述。非rbreak断点的用法:
1.(t/h)break x.cpp:y:在文件x.cpp第y行加断点,x.cpp若不指定则在当前文件第y行加断点,若程序未执行则以包含main函数的文件设置断点,若x.cpp和y都不指定则以当前debuger的点作为断点
2.(t/h)break 0xN:在地址0xN处加断点,N为有效的代码段地址
3.(t/h)break x.cpp:func:在x.cpp的函数func入口处加断点
4.(t/h)break +/-N:在当前运行处第N行后/前加断点
5.rbreak REGEXP:在所有符合正则表达式的函数入口处加断点,如:rbreak EX_*表示所有符合以EX_开头的函数入口处加断点。注意break后面还有两个可选参数:线程id和条件,线程id指在info threads中的线程序号,而非系统提供的tid,例如break x.cpp:y 2 if(a == 24)表示在2号线程的x.cpp文件第y行加入断点并且只有当a==24,程序才会触发断点另外,breakpoint可以通过save命令保存,方便下次再次进入程序调试时不再需要需要重新设置断点 - 捕捉点是当某些事件发生时,程序进入调试状态,如catch一个exception、assert、signal,fork甚至syscall。tcatch和catch功能一样,区别是tcatch是临时的,catch一次后就会自动删除
五. 跟踪点(tracepoint)
跟踪点与上面介绍的不同之处在于,他只是跟踪记录信息而不会中断程序的运行。tracepoint可以通过save保存,方便下次继续使用。
六. 检查点
gdb可以保存程序某个时间点的程序状态或者程序映像,并且稍后又可以返回到这个状态,这称为checkpoint。每个检查点是进程的一份拷贝,这样当一个bug很难重现,而又担心调试过了头时又要重头开始,这时候可以设置checkpoint,这样即使调试过头了,也可以直接从该checkpoint开始,而不用重新重启整个程序(也许需要很久!!!)。但是每个checkpoint有自己的唯一进程id,这个pid与原程序的pid是不同的,因此程序需要使用pid信息时需要慎重考虑。