调试工具:
GDB
UNIX程序员最常用的调试工具是GDB,大多数Linux系统应该预先安装了GDB。如果没有预先安装该工具,则必须下载GCC编译器程序包。
DDD
随着GUI(图形用户界面)越来越流行,大量的UNIX环境下运行的基于GUI的调试器被开发出来。其中的大多数工具都是GDB的GUI前端:用户通过GUI发出命令,GUI将这些命令传递给GDB。DDD就是其中的一种工具。
如果你的系统还没有安装DDD,则可以下载该工具。例如,centos系统上,命令
yum install ddd
将自动处理整个安装过程。
主要调试器操作
1 单步调试源代码
可以安排程序的执行run在某个地方暂停,以便检查变量的值,从而得到关于程序错误所在位置的线索。下面是可用来暂停程序执行的一些方法。
- 断点
正如前面所说,调试工具会在指定断电处暂停程序的执行。在GDB中是通过break命令及其行号完成的,在DDD中是在相关代码行的任意空白处右击并选择Set Breakpoint来完成的。
- 单步调试
GDB的next命令让GDB执行下一行,然后暂停。step命令的作用于此类似,只是在函数调用时step命令会进入函数,而next导致程序执行的暂停出现在下次调用函数时。DDD有对应的Next和Step菜单项。
- 恢复操作
在GDB中,continue命令通知调试其恢复执行并继续,直到遇到断点为止。DDD中有一个对应的菜单项。
- 临时断点
在GDB中,tbreak命令和break相似,但是这一命令设置的断电的有效期只到首次到达指定行时为止。在DDD中临时断点的设置方式为:在源文本窗口中要设置断点的代码行的任意空白处右击,然后选择Set Temporary Breakpoint。
- GDB中还有创建特殊类型的一次性断点的命令:until和finish。DDD的命令工具中有对应的Until和Finish项。
- 程序执行的典型调试模式如下(以GDB为例):单击一个断点后,通过GDB的next命令一次移动一行代码,或通过step命令单步调试一段时间,以便仔细检查靠近断点处的程序状态和行为。做完这些操作后,可以用continue命令让调试器继续执行程序,直到遇到下一个断点为止,其间不需要暂停。
GDB的暂停机制
有3种方式可以通知GDB的暂停程序的执行。
断点:通知GDB在程序中的特定位置暂停执行。
监视点:通知GDB当特定内存位置(或者涉及一个或多个位置的表达式)的值发生变化时暂停执行
捕获点:通知GDB当特定事件发生时暂停执行。
断点概述
断点就像程序中的绊网:在程序中的特定“位置”设置断点,当到达那一点时,调试器会暂停程序的执行(在GDB这样的基于文本的调试器的情况下,会出现命令行提示符)。
GDB中关于“位置”的含义是非常灵活的,它可以指各种源代码行、代码地址、源代码文件中的行号或者函数的入口等。
GDB执行到断点行之前,GDB显示的是将要执行的代码行。
然而,GDB的工作针对的是机器语言指令,而不是源代码行,一行代码可能对应于数行机器语言。
可以使用info breakpoints命令获得断点信息。使用delete + 断点编号来删除断点。
设置断点
在GDB中设置断点
GDB中有许多指定断点的方式,下面是一些最常见的方法。
- break function
在函数function()的入口(第一行可执行代码)处设置断点。
(gdb) break main
在main()的入口处设置断点。
- break line_number
在当前活动源代码文件的line_number处设置断点。对于多行程序,这要么是上次使用list命令查看其内容的文件,要么是包含main()的文件。
(gdb) break 35
它在文件的第35行处设置了一个断点。
- break filename:line_number
在源代码文件filename的line_number处设置断点。如果filename不在当前工作目录中,则可以给出相对路径名或者完全路径名来帮助GDB查找该文件,例如:
(gdb) break source/bed.c:35
- break filename:function
在文件filename中的函数function()的入口处设置断点。重载函数或者使用同名静态函数的程序可能需要使用这种方式,例如:
(gdb) break bed.c:parseArguments
正如我们看到的,当设置一个断点时,该断点的有效性会持续到删除、禁用或退出GDB时。然而,临时断点是首次到达后就会自动删除的断点。临时断点使用tbreak命令设置,它与break采用相同类型的参数。例如,tbreak foo.c在文件的第10行设置临时断点。
当同一行源代码上有多个断点时会发生什么情况。当GDB使用多个断点中断一行源代码时,它只会中断一次。换言之,当它到达该行代码时,如果恢复执行,会忽略恰好在同一行的其他断点。事实上,GDB知道是哪个断点“触发”了程序停止执行。在具有多个断点的代码行上,触发中断的断点将是标识符编号最小的断点。
断点的持久性
如果在重新修改编译源代码期间不退出GDB。例如,当发现并修复了一个程序错误,但是其他程序错误仍然存在时,不应当退出GDB然后重新进入来使用程序的新版本。这样做有些不必要地繁琐,而且还会不得不重新进入断点。
如果在修改和重新编译代码时没有退出GDB,那么在下次执行GDB的run命令时,GDB会感知到代码已经修改,并自动重新加载新版本。
删除和禁用断点
如果确认不再需要当前断点,那么可以删除该断点。GDB中有两个用来删除断点的命令。delete命令用来基于标识符删除断点,clear命令使用创建断点相同的语法删除断点。
- delete breakpoint_list
删除断点使用数值标识符。断点可以是一个数字,比如delete 2删除第二个断点;也可以是数字列表,比如delete 2 4 删除第二个和第四个断点。
- delete
删除所有断点。除非执行也可以放在.gdbinit启动文件中的set confirm off命令,否则GDB会要求确认删除操作。
- clear
清除GDB将执行的下一个指令处的断点。这种方法适用于要删除GDB已经到达的断点的情况。
- clear function、clear filename:function、clear line_number和clear filename:line_number
这些命令根据位置清楚断点,工作方式与对应的break命令相似。
在GDB中禁用断点
每个断点都可以被启用或禁用。只有当GDB遇到启用的断点时,才会暂停程序的执行;它会忽略禁用的断点。默认情况下,断点的生命期从启用时开始。
使用disable breakppoint-list命令禁用断点,使用enable breakpoint-list命令启用断点,其中breakpoint-list是使用空格分隔的列表,其中有一个或多个断点标识符。例如
(gdb) disble 3
将禁用第三个断点。类似地,
(gdb) enable 1 5
将启用第一个和第五个断点。
不带任何参数地执行disable命令将禁用所有现有断点。类似地,不带参数地执行enable命令会启用所有现有断点。
还有一个enable once命令。在断点下次引起GDB暂停执行后被禁用。语法为:
enable once breakpoint-list