GDB 常见用法
GDB QUICK REFERENCE 这个 PDF 中罗列了 GDB 常用的命令及其用法,推荐收藏。
启动 GDB
下面是一些启动 GDB 的实际例子:
# 启动可执行文件并执行调试
$ gdb ./a.out
# 调试正在运行的程序
$ gdb
(gdb) attach <pid>
# 根据 core 文件调试
$ gdb <program> <core>
# 启动调试时指定参数
$ gdb --args ./build/nutcracker --version
断点(break)
常规用法:
# 在函数 foo 处设置断点,break 可以简写为 b
(gdb) break foo
(gdb) b foo
# 在 foo.c 中的 foo 处设置断点
(gdb) b foo.c:foo
# 在 foo.c 中的第 100 行设置断点
(gdb) b foo.c:100
# 在当前位置后的第 10 行处设置断点
(gdb) b +10
# 在当前位置前的第 10 行处设置断点
(gdb) b -10
# 在当前源文件的第 100 行设置断点
(gdb) b 100
# 在指令地址处设置断点
# 下面的两行命令在 main 函数后 20 个字节处设置断点
(gdb) p main
$1 = {int (int, char **)} 0x47e300 <main>
(gdb) b *0x47e300 + 20
# 也可以写做
(gdb) b *main + 20
临时断点:
# 该断点进入一次后会自动删除,tbreak 可以简写为 tb
(gdb) tbreak foo
(gdb) tb foo
使用正则表达式设置断点:
# 在匹配 foo* 的函数处设置断点
(gdb) rbreak foo*
给断点增加条件:
# 条件成立时才进入断点
(gdb) b foo if num == 1001
# 也可以使用 condition 命令,语法为:
# condition <breakpoint_number> <condition>
# 如下命令给断点 1 增加条件
(gdb) condition 1 num == 101
# 也可以使用入门命令删除条件
(gdb) condition <breakpoint_number>
给 C++ 重载函数加断点:
因为 C++ 中存在函数重载,比如:
int add(int i,int j){...}
int add(float i,float j){...}
使用函数名加断点时,无法确定要加到那个函数上,此时可以使用如下命令:
(gdb) b add(int, int)
观察断点:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000047e300 in main at foo.c:1002
2 breakpoint keep y 0x000000000047e30a in main at foo.c:1002
3 breakpoint del y 0x000000000047e300 in main at foo.c:1002
删除断点:
# 删除编号为 1 的断点
(gdb) d 1
观察点(watch)
观察点用来观察某个表达式的值是否有变化了,如果有变化,马上停住程序。
# 表达式值有变化时,马上停住程序
(gdb) watch expr
# 当变量被读时,停住程序。
(gdb) rwatch var
# 变量被读写时,停住程序
(gdb) awatch var
# 列出所有的观察点
(gdb) info watchpoints
捕捉点
你可设置捕捉点来补捉程序运行时的一些事件,比如 C++的异常。
设置捕捉点的语法为:
catch $event
你可以查看帮助获取 event 的可取值:
(gdb) help catch
Set catchpoints to catch events.
List of catch subcommands:
catch assert -- Catch failed Ada assertions
catch catch -- Catch an exception
catch exception -- Catch Ada exceptions
catch exec -- Catch calls to exec
catch fork -- Catch calls to fork
catch handlers -- Catch Ada exceptions
catch load -- Catch loads of shared libraries
catch rethrow -- Catch an exception
catch signal -- Catch signals by their names and/or numbers
catch syscall -- Catch system calls by their names
catch throw -- Catch an exception
catch unload -- Catch unloads of shared libraries
catch vfork -- Catch calls to vfork
查看源码
启动 gdb 后希望能够看到当前程序的源码,这样才方便调试,此时可以使用 layout 命令。该命令可以帮你看到当前的源码、寄存器、汇编代码等。
layout [src|asm|split|regs]
# src : Displays source and command windows.
# asm : Displays disassembly and command windows.
# split : Displays source, disassembly and command windows.
# regs : Displays register window.
# 在 layout 窗口中按下 ctrl+x a 可以关闭窗口
控制执行
r # Runs the program until a breakpoint or error
c # Continues running the program until the next breakpoint or error
f # Runs until the current function is finished
s # Runs the next line of the program
s N # Runs the next N lines of the program
n # Like s, but it does not step into functions
u N # Runs until you get N lines in front of the current line
打印变量的值
print [Expression] # 打印一个表达式的值
p [Expression]
p {[Type]}[Address] # 将一个地址视为某种类型,然后打印
# 打印数组
print [First element]@[Element count]
# nums 是数组或者指针,下面打印前 10 个元素
print nums@10
# 以不同的格式打印
print /[Format] [Expression]
# Format 的取值和解释如下:
# o - octal
# x - hexadecimal
# u - unsigned decimal
# t - binary
# f - floating point
# a - address
# c - char
# s - string
watch <var> # 在 <var> 改变时打印此变量
display <var> # 在程序每次暂停时都自动打印 <var>
undisplay <var> # 取消 display
# 设置打印内容的最大长度
set print elements 100
查看堆栈情况
查看调用栈,在不同的函数帧中切换,可以方便地观察调用过程,以及不同栈帧中的变量。
bt # 查看当前调用栈
up # 跳到上一帧
down # 跳到下一帧
return # 返回当前函数帧
current # 查看当前帧
f n # 回到第 n 帧
线程
(gdb) info threads # 查看有那些线程
(gdb) t 2 # 切换至线程 2