GDB的基本介绍
GDB是GUN开源组织发布的一个强大的UNIX下的程序调试工具。或许,平时大家更加习惯图形界面的调试,比如VS上的IDE调试;但是如果我们在UNIX或Linux下做软件,我们呢就更加需要熟练GDB这个调试工具。
一般来说,GDB主要能够完成以下的几个功能:
①启动你的程序,可以按照你自己的要求随性所欲的运行程序;
②可以让被调试的程序在你自己所定的位置的断点处挺住;
③当程序被停住时,可以检查此时你的程序中所发生的事情;
④动态的改变你程序的执行环境。
GDB调试使用的是DEBUG版本的,所以我们在生产可执行文件时,我们需要在命令的尾部加上 -g,这样就可以生成的是DEBUG版本下的可执行文件。
调试代码的基本命令
命令 | 作用 |
list或l + 行号 | 显示从行号开始的源代码 |
list或l + 函数名 | 列出某个函数的源代码 |
run或r | 运行程序 |
step或s | 进入函数调用 |
breaktrace或bt | 查看各级函数调用及参数 |
info或i | locals查看当前栈帧局部变量的值 |
info break | 查看断点信息 |
finish | 执行到当前函数返回,然后停下来等待命令 |
print或p | 打印表达式的值,通过表达式可以修改变量的值或者调用函数 |
break或b + 行号 | 在某一行设置断点 |
set var | 修改变量的值 |
quit | 退出调试 |
break + 函数名 | 在某个函数开头设置断点 |
continue或c | 从当前位置开始连续而非单步执行调试程序 |
run或r | 从开头连续执行程序而非单步执行 |
delete breakpoints | 删除断点 |
delete breakpoints n | 删除序号为n的断点 |
disable breakpoints | 禁用断点 |
enable breakpoints | 启用断点 |
info或i breakpoints | 查看当前设置了哪些断点 |
display + 变量名 | 跟踪查看一个变量,每次停下来都显示这个值 |
undisplay | 取消先前对那些变量设置的跟踪 |
until + x | 跳至第x行 |
n或next | 单步执行 |
p + 变量 | 打印变量值 |
list或l | 列出源代码,接着上次的位置往下列,每次列10行 |
frame或f | 帧编号,选择栈帧 |
start | 开始执行程序,停在main函数第一行语句前面等待命令 |
call + 函数名 | 强制调用某个函数 |
GDB调试多进程
在默认情况下是调试多进程程序时GDB会默认调试主进程,但是GDB支持多进程的分别与同步调试。即GDB支持同时调试多个进程,只需要设置follow-fork-mode(默认为parent)和detach-on-fork(默认为on)即可。我们还可以使用catch fork指令,如果fork异常,会停止程序。follow-fork-mode | detach-on-fork | 说明 |
parent | on | 只调试主进程(GDB默认) |
child | on | 只调试子进程 |
parent | off | 同时调试两个进程,gdb跟主进程,子进程block(阻塞)在fork位置 |
child | off | 同时调试两个进程,gdb跟子进程,主进程block在fork位置 |
设置方法: set follow-fork-mode[parent|child] set detach-on-fork[on|off]
显示:show follow-fork-mode show detach-on-fork
下面通过代码来演示
调试步骤:
只调试父进程
只调试子进程(与上面的对比起来看)
下面呢我们开始直接调试两个进程(父进程运行调试,子进程阻塞等待)
最后我们进行子进程运行调试,父进程阻塞等待
GDB调试多线程
在多线程编程时,当我们需要调试时,有时需要控制某些线程停在断点处,有些线程继续执行;有时需要控制线程的运行程序;有时需要中断某个线程,切换到其他线程。这些呢都可以通过gdb来实现。GDB默认支持调试多线程,跟主线程,子线程block在create+thread。
gdb调试一般有两种模式:all-stop模式和no-stop模式(gdb7.0之前不支持no-stop模式)。
1.all-stop模式
在这种模式下,当你的程序在gdb由于任何原因而停止,所有的线程都会停止,而不仅仅是当前的线程。一般来说,gdb不能单步所有的线程。因为线程调度是gdb无法控制的。无论什么时候当gdb停止你的程序,它都会自动切换到触发断点的那个线程。
2.no-stop模式(网络编程常用)
顾名思义,启动不关模式。当程序在gdb中停止,只有当前的线程会被停止,而其他线程将会继续运行。这时候step,next这些命令就只对当前线程起作用。
如果需要打开no-stop模式,可以向~/.gdbinit添加配置文件:
gdb支持的命里有两种类型:前台的(同步的)和后台(异步 )的。区别很简单,同步的在输出提示符之前会等待程序report一些线程已经终止的信息,异步则是直接返回。所以我们需要set target-async 1。set pagination off不要出现 Type <return> to continue 的提示信息 。最后一步是打开。
下面看一下gdb调试多线程常用命令:
命令 | 作用 |
info threads | 显示所有可调试的线程 |
thread ID | 切换到指定线程,gdb为每一个线程分配一个ID(与tid不同),编号一般从1开始 |
breakfilename:linenum thread all | 在所有线程相应行设置断点,注意如果主线程不会执行到该行,并且启动all-stop模式,主线程执行n或s会切换过去 |
set scheduler-locking off|on\step | 默认off,执行s或c其它线程也同步执行。on,只有当前相称执行。step,只有当前线程执行 |
show scheduler-locking | 显示当前模式 |
thread apply all command | 每个线程执行同意命令,如bt。或者thread apply 1 3 bt,即线程1,3执行bt。 |
通过代码来演示:
调试步骤:
开始调试:
然后开始运行代码:
单步调试:
切换到其他线程:
再切换到其他线程调试:
设置core文件
core的意思是核心,dumped的意思就是抛出,转储,core dumped就是核心转储的意思。当一个进程异常退出前,该进程会抛出当时该程序进程的内存详细情况存储在硬盘上,文件名通常是core,这就叫core dump。
进程异常终止通常是因为代码存在BUG,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做事后调试。
uname -a 查看机器参数
ulimit -a 查看默认参数
ulimit -c 1024 设置core文件大小为1024
ulimit -c unlimit 设置core文件大小为无限
ulimit -c unlimited 生成core文件,也可以是指定大小,然后使用gdb ./main core启动,bt查看调用栈即可。
eg1(可以快速定位出问题的位置)
gdb a.out core.xxx
where
eg2 (在 gdb 中使用)
(gdb) core-file core.xxx