特别好的一家网站,推荐食用。
gdb -v # 看信息返回结果,如果没有安装,执行下一步,安装GDB
sudo apt-get install gdb
只有当源程序编译成可执行文件时,GDB才可以调试,并且仅仅使用(gcc、g++)命令编译生成的可执行文件,也是无法借助GDB进行调试的。
gcc main.c -o main # 不可以调试
只需要使用 gcc -g
选项编译源文件,即可生成满足 GDB 要求的可执行文件,该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等)
gcc main.c -o main -g
第一步,利用编译指令,生产可以debug的可执行文件
gcc/g++ 源程序 -o 可执行文件名 -g # 举例
第二步,启动调试器
# 打开终端,切换到可执行文件所在的路径
gdb 可执行文件名 # 这样启动GDB,会打印一堆免责条款
或者:
gdb 可执行文件名 --silent # 这样会省略免责条款
直到最后出现下面的显示,表示启动成功:
(gdb)
GDB常用调试指令
调试指令 | 作用 |
---|---|
(gdb) break xxx (gdb) b xxx |
在源代码指定的某一行设置断点,其中 xxx 用于指定具体打断点的位置(行号)。 |
(gdb) run (gdb) r |
执行被调试的程序,其会自动在第一个断点处暂停执行。 |
(gdb) continue (gdb) c |
当程序在某一断点处停止运行后,使用该指令可以继续执行,直至遇到下一个断点或者程序结束。 |
(gdb) next (gdb) n |
令程序一行代码一行代码的执行。 |
(gdb) print xxx (gdb) p xxx |
打印指定变量的值,其中 xxx 指的就是某一变量名。 |
(gdb) list (gdb) l |
显示源程序代码的内容,包括各行代码所在的行号。 |
(gdb) quit (gdb) q |
终止调试。 |
启动 GDB
调试器之后,可以使用 help
命令查看 GDB
指令
help 单独使用是查看命令的种类
help 添加命令的种类,表示使用这条命令查看各个种类中具体命令选项
help 添加具体的一条命令,表示查看命令的使用方式
举例:
# 1、使用 help 命令,向我们展示了命令总体被划分成了12种,其中每一种又会包含许多的命令
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands
# 2、查看各个种类种的命令使用方法:
(gdb) help <class>
# 其中 表示 help 命令显示的 GDB 中的命令的种类
# 比如:
(gdb) help breakpoints
Making program stop at certain points.
List of commands:
awatch -- Set a watchpoint for an expression
break -- Set breakpoint at specified location
break-range -- Set a breakpoint for an address range
catch -- Set catchpoints to catch events
catch assert -- Catch failed Ada assertions
catch catch -- Catch an exception
……
# 3、命令的具体使用方式
(gdb) help <command>
# 其中, 表示的是具体的一条命令
# 比如:
(gdb) help break
Set breakpoint at specified location.
break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
直接使用 gdb 指令启动 GDB 调试器,由于事先未指定要调试的具体程序,因此需启动后借助 file 或者 exec-file 命令指定
举例:
# 启动GDB调试器
gdb
# 然后指定可执行文件
file demo.exe # 路径自己选择
# 可以切换 GDB调试器所处的工作路径
cd /root/... # 可以自己更改路径
run 和 start 指令都可以用来在 GDB 调试器中启动程序,它们之间的区别是:
默认情况下,run
指令会一直执行程序,直到断点执行结束。如果程序中手动设置有断点,则 run
指令会执行程序至第一个断点处;
start
指令会执行程序至 main() 主函数的起始位置,即在 main() 函数的第一行语句处停止执行(该行代码尚未执行)。
程序执行过程中使用 run
或者 start
指令,表示的是重新启动程序。
调试尚未执行的程序
gdb 可执行文件名
调试正在执行的程序
如果需要使用 GDB 调试正在运行的 C、C++ 程序,需要事先找到该程序运行所对应的进程号。
pidof 可执行文件名 # 获得进程号
# 然后开始调试
gdb -p PID # PID就是进程号
注意,当 GDB 调试器成功连接到指定进程上时,程序执行会暂停,此时可以通过断点调试、逐步运行等方式监控程序的执行过程。
注意,当调试完成后,如果想令当前程序继续执行,消除调试操作对它的影响,需手动将 GDB
调试器与程序分离,分离过程分为 2 步:
1、执行 detach 指令,使 GDB 调试器和程序分离;
2、执行 quit(或 q)指令,退出 GDB 调试。
调试执行异常崩溃的程序
在 Linux 操作系统中,当程序执行发生异常崩溃时,系统可以将发生崩溃时的内存数据、调用堆栈情况等信息自动记录下载,并存储到一个文件中,该文件通常称为 core 文件,Linux 系统所具备的这种功能又称为核心转储(core dump)。幸运的是,GDB 对 core 文件的分析和调试提供有非常强大的功能支持,当程序发生异常崩溃时,通过 GDB 调试产生的 core 文件,往往可以更快速的解决问题。
默认情况下,Linux 系统是不开启 core dump 这一功能的,可以用以下指令查看
# 输入ulimit -a 指令
ulimit -a
# 输出
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
......
其中,如果 core file size(core 文件大小)对应的值为 0,表示当前系统未开启 core dump 功能,可以使用下面的 指令开启 core dump 功能
ulimit -c unlimited # 开启 core dump 功能
# 查看是否开启成功
ulimit -a
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
......
举例:
# 编译一个源程序,出现了错误
gcc main.c -o main.exe -g
./main.exe
# 下面是报错信息
Segmentation fault (core dumped) <--发生段错误,并生成了 core 文件
# 然后系统主动把报错信息 存储了下来
ls # 可以查看路径里面的内容,多了 core 文件
core main.c main.exe
对于 core 文件的调试,其调用 GDB 调试器的指令为:
# 输入这个指令
gdb main.exe core
# 下面就会有详细的报错信息,可以帮助调试程序
启动 GDB调试器可用的参数
参数 | 功能 |
---|---|
-pid number -p number |
调试进程 ID 为 number 的程序。 |
-symbols file -s file |
仅从指定 file 文件中读取符号表。 |
-q -quiet -silent |
取消启动 GDB 调试器时打印的介绍信息和版权信息 |
-cd directory | 以 directory 作为启动 GDB 调试器的工作目录,而非当前所在目录。 |
–args 参数1 参数2… | 向可执行文件传递执行所需要的参数。 |
1、break
命令的语法格式有:
1、(gdb) break location # b location
2、(gdb) break ... if cond # b ... if cond
location
用于指定打断点的具体位置,
...
可以是 1 中所有参数的值,用于指定打断点的具体位置;cond
为某个表达式。整体的含义为:每次程序执行到 ...
位置时都计算 cond
的值,如果为 True,则程序在该位置暂停;反之,程序继续执行。
2、tbreak
命令:tbreak
和 break
命令的用法和功能都非常相似,唯一的不同在于,使用 tbreak
命令打的断点仅会作用 1 次,即使程序暂停之后,该断点就会自动消失。
3、rbreak
命令:rbreak
命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点。和 break
命令打断点的效果是一样的,会一直存在,不会自动消失。
watch
命令的功能是:只有当被监控 **变量(或表达式)**的值发生改变,程序才会停止运行。
语法格式:
(gdb) watch cond # cond指的是要监控的变量或表达式
和 watch 命令功能相似的,还有 rwatch
和 awatch
命令。其中:
rwatch
命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;awatch
命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。捕捉断点的作用是,监控程序中某一事件的发生,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标时间发生,则程序停止执行。
(用捕捉断点监控某一事件的发生,等同于在程序中该事件发生的位置打普通断点。)
语法格式:
(gdb) catch event # event参数表示要监控的具体事件
next
命令:
单步调试的命令,其最大的特点是当遇到包含调用函数的语句时,无论函数内部包含多少行代码,next 指令都会一步执行完。也就是说,对于调用的函数来说,next 命令只会将其视作一行代码。
语法格式:
(gdb) next count # count 表示单步执行多少行代码,默认为 1 行
step
命令:
step 命令也是单步执行程序。但是 step
命令所执行的代码行中包含函数时,会进入该函数内部,并在函数第一行代码处停止执行,会一行一行执行函数内部的代码。
语法格式:
(gdb) step count # count 表示一次执行的行数,默认为 1 行
until
命令:
语法格式:
(gdb) until
(gdb) until location # 参数 location 为某一行代码的行号
不带参数的 until
命令,可以使 GDB 调试器快速运行完当前的循环体,并运行至循环体外停止。注意,until
命令并非任何情况下都会发挥这个作用,只有当执行至循环体尾部(最后一行代码)时,until 命令才会发生此作用;反之,until
命令和 next
命令的功能一样,只是单步执行程序。
until
命令还可以后跟某行代码的行号,以指示 GDB
调试器直接执行至指定位置后停止。
命令(缩写) | 功能 |
---|---|
run(r) | 启动或者重启一个程序。 |
list(l) | 显示带有行号的源码。 |
continue(c) | 让暂停的程序继续运行。 |
next(n) | 单步调试程序,即手动控制代码一行一行地执行。不进入函数内部 |
step(s) | 如果有调用函数,进入调用的函数内部;否则,和 next 命令的功能一样。 |
until(u) until location(u location) |
当你厌倦了在一个循环体内单步跟踪时,单纯使用 until 命令,可以运行程序直到退出循环体。 until n 命令中,n 为某一行代码的行号,该命令会使程序运行至第 n 行代码处停止。 |
finish(fi) | 结束当前正在执行的函数,并在跳出函数后暂停程序的执行。 |
return(return) | 结束当前调用函数并返回指定值,到上一层函数调用处停止程序执行。 |
jump(j) | 使程序从当前要执行的代码处,直接跳转到指定位置处继续执行后续的代码。 |
print(p) | 打印指定变量的值。 |
quit(q) | 退出 GDB 调试器。 |
1、print
命令,功能就是在 GDB 调试程序的过程中,输出或者修改指定变量或者表达式的值。
语法格式:
(gdb) print num
(gdb) p num
# 参数 num 用来代指要查看或者修改的目标变量或者表达式。
2、display
命令也用于调试阶段查看某个变量或表达式的值,它们的区别是,使用 display 命令查看变量或表达式的值,每当程序暂停执行(例如单步执行)时,GDB 调试器都会自动帮我们打印出来(每次都打印)
语法格式:
(gdb) display expr
(gdb) display/fmt expr
# expr 表示要查看的目标变量或表达式;参数fmt用于指定输出变量或表达式的格式
/fmt 常用的值
/fmt | 功能 |
---|---|
/x | 以十六进制的形式打印出整数。 |
/d | 以有符号、十进制的形式打印出整数。 |
/u | 以无符号、十进制的形式打印出整数。 |
/o | 以八进制的形式打印出整数。 |
/t | 以二进制的形式打印出整数。 |
/f | 以浮点数的形式打印变量或表达式的值。 |
/c | 以字符形式打印变量或表达式的值。 |
对于使用 display
命令查看的目标变量或表达式,都会被记录在一张列表(称为自动显示列表)中。通过执行 info dispaly
命令,可以打印出这张表。
GDB 调试器支持 3 种断点,分别为普通断点(用 break 命令创建)、观察断点(用 watch
命令建立)以及捕捉断点(用 catch
命令建立)。并且如果需要的话,我们可以在被调试程序中打多个断点,甚至于 GDB 允许在同一位置打多个断点。
如果之前建立的断点不再需要或者暂时不需要,该如何删除或者禁用呢?常用的方式有 2 种:
1、使用 quit
命令退出调试,然后重新对目标程序启动调试,此方法会将消除上一次调试操作中建立的所有断点;
2、使用专门删除或禁用断点的命令,既可以删除某一个断点,也可以删除全部断点。
查看当前已经建好的断点
(gdb) info breakpoint [n]
(gdb) info break [n]
# 参数 n 作为可选参数,为某个断点的编号,表示查看指定断点而非全部断点。
# 不加参数 n,表示查看所有断点
删除断点
clear
命令
(gdb) clear location
# 参数 location 通常为某一行代码的行号或者某个具体的函数名。当 location 参数为某个函数的函数名时,表示删除位于该函数入口处的所有断点。
delete
命令
(gdb) delete [breakpoints] [num]
# breakpoints 参数可有可无,num 参数为指定断点的编号,其可以是 delete 删除某一个断点,而非全部。
# 如果不指定 num 参数,则会删除当前程序中存在的所有断点
禁用断点
disable [breakpoints] [num...]
# breakpoints 参数可有可无;num... 表示可以有多个参数,每个参数都为要禁用断点的编号。如果指定 num...,disable 命令会禁用指定编号的断点;反之若不设定 num...,则 disable 会禁用当前程序中所有的断点。
激活断点
# 激活用 num... 参数指定的多个断点,如果不设定 num...,表示激活所有禁用的断点
enable [breakpoints] [num...]
# 临时激活以 num... 为编号的多个断点,但断点只能使用 1 次,之后会自动回到禁用状态
enable [breakpoints] once num…
# 临时激活以 num... 为编号的多个断点,断点可以使用 count 次,之后进入禁用状态
enable [breakpoints] count num...
# 激活 num.. 为编号的多个断点,但断点只能使用 1 次,之后会被永久删除。
enable [breakpoints] delete num…