在了解GDB可以做什么,怎么做之前,让我们先来看看为什么要用GDB,或者说对调试工具有什么期望。
一般我们使用GDB(或其他调试工具)是为了发现程序bug,更经常地是在已知程序有错的情况下定位bug。既然这样,我们就需要跟踪程序的执行情况,查看程序执行是否正常,当然这就需要有个让我们与执行程序交互的环境,调试工具提供一个能让程序在你的掌控下执行,并让你能够查看一些执行过程中的“内幕信息”的环境。
为了查看程序运行过程中的状态,我们就希望程序能在适当的位置或者在一定的条件下能够暂停运行;为此,调试工具提供了断点、查看变量/表达式、显示程序栈等功能。看了某个点的“内幕”后,我们还期望更多,所以要能控制程序运行才行,这就要求断点、继续运行、单步(多步)运行、进入函数运行等功能,在某些情况下,还需要通过修改当前的执行环境(变量等)来达到期望的执行顺序。也就是说,光看着是不够的,还需要能改才行。
一、引言 | |
在了解GDB可以做什么,怎么做之前,让我们先来看看为什么要用GDB,或者说对调试工具有什么期望。 一般我们使用GDB(或其他调试工具)是为了发现程序bug,更经常地是在已知程序有错的情况下定位bug。既然这样,我们就需要跟踪程序的执行情况,查看程序执行是否正常,当然这就需要有个让我们与执行程序交互的环境,调试工具提供一个能让程序在你的掌控下执行,并让你能够查看一些执行过程中的“内幕信息”的环境。 为了查看程序运行过程中的状态,我们就希望程序能在适当的位置或者在一定的条件下能够暂停运行;为此,调试工具提供了断点、查看变量/表达式、显示程序栈等功能。看了某个点的“内幕”后,我们还期望更多,所以要能控制程序运行才行,这就要求断点、继续运行、单步(多步)运行、进入函数运行等功能,在某些情况下,还需要通过修改当前的执行环境(变量等)来达到期望的执行顺序。也就是说,光看着是不够的,还需要能改才行。 理解了这些问题后,我们就明白GDB的各个功能的用意了,自然也就明白该如何使用调试工具了。当然,要让GDB有效的发挥作用,还是需要一定的经验与技巧,而这主要靠实践,学习资料(包括本文)充其量只能帮你一把(小心别让它帮倒忙)。 总而言之,我们首先要明白使用调试工具的目的和用意,才能理解它的各项功能,才能借助它快速有效的发现问题;否则,即使工具再强大,你也不知道该如何使用才好。 | |
二、GDB能做什么 | |
GDB可以用来调试C、C++、Modula-2的程序。一般来说,GDB能做的事大致可以分为四类: 1、启动程序,按指定的方式执行程序。 | |
三、GDB使用概述 | |
首先要了解的是gdb的help命令,因为你可能记不住各个命令的语法和用途,但只要能正确使用help命令,你就不需要任何其它的gdb资料。 启动gdb后,输入help [eric@linux eric]$ gdb aliases -- Aliases of other commands Type "help" followed by a class name for a list of commands in that class.
为调试编译代码 为了使 gdb 正常工作, 你必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号. gdb 利用这些信息使源代码和机器码相关联. 在GDB中运行程序 当以gdb <program>方式启动gdb后,可以使用r或是run命令运行程序。在程序运行之前,你有可能需要设置下面四方面的事。 1、程序运行参数。 2、运行环境。 3、工作目录。 4、程序的输入输出。
可以有两种方法调试已运行程序: 暂停/恢复程序运行 你可以使用info program 来查看程序的当前的执行状态。 在gdb中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops)。如果要恢复程序运行,可以使用c或是continue命令。 查看变量/表达式的值 可以使用print expr(或p expr)来查看程序变量/表达式的值 显示程序栈 可以使用backtrace(或bt)来显示程序栈 单步跟踪 next [n] 执行下一条(或n条)语句,不进入子程序 step [n] 执行下一条(或n条)语句,进入子程序,可用finish从子程序返回
| |
四、GDB常用命令 | |
backtrace 显示程序中的当前位置和表示如何到达当前位置的栈跟踪(同义词:where) 命令的具体使用方法请用上面介绍的help查询,看不明白的地方就多试试。 | |
五、用例子说话 | |
本文是打算写个简单的程序作为例子讲解的,后来一想:“太假”,就讲一个前几天我的实际调试经历吧,因为当时没抓图,这里就用文字描述了,请读者注意其中的思路和方法,具体的一些操作就要劳烦自己去实践了 背景:在将一个linux程序(姑且就叫A吧)重redhat 9.0移植到redhat es时发现程序core了 开始了,呵呵: 1、首先我查看了程序日志,找到引起程序core掉的数据(一个网页);//所以说日志很重要 2、下载了那个网页,用它作为输入,结果必core;//确认出错环境 3、用gdb启动程序,然后触发错误后,用bt查看程序栈,记录栈中的函数调用链以及出错的代码行数 4、在用gdb启动程序,在出错行前设断点,运行之,再触发错误 5、使用next和step精确定位到出错行 6、print一个指针变量,发现不是NULL,再看指针所指结构的各个变量也正常 7、好像没错呀,呵呵,此处是个循环,继续单步便重复6 8、发现循环中指针递减,怀疑指针所指数组越界,打印数组起始位置地址 9、继续循环一直到出错,打印指针变量,发现其指向的地址低于数组起始位置地址,真的越界了 10、初步算是找到了,查看程序源码,发现循环中没有判断该指针是否低于数组起始位置地址 11、修改代码后重新运行,程序不core了 12、将新程序放到正常执行环境下工作,长时间运行后没有发现该问题重现,确认解决问题 13、通知出错部分(一个功能函数库)的作者问题找到、原因 注:为简单起见省略了过程中的一些因系统特殊性引起的工作 |