gdb
基础
GDB (GNU Debugger) 是一个强大的调试工具,广泛用于调试C和C++程序。以下是GDB的基本使用教程,帮助你入门并有效地使用GDB进行调试。
安装GDB
在大多数Linux发行版中,你可以通过包管理器安装GDB。例如:
编译带调试信息的程序
在使用GDB调试程序之前,需要使用 -g
选项编译你的程序,以包含调试信息:
启动GDB
在终端中运行以下命令启动GDB并加载你的程序:
常用GDB命令
启动和运行程序
run
(或r
): 启动程序并运行到断点或程序结束。run [args]
: 运行程序并传递命令行参数。
设置断点
break [location]
(或b [location]
): 在指定位置设置断点。break main
: 在main
函数开始处设置断点。break myprogram.c:10
: 在myprogram.c
文件的第10行设置断点。
控制执行
continue
(或c
): 继续执行程序,直到下一个断点或程序结束。next
(或n
): 执行下一行源代码,不进入函数调用。step
(或s
): 执行下一行源代码,如果是函数调用则进入函数。finish
: 执行当前函数直到返回。
检查程序状态
print [expression]
(或p [expression]
): 打印表达式的值。print x
: 打印变量x
的值。
info locals
: 打印当前函数中的所有局部变量。backtrace
(或bt
): 打印当前的调用堆栈。list [location]
(或l [location]
): 显示源代码。list 10
: 显示第10行附近的源代码。
修改变量值
set var [variable] = [value]
: 修改变量的值。set var x = 10
: 将变量x
的值设置为10。
调试多线程程序
info threads
: 显示所有线程的信息。thread [num]
: 切换到指定线程。
退出GDB
quit
(或q
): 退出GDB。
示例调试流程
-
启动GDB并加载程序
-
设置断点
-
运行程序
-
查看变量值
-
单步执行代码
-
继续执行到下一个断点
-
查看调用堆栈
-
退出GDB
gdbinit
在使用GDB时,你可以通过.gdbinit
文件自动加载GDB命令,使每次启动GDB时自动执行这些命令。以下是配置和使用GDB自动加载命令的步骤:
创建 .gdbinit
文件
-
在当前目录创建
.gdbinit
文件:在你要调试的程序所在的目录下创建一个名为.gdbinit
的文件。如果你希望这些命令在所有GDB会话中都生效,可以在你的主目录中创建该文件。 -
编辑
.gdbinit
文件:使用你喜欢的文本编辑器打开.gdbinit
文件,并添加你希望自动加载的GDB命令。例如:
示例 .gdbinit
文件
以下是一个示例 .gdbinit
文件,它包含一些常用的GDB命令:
安全提示
GDB默认会自动加载当前目录下的 .gdbinit
文件,但为了安全起见,它不会自动加载主目录中的 .gdbinit
文件,除非你明确允许。你可以通过以下方式允许自动加载:
-
在GDB启动时使用
-x
选项指定加载.gdbinit
文件: -
修改GDB配置文件
~/.gdbinit
,允许加载主目录中的.gdbinit
文件:在
~/.gdbinit
文件中添加以下命令:
自动加载示例
假设你的主目录中有一个 .gdbinit
文件,内容如下:
你可以通过以下步骤确保GDB会自动加载并执行这些命令:
-
确保GDB配置允许加载主目录中的
.gdbinit
文件: -
启动GDB并加载程序:
break
break
命令用于设置断点,使程序在执行到特定位置时暂停
基本用法
-
在函数入口设置断点
例如:
-
在文件的特定行设置断点
例如:
-
在特定的行设置断点(当前文件)
例如:
高级用法
-
在条件满足时设置断点
例如:
-
在特定地址设置断点
例如:
断点管理
-
查看所有断点
-
删除断点
例如:
-
禁用断点
例如:
-
启用断点
例如:
-
启用断点
例如:
示例
以下是一些使用 break
命令的实际示例:
-
在
main
函数入口设置断点并运行程序 -
在
myfile.c
文件的第20行设置断点 -
在当前文件的第50行设置断点
-
在
myfunc
函数中,当变量x
等于10时设置断点 -
查看所有断点
-
删除第一个断点
-
禁用第二个断点
-
启用第二个断点
watch
watch
命令用于设置监视点,使程序在指定变量或内存位置的值发生变化时暂停执行。watch
命令非常有用,特别是在调试涉及复杂数据结构或需要监控变量变化的程序时。
基本用法
-
设置监视点
例如:
这将设置一个监视点,当变量
x
的值发生变化时,程序会暂停执行。 -
设置条件监视点
例如:
这将设置一个条件监视点,当变量
x
的值发生变化并且满足条件x > 10
时,程序会暂停执行。
其他类型的监视点
-
读取监视点
例如:
这将设置一个读取监视点,当变量
x
被读取时,程序会暂停执行。 -
访问监视点
例如:
这将设置一个访问监视点,当变量
x
被读取或写入时,程序会暂停执行。
管理监视点
-
查看所有监视点
-
删除监视点
例如:
-
禁用监视点
例如:
-
启用监视点
例如:
示例
以下是一些使用 watch
命令的实际示例:
-
设置变量
x
的监视点 -
设置变量
x
的条件监视点,当x > 10
时 -
设置变量
x
的读取监视点 -
设置变量
x
的访问监视点 -
查看所有监视点
-
删除第一个监视点
-
禁用第二个监视点
-
启用第二个监视点
info
info
命令用于显示调试过程中各种有用的信息,例如断点、变量、寄存器、线程等。
常用 info
命令
-
查看断点信息
显示当前所有断点的信息,包括断点编号、类型、使能状态、位置和命中次数。
-
查看本地变量
显示当前函数中的所有本地变量及其值。
-
查看程序状态
显示程序的当前状态,包括是否在运行、暂停位置等。
-
查看寄存器
显示所有CPU寄存器的内容。
-
查看线程
显示当前所有线程的信息,包括线程编号、状态和函数调用栈。
-
查看堆栈帧信息
显示当前堆栈帧的信息,包括函数名、源码文件、行号和参数值。
-
查看堆栈
显示当前的调用堆栈。
-
查看全局和静态变量
显示当前程序中的全局和静态变量。
-
查看源文件信息
显示已加载的源文件列表。
-
查看共享库信息
显示已加载的共享库的信息。
-
查看符号信息
显示已加载的所有函数符号。
显示指定符号的地址。
-
查看内存映射
显示当前进程的内存映射(仅在Linux上可用)。
-
查看线程库信息
显示当前所有线程的信息。
示例
以下是一些使用 info
命令的实际示例:
-
查看断点信息
-
查看当前函数中的本地变量
-
查看程序的当前状态
-
查看所有寄存器的内容
-
查看当前所有线程的信息
-
查看当前堆栈帧的信息
-
查看当前的调用堆栈
-
查看全局和静态变量
-
查看已加载的源文件列表
-
查看已加载的共享库信息
-
查看已加载的所有函数符号
-
显示指定符号的地址
-
查看当前进程的内存映射(仅在Linux上可用)
控制执行
- continue 设置忽略断点的次数
- next 执行多步
- step 执行多步
- finish 执行当前函数直到返回
print
命令用于显示表达式的值。该命令在调试过程中非常有用,因为它允许你检查变量、数组、结构体等的数据状态,从而更好地理解程序的运行情况和发现问题。
基本用法
-
打印变量值
例如:
这将显示变量
x
的当前值。 -
打印表达式的值
例如:
这将计算并显示表达式
x + y
的值。 -
打印指针的值和解引用
例如:
这将分别显示指针
ptr
的地址和值。
打印数组和结构体
-
打印数组
例如:
这将显示数组
arr
的所有元素。 -
打印结构体
例如:
这将显示结构体
p
的所有成员变量及其值。
打印指定格式
-
指定打印格式
格式选项包括:
d
:十进制x
:十六进制o
:八进制t
:二进制a
:地址c
:字符f
:浮点数
例如:
这将以十六进制格式显示变量
x
的值。
示例
假设你在调试一个简单的C程序:
在GDB中调试该程序并打印变量、数组和结构体的值:
-
打印变量
a
和b
的值 -
打印表达式
a + b
的值 -
打印数组
arr
的值 -
打印结构体
p
的值 -
打印变量
a
的十六进制值
backtrace
backtrace
命令用于显示当前调用堆栈(call stack),帮助你了解程序执行的路径。通过 backtrace
命令,你可以看到当前函数是由哪些函数调用的,以及这些调用链中的每个函数。
基本用法
-
显示完整调用堆栈
或者简写形式:
这将显示从当前函数开始的完整调用堆栈,包括每个栈帧的函数名、源文件名和行号。
带参数的用法
-
显示指定深度的调用堆栈
例如:
这将显示从当前栈帧开始的前3个栈帧。
-
显示从指定深度开始的调用堆栈
例如:
这将显示从第3个栈帧开始的5个栈帧。
示例
假设你在调试一个简单的C程序:
在GDB中调试该程序并查看调用堆栈:
-
在
foo
函数中设置断点并运行程序输出:
-
显示完整调用堆栈
输出:
这显示了当前栈帧(
foo
函数)和调用链中的其他函数(bar
和main
)。 -
显示前2个栈帧
输出:
-
显示从第1个栈帧开始的2个栈帧
输出:
list
list
命令用于显示源代码片段。你可以使用 list
命令查看当前调试位置附近的源代码,或者指定文件和行号、函数名等来查看特定位置的代码。
基本用法
-
显示当前执行位置的源代码
这将显示当前执行位置附近的源代码。
带参数的用法
-
指定行号
例如:
这将显示文件中第10行附近的源代码。
-
指定文件和行号
例如:
这将显示
myfile.c
文件中第10行附近的源代码。 -
指定函数
例如:
这将显示
main
函数的源代码。 -
显示下一部分源代码
再次使用
list
命令,将显示上一条list
命令之后的源代码。 -
显示指定范围的源代码
例如:
这将显示从第10行到第20行的源代码。
示例
假设你在调试一个简单的C程序:
在GDB中调试该程序并查看源代码:
-
显示当前执行位置的源代码
输出可能如下:
-
显示第15行附近的源代码
输出可能如下:
-
显示
main
函数的源代码输出可能如下:
-
显示
myfile.c
文件中第5行到第10行的源代码输出可能如下:
-
显示下一部分源代码
在运行
list
命令后,使用list
可以显示下一部分源代码:输出可能如下:
set
GDB 中的 set
命令用于修改调试器的内部状态、变量的值、设置断点的条件、调整调试选项等。set
命令非常灵活,涵盖了许多不同的用法。以下是一些常见的 set
命令及其用法示例:
基本用法
-
修改变量的值
例如:
这将把变量
x
的值设置为42
。 -
设置断点的条件
例如:
这将在
foo
函数设置一个断点,并在x
等于10
时触发。
调整调试选项
-
设置显示格式
例如:
这将限制打印数组时显示的元素数为 10 个。
-
设置调试信息
例如:
这将开启 GDB 的日志记录。
调整运行时参数
-
设置程序的命令行参数
例如:
这将设置程序运行时的命令行参数为
arg1
和arg2
。 -
设置环境变量
例如:
这将设置环境变量
PATH
为/usr/bin
。
设置调试器选项
-
设置自动显示源代码
例如:
这将设置每次显示源代码的行数为 20 行。
-
设置分页
例如:
这将关闭分页功能,使输出不再分页显示。
示例
假设你在调试一个简单的C程序:
在GDB中调试该程序并使用 set
命令:
-
修改变量
x
的值 -
设置断点的条件
-
设置命令行参数
-
设置环境变量
-
调整显示格式
-
开启日志记录
-
设置自动显示源代码的行数
-
关闭分页
thread
thread
命令用于调试多线程程序。通过 thread
命令,你可以查看和切换不同的线程,检查每个线程的状态,控制线程的执行等。
基本用法
-
查看所有线程
这将显示当前所有线程的信息,包括线程编号、状态和函数调用栈。
-
切换到指定线程
例如:
这将切换到编号为
2
的线程。 -
查看当前线程
这将显示当前线程的详细信息。
高级用法
-
对所有线程执行命令
例如:
这将对所有线程执行
bt
(backtrace)命令,显示每个线程的调用堆栈。 -
对特定线程执行命令
例如:
这将对编号为
2
的线程执行bt
命令,显示该线程的调用堆栈。
示例
假设你在调试一个多线程的C程序:
在GDB中调试该程序并查看和切换线程:
-
启动GDB并运行程序
-
查看所有线程
输出可能如下:
-
切换到编号为
2
的线程输出可能如下:
-
查看当前线程的调用堆栈
输出可能如下:
-
对所有线程执行
backtrace
命令输出可能如下: