简介
GDB(GNU Debugger)是GCC的调试工具。其功能强大,现描述如下:
GDB主要帮忙你完成下面四个方面的功能:
- 启动程序,可以按照你的自定义的要求随心所欲的运行程序。
- 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
- 当程序被停住时,可以检查此时你的程序中所发生的事。
- 动态的改变你程序的执行环境。
一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中(同时最好关闭优化选项-O0)。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。如:
gcc -g hello.c -o hello
g++ -g hello.cpp -o hello
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址
。
[other:strip命令]
strip
:移除掉文件内的符号信息,即移除某个程序中的调试信息。例如,
strip -s filename #移除所有符号信息
strip --strip-debug filename #仅移除调试信息
GDB调试程序
1. 启动程序
-
启动gdb
gdb program #program 也就是你的执行文件
-
设置运行参数
set args xxx show args # 打印设置好的参数
-
设置运行环境
path <dir> # 可设定程序的运行路径 show paths # 可查看程序的运行路径 set environment varname[=value] # 设置环境变量 show environment # 查看环境变量
-
程序的输入输出
info terminal #显示程序用到的终端的模式 tty /dev/xxx #指定输入输出的终端设备 run > outfile #也可以使用重定向控制程序输出
启动程序
run/r # 程序开始执行,如果有断点,停在第一个断点处
start # 程序向下执行一行
2. 指定源码路径
若源码移动了目录,则使用list命令查看不了源码,可以使用以下方式获取源码路径:
-
dir命令指定源码路径
#查看当前源码搜索路径 (gdb) show dir #将一个或者多个源代码搜索目录加入到当前源码搜索目录列表的前面,目录之间使用空格间隔 #若不带参数则将源码搜索目录恢复为默认值 (gdb) dir ./temp
-
使用set substitute-path src dst 将原来的路径替换为新的路径
#借助readelf命令获取原来的源码路径 readelf a.out -p .debug_str #用新的源码路径替换原来的源码路径 (gdb) set substitute-path srcpath newpath #取消替换 (gdb) unset substitute-path #显示替换规则 (gdb) show substitute-path
注意,gdb还会在当前目录中查找源代码。
3. 显示源代码
GDB 可以打印出所调试程序的源代码,当然,在程序编译时一定要加上 –g 的参数,把源程序信息编译到执行文件中。不然就看不到源程序了。当程序停下来以后,GDB会报告程序停在了那个文件的第几行上。你可以用list命令来打印程序的源代码。默认打印10行。
list linenum # 打印第linenm行的上下文内容
list function # 显示函数名为function的函数的源程序
list - # 显示当前行前面的源程序
list # 显示当前行后面的源程序
set listsize count # 设置一次显示源代码的行数
show listsize # 查看当前listsize的设置
4. 断点操作
-
设置断点(break,简写b)
b linenum # 设置断点,在源程序第linenum 行 b func # 设置断点,在func函数入口处 b filename::linenum # 在源文件filename的linenum行处停住 b filename::func # 在源文件filename的func函数的入口处停住 b namespace::class::func # 在名称空间为namespace的类class的func函数的入口处停住(c++中) b function(type, type) #函数重载时直接指定具体参数类型 b func if value == num # 条件断点 b filename::linenum thread <num> # 给某个线程断点 b #没有参数时,表示在下一条指令处停住 info b <==> info break <==> i b # 查询所有断点
-
维护断点
delete [range..] # 简写为d,如果不指定断点号,则表示删除所有断点。断点号也可以表示为范围(3-7)。 disable [range..] # 简写为dis,使断点无效 enable [range..] # 简写为ena,使断点生效
-
观察点
watch var # 表示在变量 var 设置一个写观察点,当该变量被写时,由 GDB 暂停程序 rwatch var # 表示在变量 var 设置一个读观察点,当该变量被读时,由 GDB 暂停程序 awatch var # 表示在变量 var 设置一个读写观察点,当该变量被读或被写时,由 GDB 暂停程序 info watchpoints # 列出当前所设置了的所有观察点
5. 基本操作
-
调试程序
run/r # 运行程序 #不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住 next/n <count># 单步跟踪,函数调用当作一条简单语句执行 step/s <count># 单步跟踪,函数调进入被调用函数体内 finish # 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址等信息(退出进入的函数) until/u # 在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体 continue/c [ignore-count] # 从当前位置开始运行直到程序结束,或到达下一个断点。ignore-count表示忽略其后的多少次断点 stop # 停止运行 return <value> # 取消当前函数的执行,并立即返回,value会被作为函数返回值 call <func> # 强制调用某函数
-
查看数据
#1.打印数据 print/p value # 打印变量、字符串、表达式等的值, #标准的打印格式print [options --] [/fmt] expr; #使用</fmt>的格式来设置输出格式,例如p/x; #fmt有x十六进制、d十进制、u十六进制无符号、o八进制、t二进制、c字符、f浮点 #打印数组 print array[start_index]@cout set print address on/off # 打开(默认)/关闭打印地址 set print elements 0 # 来关闭掉显示长度限制 #打印高级用法:http://m.biancheng.net/view/8252.html #2.自动显示 display value # 自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量会自动显示 info display # 查看display设置的自动显示的信息 delete display nums # 删除自动显示,nums意为所设置好了的自动显式的编号(info display中可以看到)。 #如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5) disable display nums enable display nums #3.查看内存地址中的值 x/<nfu> <address> # 查看内存地址中的值,nfu是可选的参数。 #(1)n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。 #(2)f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i. #(3)u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes.u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。 #eg: x/3uh 0x54320 :从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。 #4.查看汇编代码 disassemble <func>#反汇编命令,它可被用来查看当前执行时源代码的汇编代码。 #5.查看寄存器、断点、观察点和信号等信息 info #(1) 查看寄存器信息 #info registers (查看除了浮点寄存器以外的寄存器) #info all-registers (查看所有寄存器,包括浮点寄存器) #info registers <regname ...> (查看所指定的寄存器) #(2)查看断点和观察点信息 # info break/watchpoints #(3)查看哪些信号正在被GDB检测 # info signals info handle #(4)查看源码在内存中的地址 # info line <行号、函数名、文件名:行号、文件名:函数名> #(5)查看线程信息 # info threads
-
修改变量的值
ptype value # 查看变量value的类型 set var value=xx # 修改变量value的值 p value=xx # p命令也可以修改变量value的值
-
信号
signal signum # 给程序发送一个信号,常用于在程序运行的任意位置设置断点,并在该断点用GDB产生一个信号量
6. 堆栈操作
-
查看堆栈信息
backtrace/bt # 查看各级函数调用及参数 info locals # 查看当前栈帧局部变量的值 frame/f num # 查看指定栈层信息 info frame # 当前栈层更加详细信息 down/up n # 上下移当前栈层
-
查看当前函数信息
info args # 函数的参数 info locals # 函数的局部变量 info catch # 函数的异常处理
7. 多进程调试
-
在fork()函数之前设置跟踪父进程或是子进程
set follow-fork-mode [child | parent]
-
设置是否分离不需要调试的进程(on)或者是使其暂停(off)
set detach-on-fork [on | off]
-
查看进程信息
info inferiors
-
切换进程
inferiors nums
8. 多线程调试
1.通过info threads可以获取当前存在的线程及其线程编号,使用thread num可以切换到相应的线程:
info threads #查看存在那些线程
thread num #切换线程
break [location] thread id #为特定线程设置断点
break [location] thread all #在所有线程中相应的行上设置断点
2.指定某一个线程执行命令:
thread apply num xxx # 指定某一个线程执行命令
thread apply all xxx # 所以线程都执行命令
3.当切换线程后可以通过set scheduler-locking on设置,只运行当前线程;set scheduler-locking off会运行全部线程(默认):
set scheduler-locking on # 执行一个命令后,例如next , 只运行当前线程
set scheduler-locking off # 运行全部线程
4.gdb的non-stop模式:
a. non-stop off ,当某个线程停止时,其他线程也会停止(默认的,也叫all-stop模式);
b. non-stop on,不会影响其他线程;
set non-stop on / off # 设置non-stop模式
show non-stop # 查看non-stop模式是否开启
注意:在 all-stop 模式下,当某一线程暂停执行时,GDB 调试器会自行将其切换为当前线程;而在 non-stop 模式下则不会。
5.关闭线程产生与退出时的提示信息:
set print thread-events off
9. gdb attach
当使用attach方式调试运行程序时,进入gdb那刻,程序暂停;结束调试时使用detach命令可以让程序和调试器分离,不影响程序的继续运行。
gdb attach pid # 调试正在运行中的程序
detach #分离调试器和程序