参考GDB
特别好的一家网站,推荐食用。
查看GDB版本
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查看命令
启动 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> # 其中 <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> # 其中,<command> 表示的是具体的一条命令 # 比如: (gdb) help break Set breakpoint at specified location. break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [if CONDITION]
调用GDB调试器的四种方式
-
直接使用 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… 向可执行文件传递执行所需要的参数。
设置普通断点(break)
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)
watch
命令的功能是:只有当被监控 **变量(或表达式)**的值发生改变,程序才会停止运行。
语法格式:
(gdb) watch cond # cond指的是要监控的变量或表达式
和 watch 命令功能相似的,还有 rwatch
和 awatch
命令。其中:
rwatch
命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;awatch
命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。
设置捕捉断点(catch)
捕捉断点的作用是,监控程序中某一事件的发生,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标时间发生,则程序停止执行。
(用捕捉断点监控某一事件的发生,等同于在程序中该事件发生的位置打普通断点。)
语法格式:
(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 调试器。 |
GDB print和display命令:查看变量的值
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禁用和删除断点
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…