GDB 调试器支持在程序中打 3 种断点,分别为普通断点、观察断点和捕捉断点
break
break 命令(可以用 b 代替)常用的语法格式有以下 2 种。
1、(gdb) break location // b location
2、(gdb) break ... if cond // b .. if cond
- 第一种格式中,location 用于指定打断点的具体位置。
location 的值 | 含 义 |
---|---|
clinenum | inenum 是一个整数,表示要打断点处代码的行号。要知道,程序中各行代码都有对应的行号,可通过执行 l(小写的 L)命令看到。 |
filename:linenum | filename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。 |
+ offset - offset | offset 为整数(假设值为 2),+offset 表示以当前程序暂停位置(例如第 4 行)为准,向后数 offset 行处(第 6 行)打断点;-offset 表示以当前程序暂停位置为准,向前数 offset 行处(第 2 行)打断点。 |
function | function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。 |
filename:function | filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点 |
- 第二种格式中,… 可以是表 1 中所有参数的值,用于指定打断点的具体位置;cond 为某个表达式。整体的含义为:每次程序执行到 … 位置时都计算 cond 的值,如果为 True,则程序在该位置暂停;反之,程序继续执行。
例:
(gdb) b 7 if num>10 <-- 如果 num>10 在第 7 行打断点
(gdb) r <-- 运行程序,至第 7 行暂停
(gdb) c <-- 继续执行
(gdb) p num <-- p 命令查看 num 当前的值
tbreak 命令可以看到是 break 命令的另一个版本,tbreak 和 break 命令的用法和功能都非常相似,唯一的不同在于,使用 tbreak 命令打的断点仅会作用 1 次,即使程序暂停之后,该断点就会自动消失。
rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点.
rbreak 命令的使用语法格式为:
(gdb) rbreak regex
其中 regex 为一个正则表达式,程序中函数的函数名只要满足 regex 条件,rbreak 命令就会其内部的开头位置打断点。rbreak 命令打的断点和 break 命令打断点的效果是一样的,会一直存在,不会自动消失。
watch
使用 GDB 调试程序的过程中,借助观察断点可以监控程序中某个变量或者表达式的值,只要发生改变,程序就会停止执行。相比普通断点,观察断点不需要我们预测变量(表达式)值发生改变的具体位置。
# conde 指的就是要监控的变量或表达式。
(gdb) watch cond
和 watch 命令功能相似的,还有 rwatch 和 awatch 命令。其中:
rwatch 命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;
awatch 命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。
watch 命令的功能是:只有当被监控变量(表达式)的值发生改变,程序才会停止运行。
查看当前建立的观察点的数量,借助如下指令即可:
(gdb) info watchpoints
注意事项:
- 当监控的变量(表达式)为局部变量(表达式)时,一旦局部变量(表达式)失效,则监控操作也随即失效;
- 如果监控的是一个指针变量(例如 *p),则 watch *p 和 watch p 是有区别的,前者监控的是 p 所指数据的变化情况,而后者监控的是 p 指针本身有没有改变指向;
- 这 3 个监控命令还可以用于监控数组中元素值的变化情况,例如对于 a[10] 这个数组,watch a 表示只要 a 数组中存储的数据发生改变,程序就会停止执行。
watch命令的实现原理
watch 命令实现监控机制的方式有 2 种,一种是为目标变量(表达式)设置硬件观察点,另一种是为目标变量(表达式)设置软件观察点。
所谓软件观点(software watchpoint),即用 watch 命令监控目标变量(表达式)后,GDB 调试器会以单步执行的方式运行程序,并且每行代码执行完毕后,都会检测该目标变量(表达式)的值是否发生改变,如果改变则程序执行停止。
所谓硬件观察点(Hardware watchpoint),是在实现监控机制的同时不影响程序的执行效率。系统会为 GDB 调试器提供少量的寄存器(例如 32 位的 Intel x86 处理器提供有 4 个调试寄存器),每个寄存器都可以作为一个观察点协助 GDB 完成监控任务。
基于寄存器个数的限制,如果调试环境中设立的硬件观察点太多,则有些可能会失去作用,这种情况下,GDB 调试器会发出如下警告:
Hardware watchpoint num: Could not insert watchpoint
# 可以删除或者禁用一部分硬件观察点。
某些操作系统中,GDB 调试器最多只能监控 4 个字节长度的数据
GDB 调试器在建立观察断点时,会优先尝试建立硬件观察点,只有当前环境不支持硬件观察点时,才会建立软件观察点。
可强制 GDB 调试器只建立软件观察点:
set can-use-hw-watchpoints 0
awatch 和 rwatch 命令只能设置硬件观察点,如果系统不支持或者借助如上命令禁用,则 GDB 调试器会打印如下信息:
Expression cannot be implemented with read/access watchpoint.
catch
捕捉断点的作用是,监控程序中某一事件的发生,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标时间发生,则程序停止执行。
用捕捉断点监控某一事件的发生,等同于在程序中该事件发生的位置打普通断点。
event 参数表示要监控的具体事件。
(gdb) catch event
1. 对于使用 catch 监控指定的 event 事件,其匹配过程需要借助 libstdc++ 库中的一些 SDT 探针,系统中 GCC 编译器的版本最低为 4.8,catch 命令是否能正常发挥作用,还可能受到系统中其它因素的影响。
2. 当 catch 命令捕获到指定的 event 事件时,程序暂停执行的位置往往位于某个系统库(例如 libstdc++)中。这种情况下,通过执行 up 命令,即可返回发生 event 事件的源代码处。
3. catch 无法捕获以交互方式引发的异常。
对于目标事件,catch 命令的监控是永久的,而 tcatch 命令只监控一次,也就是说,只有目标时间第一次触发时,tcath 命令才会捕获并使程序暂停,之后将失效。
在执行catch throw int时,其会暂停至 libstdc++ 库中的某个位置,借助 up 指令可以得知该异常发生在源代码文件中的位置。
在个别场景中,还可以使用 catch 命令监控 C、C++ 程序动态库的加载和卸载。就以 main.exe 为例,其运行所需加载的动态库可以使用 ldd 命令查看,例:dd main.exe
以监控 libstdc++.so.6 为例,在 GDB 调试器中,通过执行如下指令,即可监控该动态库的加载:
(gdb) catch load libstdc++.so.6
tcatch 命令的语法格式和 catch 完全相同