GDB的使用方法 (2)
一、GDB的静态调试启动方法:
1、当需要在命令行通过gdb来启动可执行程序的时候,可使用一下命令:
gdb <可执行程序名>
这个时候gdb会加载可执行程序的符号表和堆栈,并为启动程序作好准备;
接下来,需要设置可执行程序的命令行参数:
set args <参数列表>
然后设置断点: b或break;
最后通过命令r或run来启动程序,或者通过c或continue命令来继续已经被暂停的程序;
2、当程序core的时候,需要查看core文件的内容,颗使用以下方式:
gdb <可执行程序名> <core文件名>
这个时候,gdb会结合可执行程序的符号和堆栈来查看core文件内容,以分析程序在core掉时的内存影象;
二、GDB的动态调试启动方法:
动态调试就是在不终止正在运行的进程的情况下来对这个正在运行的进程进行调试;其启动方式有两种:
方式一:
gdb <可执行程序名> <进程ID>
比如: gdb <可执行程序名> 1234
这条命令会把进程ID为1234的进程与gdb联系起来,也就是说,这条命令会把进程ID为1234的进程的地址空间附着在gdb的地址空间中,然后使这个进程在gdb的环境下运行,这样的话,gdb就可以清楚地了解该进程的执行情况、函数堆栈、内存使用情况,等等;
方式二:
直接在gdb中把一个正在运行的进程连接到gdb中,以便于进行动态调试;使用attach命令:
attach <进程ID>
当使用attach命令时,你应该先使用file命令来指定进程所联系的程序源代码和符号表;当gdb接到attach命令后的第一件事情就是停止进程的运行,你可以使用所有gdb的命令来调试一个已"连接"到gdb的进程,这就像你使用run/r命令在gdb中启动它一样.如果你要进程继续运行,那么,可以使用continue/c命令就可以了;
detach:
当你调试结束之后,可以使用该命令断开进程与gdb的连接(结束gdb对进程的控制),在这个命令执行之后,你所调试的那个进程将继续运行;
如果你在使用attach命令把一个正在运行的进程连接到gdb之后又退出了gdb,或者是使用run/r命令执行了另外一个进程,那么刚才那个被连接到gdb的进程将会因为收到一个kill命令而退出;
如果要使用attach命令,你的操作系统环境就必须支持进程;另外,你还需要有向进程发送信号的权力;
使用attach命令的例子:
gdb
file <可执行程序名> #指定进程所关联的程序源代码和符号表
attach <进程ID>
.....
使用gdb的命令进行调试;
.....
detach #调试结束,解除进程与gdb的连接,使进程继续运行;
三、GDB调试过程中使用到的概念和命令
1、gdb的工作路径:
在gdb命令提示符下:
pwd: 查看gdb当前的工作路径;
cd : 改变gdb当前的工作路径;
info terminal:显示gdb当前所使用的终端的类型信息;
2、gdb的环境
在gdb命令提示符下:
show paths: 显示当前路径变量的设置情况;
show environment/env [VARNAME]:显示程序的环境变量VARNAME的值;如果不指明环境变量名,那么该命令将显示所有环境变量的值;
set environment/env VARNAME [=] VALUE:设置程序的某个环境变量VARNAME的值;不过,只对你所调试的程序有效,对gdb本身不起作用;
unset environment/env VARNAME:删除程序的某个环境变量VARNAME;
3、程序的停止和继续
调试器的基本功能就是让你能够在程序运行时在终止之前在某些条件成立的时候停下来,然后你就可以使用gdb的所有命令来检查程序变量的值和修改变量的值,这样的话,你就可以检查当的你程序出错时你的程序究竟做了什么.
当程序停止的时候,gdb都会显示一些有关程序状态的信息,比如:程序停止的原因、堆栈,等等.如果要了解更详细的信息,可以使用info program命令来显示当前程序运行的状态信息;
info program: 显示有关你的程序的状态信息,你的程序是在运行还是停止,是什么进程,为什么停止,等等;
4、断点:
断点的作用是当你的程序运行到断点时,无论它在做什么,都会被停止下来.对于每个断点,你还可以设置一些更高级的信息以决定断点在什么时候起作用.
设置断点的位置:代码行、函数上、地址上.在那些含有异常处理的语言(如C++)中,还可以在异常发生的地方设置断点;
断点分为普通断点和条件断点;
5、设置普通的断点:
使用break/b命令来设置普通断点;有很多种方法可以设置断点:
A、break FUNCTION
在某个函数上设置断点.当使用允许函数重载的语言(如:C++)时,有可能同时在几个重载的函数上设置了断点;
比如:CLogManager类有三个重载的成员函数insert(const struct log_mo_simple&)、insert(const struct log_mt_simple&)、insert(const struct log_st_simple&)
设置断点: break CLogManager::insert
执行该命令之后,gdb会找到实现CLogManager类的源文件,然后在这三个重载的成员函数上都设置一个断点;
B、break +OFFSET 或 break -OFFSET
程序运行到当前行时的前几行或后几行; OFFSET表示行号;
C、break LINENUM
在行号为LINENUM的行上设置断点.程序在运行到次行之前会自动被gdb停止;
D、break FILENAME:LINENUM
在文件名为FILENAME的源文件中的第LINENUM行上设置断点;
E、break FILENAME:FUNCTION
在文件名为FILENAME的源文件中的名为FUNCTION的函数上设置断点;
F、break *ADDRESS
在地址ADDRESS上设置断点.这个命令允许你在没有调试信息的程序中设置断点;
G、break
不含任何参数的break命令,会在当前执行到的程序运行栈中的下一条指令上设置一个断点.除了栈底以外,这个命令使程序在一旦从当前函数返回时停止;相似的命令是finish,但是finish命令并不设置断点.这一点在循环语句中很有用;gdb在恢复执行时,至少要执行一条命令;
6、条件断点:
条件断点就是设置的断点只在某个条件成立的时候才有效,才会使程序在运行到断点之前停止;语法:
break ... if CONDITION
这个命令设置一个条件断点,条件由CONDITION来决定.在gdb每次执行到此时,如果CONDITION条件的值被计算为非0,那么程序就在该断点处停止;
tbreak ARGS:
该命令设置断点为只有效一次.ARGS的使用同"break"中的参数的使用;
7、删除断点:
当一个断点使用完之后,需要删除这些断点;clear命令和delete命令可以完成这项任务;
A、clear:
不带任何参数的clear命令会在当前所选择的栈上清除下一个所要执行到的断点(指令级).当你当前的栈帧是栈中最内层的时候,使用这个命令可以很方便地删除刚才程序停止处的断点;
B、clear FUNCTION 和 clear FILENAME:FUNCTION
删除名为FUNCTION的函数上的断点;
C、clear LINENUM 和 clear FILENAME:LINENUM
删除第LINENUM行上的断点;
D、delete [breakpoints] [BNUMS...]
删除参数所指定的断点,如果没有指定参数,则删除程序中所有的断点.这个命令可以缩写成d;
8、使断点暂时不起作用:
使用enable命令来激活断点或观察点,使用disable命令来使断点或观察点暂时不起作用,使用info break或info watch命令来观察哪些断点是活跃的;
断点或观察点有四种状态:
A、使能:
当程序运行到断点处的时候,程序自动停止.使用break命令设置的断点默认情况下都是使能的;
B、不使能:
断点虽然被设置了,但是它不影响程序的运行,程序不会在断点处停止;
C、使能一次后变为不使能:
断点对程序运行的影响只有一次,然后就自动变为不使能状态了.使用tbreak命令设置的断点默认是这个状态的;
D、使能一次之后自动删除:
断点在起了一次作用之后被自动删除了;
使用一下命令来使能或不使能断点:
A、disable [breakpoints] [BNUMS...]
使参数所指定的断点或观察点变为不使能状态;如果没有指定参数,那么缺省的动作就是使程序中设置的所有断点和观察点都变为不使能状态;当一个断点或观察点被使能之后,它在不使能前的状态会被记录下来,在断点或观察点再次被激活的时候,原来的状态会得到继续.该命令缩写为dis;
B、enable [breakpoints] [BNUMS...]
使能参数所指定的断点或全部断点;
C、enable [breakpoints] once BNUMS...
使能参数所指定的断点或全部断点,但是这些断点都只被使能这些断点一次;
D、enable [breakpoints] delete BNUMS...
使能参数所指定的断点或全部断点,但是这些断点在被使能一次之后就被自动删除了;
除了使用tbreak命令设置的断点之外,其余的命令所设置的断点在被设置时都是使能的;
9、查看断点或观察点的状态信息:
A、info breakpoints [BREAKNUM]
查看断点号BREAKNUM所指定的断点的状态信息;如果没有指定参数,则查看所有断点的状态信息;
B、info break [BREAKNUM]
查看断点号BREAKNUM所指定的断点的状态信息;如果没有指定参数,则查看所有断点的状态信息;
C、info watchpoints [BREAKNUM]
查看断点号BREAKNUM所指定的观察点的状态信息;如果没有指定参数,则查看所有观察点的状态信息;
D、maint info breakpoints
与info breakpoints一样,显示所有断点的状态信息,不论是你设置的还是gdb自动设置的;
10、设置观察点:
可以使用一个观察点来停止一个程序的执行,当某个表达式的值改变时,观察点将会停止程序,而不需要事先在某个地方设置一个断点;由于观察点的这个特性,使观察点的开销比较大,但是再捕捉错误时非常有用,特别别是当你不知道程序到底在什么地方出了问题;
A、watch EXPR
watch命令使用EXPR作为表达式设置一个观察点.gdb将把表达式加入到程序中,并监视程序的运行,当表达式的值被改变的时候,gdb将会停止程序;
B、rwatch EXPR:
使用EXPR作为表达式设置一个断点,当EXPR被程序读取时,程序被gdb暂停;
C、awatch EXPR:
使用EXPR作为表达式设置一个观察点,当EXPR被读出然后被写入时,gdb会暂停程序;这个命令常和rwatch合用;
D、info watchpoints:
显示所有设置的观察点的列表;它与info breakpoints命令类似;
11、其它gdb调试命令:
A、list/l [LINE_NUM]:
显示源文件中行号为LINE_NUM的前面几行到后面几行之间的源代码;如果不执行行号,则显示当前行的前面几行到后面几行之间的源代码;
B、continue/c: 继续运行被中断的程序;
C、next/n: 继续执行下一行代码;
D、step/s: 单步跟踪调试,它可以进入函数内部,跟踪函数的内部执行情况;
E、backtrace/bt: 显示当前堆栈的内容;
F、print/p <表达式/变量>: 打印表达式或变量的值;
G、frame/f <STACK_FRAME_NO/ADDRESS>: 选择一个栈帧,并进入这个栈帧,同时打印被选择的栈帧的内容摘要信息;该命令的参数是一个栈帧的号码或者是一个栈帧地址;
H、info stack/frame: 显示栈/帧的摘要信息;
I、run/r: 在gdb中启动并运行程序;
J、help info: 显示命令info的用法;
K、help <Command>: 显示命令Command的详细用法;
L、注意gdb的帮助系统help的使用,通过它,可以获得更多的使用信息;
12、对多线程程序的调试:
A、thread THREAD_NO: 该命令用于在线程之间进行切换,把线程号为THREAD_NO(gdb设置的线程号)的线程设置为当前线程;
B、info threads: 查询当前进程所拥有的所有线程的状态摘要信息;gdb按照顺序显示:
a、线程号: gdb为被调试进程中的线程设置的顺序号;
b、目标系统的线程标识;
3、此线程的当前栈信息;
一些前面带'*'号的线程,表示该线程是当前线程;
C、thread apply [THREAD_NO] [ALL] ARGS: 该命令用于向线程提供命令;
另外,无论gdb何时中断了你的程序(因为一个断点或者是一个信号),gdb会自动选择信号或断点发生的线程作为当前线程;也就是说,当一个信号或者一个断点在一个线程THREAD_A中发生而导致gdb中断了你的程序,那么,gdb会自动选择该线程(THREAD_A)作为当前线程,执行info threads后,显示的线程状态信息列表中,当前线程的前面带有一个星号'*';