GDB调试器完全指南

次浏览

概述

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/10x 0x20000000         # 10个16进制四字节
(gdb) x/20c &string            # 20个字符
(gdb) x/10i $pc                # 10条指令
(gdb) x/s string_ptr           # 字符串

# 查看指定地址
(gdb) x/16xb &variable         # variable的前16字节(十六进制)
(gdb) x/8hw &array             # array的前8个半字(2字节)

# 查看当前指令
(gdb) x/i $pc
(gdb) x/5i $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 栈帧信息

参考资料

使用 Hugo 构建
主题 StackJimmy 设计