概述
GDB(GNU Debugger)是 GNU 项目开发的标准调试器,支持 C、C++、Go、Rust 等多种语言。掌握 GDB 是 Linux 下开发的必备技能。
一、GDB 基本使用
1.1 启动 GDB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 调试可执行文件
gdb ./program
# 调试带参数的程序
gdb --args ./program arg1 arg2
# 附加到正在运行的进程
gdb -p <PID>
# 调试 core 文件
gdb ./program core
# 静默启动(不显示欢迎信息)
gdb -q ./program
1.2 编译时加入调试信息
1
2
3
4
5
6
7
8
# GCC/G++ 编译时添加 -g 选项
gcc -g -o program program.c
# 推荐关闭优化,否则调试体验差
gcc -g -O0 -o program program.c
# 使用 gdwarf-4 格式(兼容性更好)
gcc -g -gdwarf-4 -o program program.c
1.3 GDB 内部命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 退出 GDB
(gdb) quit
(gdb) q
# 执行 shell 命令
(gdb) shell ls -la
(gdb) !ls -la
# 清屏
(gdb) shell clear
# 帮助
(gdb) help
(gdb) help breakpoints
(gdb) help break
二、程序执行控制
2.1 运行程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 运行程序
(gdb) run
(gdb) r
# 带参数运行
(gdb) run arg1 arg2 arg3
# 重新运行(会先终止当前进程)
(gdb) run
# 启动程序但停在第一行
(gdb) start
# 启动并停在 main 函数
(gdb) starti # 停在第一条指令
2.2 单步执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 单步执行(进入函数)
(gdb) step
(gdb) s
# 单步执行(不进入函数)
(gdb) next
(gdb) n
# 执行指定步数
(gdb) next 10
(gdb) step 5
# 执行到当前函数返回
(gdb) finish
# 执行到指定行
(gdb) until 50
(gdb) until func_name
# 继续执行到下一个断点
(gdb) continue
(gdb) c
# 继续执行指定次数(跳过当前断点 n 次)
(gdb) continue 5
2.3 执行汇编级单步
1
2
3
4
5
6
7
8
9
10
# 汇编级单步(进入函数)
(gdb) stepi
(gdb) si
# 汇编级单步(不进入函数)
(gdb) nexti
(gdb) ni
# 显示当前汇编指令
(gdb) display/i $pc
三、断点管理
3.1 设置断点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 在函数处设置断点
( gdb ) break main
( gdb ) b main
# 在文件:行号处设置断点
( gdb ) break file . c : 50
( gdb ) b file . c : 50
# 在当前文件的行号处设置断点
( gdb ) break 100
# 在地址处设置断点
( gdb ) break * 0x08048500
# 条件断点
( gdb ) break main if x > 10
( gdb ) b func if i == 5
# 设置临时断点(触发一次后自动删除)
( gdb ) tbreak main
# 设置硬件断点
( gdb ) hbreak main
# 设置读断点(数据断点)
( gdb ) rwatch variable
# 设置写断点
( gdb ) watch variable
# 设置读写断点
( gdb ) awatch variable
3.2 查看断点
1
2
3
4
5
6
7
8
# 查看所有断点
(gdb) info breakpoints
(gdb) info b
(gdb) i b
# 查看特定断点
(gdb) info breakpoints 1
(gdb) info breakpoints 1-3
3.3 管理断点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 禁用断点
(gdb) disable 1
(gdb) disable 1-3
# 启用断点
(gdb) enable 1
(gdb) enable 1-3
# 启用断点一次(触发后自动禁用)
(gdb) enable once 1
# 启用断点并在触发后删除
(gdb) enable delete 1
# 删除断点
(gdb) delete 1
(gdb) delete 1-3
(gdb) delete # 删除所有断点
# 清除断点(按位置)
(gdb) clear main
(gdb) clear file.c:50
# 设置断点忽略次数
(gdb) ignore 1 10 # 忽略断点1的前10次触发
3.4 断点命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 为断点添加命令(触发断点时自动执行)
(gdb) commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>print x
>print y
>continue
>end
# 示例:自动打印变量并继续
(gdb) b func
(gdb) commands
>printf "x = %d, y = %d\n", x, y
>continue
>end
四、查看变量与内存
4.1 打印变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 打印变量值
( gdb ) print variable
( gdb ) p variable
# 打印表达式
( gdb ) print x + y
( gdb ) print array [ 0 ] + array [ 1 ]
# 打印数组
( gdb ) print array
( gdb ) print array [ 0 ] @ 10 # 打印数组前10个元素
( gdb ) print * array @ 10 # 打印指针指向的10个元素
# 打印字符串
( gdb ) print string
( gdb ) print ( char * ) string
# 打印指定长度字符串
( gdb ) print ( char [ 20 ]) string
# 打印结构体
( gdb ) print struct_var
( gdb ) print struct_var -> member
# 指定格式打印
( gdb ) print / x var # 十六进制
( gdb ) print / d var # 十进制
( gdb ) print / u var # 无符号十进制
( gdb ) print / o var # 八进制
( gdb ) print / t var # 二进制
( gdb ) print / c var # 字符
( gdb ) print / f var # 浮点数
( gdb ) print / a var # 地址
# 打印类型信息
( gdb ) ptype variable
( gdb ) ptype struct_name
# 打印变量大小
( gdb ) print sizeof ( variable )
( gdb ) print sizeof ( struct_name )
4.2 显示变量(自动打印)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 每次停止时自动显示变量
( gdb ) display variable
( gdb ) display / x variable # 十六进制显示
# 查看所有 display
( gdb ) info display
# 禁用 display
( gdb ) disable display 1
# 启用 display
( gdb ) enable display 1
# 删除 display
( gdb ) undisplay 1
( gdb ) delete display 1
4.3 查看内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查看内存
# x/nfu addr
# n: 显示单元数量
# f: 显示格式 (x/d/u/o/t/c/f/s)
# u: 单元大小 (b=1字节, h=2字节, w=4字节, g=8字节)
( gdb ) x / 10 x 0x20000000 # 10个16进制四字节
( gdb ) x / 20 c & string # 20个字符
( gdb ) x / 10 i $ pc # 10条指令
( gdb ) x / s string_ptr # 字符串
# 查看指定地址
( gdb ) x / 16 xb & variable # variable的前16字节(十六进制)
( gdb ) x / 8 hw & array # array的前8个半字(2字节)
# 查看当前指令
( gdb ) x / i $ pc
( gdb ) x / 5 i $ pc # 当前位置开始的5条指令
4.4 查看寄存器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看所有寄存器
(gdb) info registers
(gdb) i r
# 查看特定寄存器
(gdb) info registers rax rbx
(gdb) print $rax
(gdb) print $pc
(gdb) print $sp
# 查看浮点寄存器
(gdb) info float
# 查看向量寄存器
(gdb) info vector
五、栈帧操作
5.1 查看调用栈
1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看调用栈
(gdb) backtrace
(gdb) bt
# 查看完整调用栈
(gdb) backtrace full
(gdb) bt full
# 查看指定层数
(gdb) backtrace 5 # 只显示5层
# 查看所有线程的调用栈
(gdb) thread apply all backtrace
5.2 切换栈帧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看当前栈帧
(gdb) frame
(gdb) info frame
# 切换到指定栈帧
(gdb) frame 2
(gdb) f 2
# 向上移动(调用者方向)
(gdb) up
(gdb) up 2
# 向下移动(被调用者方向)
(gdb) down
(gdb) down 2
# 查看栈帧信息
(gdb) info frame 2
(gdb) info args # 当前函数参数
(gdb) info locals # 当前函数局部变量
5.3 栈帧图示
1
2
3
4
5
6
7
8
#0 func2 (x=10) at file2.c:25
#1 0x08048500 in func1 (y=20) at file1.c:50
#2 0x08048600 in main () at main.c:100
↑
当前栈帧 (#0)
up 命令移动方向 ←
→ down 命令移动方向
六、源码查看
6.1 查看源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 显示当前行附近的代码
(gdb) list
(gdb) l
# 显示指定行
(gdb) list 50
(gdb) list main.c:50
# 显示指定函数
(gdb) list main
(gdb) list file.c:func
# 显示指定范围
(gdb) list 10,30
(gdb) list main.c:10,main.c:30
# 向前/向后翻页
(gdb) list - # 向前
(gdb) list + # 向后
# 设置每次显示的行数
(gdb) set listsize 20
(gdb) show listsize
6.2 源码搜索
1
2
3
4
5
6
# 向前搜索
(gdb) forward-search pattern
(gdb) search pattern
# 向后搜索
(gdb) reverse-search pattern
6.3 反汇编
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 反汇编当前函数
(gdb) disassemble
(gdb) disas
# 反汇编指定函数
(gdb) disassemble main
# 反汇编指定范围
(gdb) disassemble 0x08048500,0x08048600
# 混合源码和汇编
(gdb) disassemble /m main
# 混合源码和汇编(带行号)
(gdb) disassemble /s main
七、修改变量与内存
7.1 修改变量
1
2
3
4
5
6
7
8
9
10
11
# 设置变量值
( gdb ) set variable = 100
( gdb ) set x = 10
( gdb ) set ptr -> member = 5
# 修改数组元素
( gdb ) set array [ 0 ] = 100
# 使用表达式
( gdb ) set x = x + 10
( gdb ) set x += 5
7.2 修改内存
1
2
3
4
5
6
7
# 修改内存
(gdb) set {int}0x20000000 = 100
(gdb) set {char[10]}0x20000000 = "hello"
# 修改寄存器
(gdb) set $rax = 0
(gdb) set $pc = 0x08048500
7.3 跳转执行
1
2
3
4
5
6
# 跳转到指定行
(gdb) jump 50
(gdb) jump file.c:50
# 跳转到指定地址
(gdb) jump *0x08048500
八、多线程调试
8.1 查看线程
1
2
3
4
5
6
7
# 查看所有线程
(gdb) info threads
# 输出示例:
# Id Target Id Frame
# * 1 Thread 0x7ffff7fd0740 (LWP 12345) "program" main () at main.c:10
# 2 Thread 0x7ffff6ffb700 (LWP 12346) "program" 0x00007ffff7bc64e0 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/x86_64-linux-gnu/libpthread.so.0
8.2 切换线程
1
2
3
4
5
6
7
8
9
# 切换到指定线程
(gdb) thread 2
# 在所有线程执行命令
(gdb) thread apply all info registers
(gdb) thread apply all backtrace
# 在指定线程执行命令
(gdb) thread apply 1-3 info locals
8.3 线程断点
1
2
3
4
5
6
# 设置特定线程的断点
(gdb) break main thread 2
(gdb) break main thread 2 if x > 10
# 设置线程特定断点
(gdb) break file.c:50 thread 2
8.4 线程调度锁定
1
2
3
4
5
6
7
# 设置调度模式
(gdb) set scheduler-locking on # 只运行当前线程
(gdb) set scheduler-locking off # 所有线程都可运行
(gdb) set scheduler-locking step # 单步时只运行当前线程
# 查看当前模式
(gdb) show scheduler-locking
九、多进程调试
9.1 follow-fork 模式
1
2
3
4
5
6
7
# 设置 fork 后跟踪的进程
(gdb) set follow-fork-mode parent # 跟踪父进程(默认)
(gdb) set follow-fork-mode child # 跟踪子进程
# 查看 fork 后是否分离另一个进程
(gdb) set detach-on-fork on # 分离(默认)
(gdb) set detach-on-fork off # 不分离,可以调试两个进程
9.2 查看被调试的进程
1
2
3
4
5
# 查看所有被调试的进程
(gdb) info inferiors
# 切换进程
(gdb) inferior 2
9.3 进程命令
1
2
3
4
5
6
7
8
9
10
11
# 添加一个被调试的进程
(gdb) add-inferior
# 克隆当前进程的调试设置
(gdb) clone-inferior
# 分离进程
(gdb) detach inferior 2
# 杀死进程
(gdb) kill inferior 2
十、观察点(Watchpoint)
10.1 设置观察点
1
2
3
4
5
6
7
8
9
# 写观察点(变量被修改时触发)
( gdb ) watch variable
( gdb ) watch * ( int * ) 0x20000000
# 读观察点(变量被读取时触发)
( gdb ) rwatch variable
# 读写观察点
( gdb ) awatch variable
10.2 观察点管理
1
2
3
4
5
6
7
8
# 查看观察点
( gdb ) info watchpoints
# 删除观察点
( gdb ) delete watch 1
# 条件观察点
( gdb ) watch variable if variable > 100
10.3 观察点示例
1
2
3
4
5
6
7
8
9
10
11
# 调试变量被谁修改的问题
( gdb ) watch x
Hardware watchpoint 1 : x
( gdb ) continue
Continuing .
Hardware watchpoint 1 : x
Old value = 0
New value = 100
func () at main . c : 10
10 x = 100 ;
十一、信号处理
11.1 查看信号处理
1
2
3
4
5
6
7
8
# 查看信号处理设置
( gdb ) info signals
( gdb ) info handle
# 输出示例:
# Signal Stop Print Pass to program Description
# SIGINT Yes Yes No Interrupt
# SIGSEGV Yes Yes Yes Segmentation fault
11.2 设置信号处理
1
2
3
4
5
6
7
8
9
# 设置信号处理方式
# handle signal action...
# stop/nostop: 收到信号时是否停止
# print/noprint: 是否打印信号信息
# pass/nopass: 是否传递给程序
( gdb ) handle SIGINT stop print pass
( gdb ) handle SIGSEGV stop print
( gdb ) handle SIGUSR1 nostop noprint pass
11.3 发送信号
1
2
3
4
5
6
7
# 向程序发送信号
( gdb ) signal SIGUSR1
# 继续执行并发送信号
( gdb ) continue
# Ctrl+C 中断
( gdb ) signal SIGCONT
十二、核心转储(Core Dump)
12.1 生成 Core Dump
1
2
3
4
5
6
7
8
# 启用 core dump
ulimit -c unlimited
# 设置 core dump 格式
echo "/tmp/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern
# 运行程序(崩溃时自动生成 core 文件)
./program
12.2 调试 Core Dump
1
2
3
4
5
6
7
8
# 使用 GDB 调试 core 文件
gdb ./program core.12345
# 在 GDB 中
( gdb) bt # 查看崩溃时的调用栈
( gdb) info registers # 查看寄存器状态
( gdb) frame 0 # 切换到崩溃帧
( gdb) info locals # 查看局部变量
12.3 Core Dump 分析流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 查看崩溃位置
(gdb) bt
# 2. 切换到崩溃帧
(gdb) frame 0
# 3. 查看源码
(gdb) list
# 4. 查看变量
(gdb) info locals
(gdb) info args
# 5. 分析原因
(gdb) print pointer
(gdb) x/10x pointer
十三、远程调试
13.1 启动 GDB Server
1
2
3
4
5
# 在目标机上启动 gdbserver
gdbserver :1234 ./program
# 附加到进程
gdbserver :1234 --attach <PID>
13.2 连接远程调试
1
2
3
4
5
6
# 在主机上连接
(gdb) target remote 192.168.1.100:1234
# 连接后正常使用 GDB 命令
(gdb) break main
(gdb) continue
13.3 常用远程调试命令
1
2
3
4
5
6
7
8
# 查看远程目标信息
(gdb) info target
# 断开连接
(gdb) disconnect
# 重新连接
(gdb) target remote :1234
十四、脚本与自动化
14.1 GDB 初始化文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# ~/.gdbinit 文件
# 设置启动信息
set pagination off
set confirm off
# 历史记录
set history save on
set history size 10000
# 美化打印
set print pretty on
set print array on
set print array-indexes on
# 定义命令
define plist
set $node = $arg0
while $node
print *$node
set $node = $node ->next
end
end
document plist
Print a linked list starting from the given node.
Usage: plist head_pointer
end
# 添加源码路径
directory /path/to/source
14.2 自定义命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 定义命令
( gdb ) define print_status
Type commands for definition of " print_status " .
End with a line saying just " end " .
> printf " Current status : % d \ n " , status
> printf " Error code : % d \ n " , error_code
> end
# 添加文档
( gdb ) document print_status
Print the current status and error code .
end
# 使用自定义命令
( gdb ) print_status
14.3 条件断点技巧
1
2
3
4
5
6
7
8
9
10
11
# 复杂条件断点
( gdb ) b func if strcmp ( name , "test" ) == 0
# 调用函数作为条件
( gdb ) b func if check_valid ( ptr )
# 打印并继续
( gdb ) commands
> printf "x= %d , y= %d \n " , x , y
> continue
> end
14.4 执行脚本文件
1
2
3
4
5
# 执行脚本文件
(gdb) source debug.gdb
# 命令行指定
gdb -x debug.gdb ./program
十五、实用技巧
15.1 TUI 模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 启动 TUI 模式
gdb -tui ./program
# 在 GDB 中切换 TUI
( gdb) tui enable
( gdb) tui disable
# TUI 窗口操作
( gdb) layout src # 源码窗口
( gdb) layout asm # 汇编窗口
( gdb) layout split # 源码+汇编
( gdb) layout regs # 寄存器窗口
# 切换焦点
( gdb) focus src
( gdb) focus cmd
( gdb) focus regs
# 刷新窗口
( gdb) refresh
15.2 Python 扩展
1
2
3
4
5
6
7
8
9
10
11
12
13
# GDB 内置 Python 解释器
(gdb) python print("Hello")
# 定义 Python 命令
(gdb) python
>class PrintList(gdb.Command):
> def __init__(self):
> super().__init__("plist", gdb.COMMAND_DATA)
> def invoke(self, arg, from_tty):
> # ... 实现逻辑
> pass
>PrintList()
>end
15.3 调试优化代码
1
2
3
4
5
6
7
8
# 显示优化后的变量位置
(gdb) info locals
# 使用 $pc 查找位置
(gdb) x/10i $pc
# 查看寄存器中的值
(gdb) info registers
15.4 常用命令别名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 在 .gdbinit 中定义别名
define bpl
info breakpoints
end
define bpc
delete $arg0
end
define bpe
enable $arg0
end
define bpd
disable $arg0
end
十六、常用命令速查表
16.1 程序控制
命令
简写
说明
run
r
运行程序
start
启动并停在 main
continue
c
继续执行
next
n
单步(不进入函数)
step
s
单步(进入函数)
finish
执行到函数返回
until
u
执行到指定行
quit
q
退出 GDB
16.2 断点
命令
简写
说明
break
b
设置断点
tbreak
临时断点
info breakpoints
i b
查看断点
delete
d
删除断点
disable
禁用断点
enable
启用断点
watch
设置观察点
condition
设置条件
16.3 查看信息
命令
简写
说明
print
p
打印变量
display
自动显示
x
查看内存
list
l
查看源码
backtrace
bt
调用栈
info registers
i r
寄存器
info locals
局部变量
info args
函数参数
ptype
类型信息
16.4 栈帧操作
命令
简写
说明
frame
f
切换栈帧
up
上一帧
down
下一帧
info frame
栈帧信息
参考资料
Licensed under CC BY-NC-SA 4.0