第5章 GDB调试详解
完整 485 页的 《编译与调试完全指南》见: https://github.com/lengjingzju/notes/releases/tag/v0.0.2
GDB(GNU Debugger)是 Linux 下最强大的源代码级调试器,支持断点设置、单步执行、变量查看、内存检查、多线程/多进程调试、远程调试等核心功能。掌握 GDB 是进行嵌入式开发、系统编程和问题定位的必备技能。本章按照"启动连接→执行控制→断点观察→数据操作→堆栈分析→高级调试→实战场景"的逻辑组织,覆盖 GDB 的完整知识体系。
#mermaid-svg-MZnCtvDqiINSb60U{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MZnCtvDqiINSb60U .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MZnCtvDqiINSb60U .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MZnCtvDqiINSb60U .error-icon{fill:#552222;}#mermaid-svg-MZnCtvDqiINSb60U .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MZnCtvDqiINSb60U .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MZnCtvDqiINSb60U .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MZnCtvDqiINSb60U .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MZnCtvDqiINSb60U .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MZnCtvDqiINSb60U .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MZnCtvDqiINSb60U .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MZnCtvDqiINSb60U .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MZnCtvDqiINSb60U .marker.cross{stroke:#333333;}#mermaid-svg-MZnCtvDqiINSb60U svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MZnCtvDqiINSb60U p{margin:0;}#mermaid-svg-MZnCtvDqiINSb60U .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MZnCtvDqiINSb60U .cluster-label text{fill:#333;}#mermaid-svg-MZnCtvDqiINSb60U .cluster-label span{color:#333;}#mermaid-svg-MZnCtvDqiINSb60U .cluster-label span p{background-color:transparent;}#mermaid-svg-MZnCtvDqiINSb60U .label text,#mermaid-svg-MZnCtvDqiINSb60U span{fill:#333;color:#333;}#mermaid-svg-MZnCtvDqiINSb60U .node rect,#mermaid-svg-MZnCtvDqiINSb60U .node circle,#mermaid-svg-MZnCtvDqiINSb60U .node ellipse,#mermaid-svg-MZnCtvDqiINSb60U .node polygon,#mermaid-svg-MZnCtvDqiINSb60U .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MZnCtvDqiINSb60U .rough-node .label text,#mermaid-svg-MZnCtvDqiINSb60U .node .label text,#mermaid-svg-MZnCtvDqiINSb60U .image-shape .label,#mermaid-svg-MZnCtvDqiINSb60U .icon-shape .label{text-anchor:middle;}#mermaid-svg-MZnCtvDqiINSb60U .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MZnCtvDqiINSb60U .rough-node .label,#mermaid-svg-MZnCtvDqiINSb60U .node .label,#mermaid-svg-MZnCtvDqiINSb60U .image-shape .label,#mermaid-svg-MZnCtvDqiINSb60U .icon-shape .label{text-align:center;}#mermaid-svg-MZnCtvDqiINSb60U .node.clickable{cursor:pointer;}#mermaid-svg-MZnCtvDqiINSb60U .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MZnCtvDqiINSb60U .arrowheadPath{fill:#333333;}#mermaid-svg-MZnCtvDqiINSb60U .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MZnCtvDqiINSb60U .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MZnCtvDqiINSb60U .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MZnCtvDqiINSb60U .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MZnCtvDqiINSb60U .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MZnCtvDqiINSb60U .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MZnCtvDqiINSb60U .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MZnCtvDqiINSb60U .cluster text{fill:#333;}#mermaid-svg-MZnCtvDqiINSb60U .cluster span{color:#333;}#mermaid-svg-MZnCtvDqiINSb60U div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MZnCtvDqiINSb60U .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MZnCtvDqiINSb60U rect.text{fill:none;stroke-width:0;}#mermaid-svg-MZnCtvDqiINSb60U .icon-shape,#mermaid-svg-MZnCtvDqiINSb60U .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MZnCtvDqiINSb60U .icon-shape p,#mermaid-svg-MZnCtvDqiINSb60U .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MZnCtvDqiINSb60U .icon-shape .label rect,#mermaid-svg-MZnCtvDqiINSb60U .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MZnCtvDqiINSb60U .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MZnCtvDqiINSb60U .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MZnCtvDqiINSb60U :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GDB核心功能
启动与连接
程序执行控制
断点与观察点
数据查看与修改
堆栈分析
多线程调试
多进程调试
信号处理
源码与反汇编
自定义命令与脚本
5.1 GDB启动与连接
本节介绍 GDB 的各种启动方式和连接模式,包括本地调试、附加进程、交叉调试和 Coredump 调试。选择合适的启动方式是调试的第一步。
5.1.1 GDB启动参数(控制调试会话初始化)
GDB 提供了丰富的启动参数,用于控制调试会话的初始化行为。使用 gdb --help 可查看所有启动参数的详细说明。
常用选项:
| 选项 | 全称 | 说明 |
|---|---|---|
-s <file> |
--symbols=<file> |
从指定文件中读取符号表 |
-se <file> |
从指定文件中读取符号表信息,并把它用在可执行文件中 | |
-c <file> |
--core=<file> |
调试时 core dump 的 core 文件 |
-d <directory> |
--directory=<directory> |
加入一个源文件的搜索路径(默认搜索路径是环境变量 PATH 所定义的路径) |
-nx |
不执行 .gdbinit 初始化文件 |
|
-nh |
不执行家目录的 .gdbinit |
|
-p <pid> |
--pid=<pid> |
绑定到运行中的进程 |
-x <file> |
执行指定的命令文件 | |
-q |
--quiet |
不显示欢迎信息 |
-batch |
批处理模式,执行完命令后自动退出 | |
-ex <command> |
启动时执行指定 GDB 命令 |
实践示例:
bash
# 加载符号表和可执行文件
$ gdb -se program
# 加载 core dump 文件进行事后调试
$ gdb -c core.12345
# 添加额外的源文件搜索路径
$ gdb -d /home/user/src -d /opt/project/src program
# 不加载 .gdbinit(排除配置干扰)
$ gdb -nx program
# 直接附加到运行中的进程
$ gdb -p 12345
# 批处理模式:启动后执行命令并退出
$ gdb -batch -ex "bt" -ex "quit" ./program core
# 启动时直接执行命令
$ gdb -ex "set args 3 5" -ex "run" ./myop
.gdbinit 配置文件说明:
如果存在 .gdbinit,GDB 就会在启动之前将其作为命令文件运行。.gdbinit 可能存在于以下位置:
| 位置 | 加载顺序 | 说明 |
|---|---|---|
系统级(/etc/gdb/gdbinit) |
最先 | 系统全局配置(需编译时启用) |
家目录(~/.config/gdb/gdbinit 或 ~/.gdbinit) |
其次 | 全局个人配置 |
当前目录(./.gdbinit) |
再次 | 项目级配置(需安全授权) |
-x 选项指定的文件 |
最后 | 命令行指定 |
5.1.2 本地调试启动(调试本机程序)
调试可执行程序:
bash
# 启动 gdb 调试可执行文件
$ gdb <program>
# 进入 gdb 后设置运行参数
(gdb) set args <arg1> <arg2> ...
# 查看运行参数
(gdb) show args
# 开始运行
(gdb) run
# 运行到 main 函数暂停(比 run 更方便)
(gdb) start
实践示例(使用第 1 章的 myop 程序):
bash
$ gdb ./myop
(gdb) set args 3 5
(gdb) run
Starting program: /home/user/myop 3 5
3 + 5 = 8
3 - 5 = -2
3 * 5 = 15
3 / 5 = 0
3 ^ 5 = 243.000000
[Inferior 1 (process 12345) exited normally]
调试运行中的进程(3种方式):
| 方式 | 命令 | 示例 |
|---|---|---|
方式1:通过 --pid 参数 |
gdb --pid <pid_num> |
sudo gdb --pid 1620 |
| 方式2:通过程序和进程号绑定 | gdb <program> <pid_num> |
gdb test 3095 |
方式3:在 GDB 中使用 attach |
(gdb) attach <pid_num> |
先进入 gdb,再 attach |
bash
# 方式1:直接通过 PID 附加
$ sudo gdb --pid 1620
# 方式2:指定程序和 PID
$ gdb test 3095
# 方式3:先启动 gdb,再 attach
$ gdb
(gdb) attach 3095
Attaching to process 3095
...
# 取消挂接(detach 后进程继续运行)
(gdb) detach
# 继续运行被调试的程序
(gdb) continue
易错点:
| 易错点 | 说明 |
|---|---|
| attach 后程序暂停 | attach 后程序处于暂停状态,需要 continue 才能继续运行 |
| detach 后进程恢复 | detach 后 GDB 不再控制该进程,进程恢复正常运行 |
| 权限问题 | 调试其他用户的进程通常需要 sudo 权限 |
| ptrace 限制 | Ubuntu 默认 ptrace_scope=1,只能调试子进程,需修改为 0 |
5.1.3 交叉调试(远程目标板调试)
交叉调试用于在主机(Host)上调试目标板(Target)上运行的程序,是嵌入式开发的核心调试方式。
前提条件: 目标板文件系统中必须有 gdbserver 工具。gdbserver 负责与远程的 GDB 进行通讯并控制本地的应用程序执行。
#mermaid-svg-yHV5LzDlEK13vfob{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yHV5LzDlEK13vfob .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yHV5LzDlEK13vfob .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yHV5LzDlEK13vfob .error-icon{fill:#552222;}#mermaid-svg-yHV5LzDlEK13vfob .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yHV5LzDlEK13vfob .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yHV5LzDlEK13vfob .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yHV5LzDlEK13vfob .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yHV5LzDlEK13vfob .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yHV5LzDlEK13vfob .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yHV5LzDlEK13vfob .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yHV5LzDlEK13vfob .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yHV5LzDlEK13vfob .marker.cross{stroke:#333333;}#mermaid-svg-yHV5LzDlEK13vfob svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yHV5LzDlEK13vfob p{margin:0;}#mermaid-svg-yHV5LzDlEK13vfob .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yHV5LzDlEK13vfob .cluster-label text{fill:#333;}#mermaid-svg-yHV5LzDlEK13vfob .cluster-label span{color:#333;}#mermaid-svg-yHV5LzDlEK13vfob .cluster-label span p{background-color:transparent;}#mermaid-svg-yHV5LzDlEK13vfob .label text,#mermaid-svg-yHV5LzDlEK13vfob span{fill:#333;color:#333;}#mermaid-svg-yHV5LzDlEK13vfob .node rect,#mermaid-svg-yHV5LzDlEK13vfob .node circle,#mermaid-svg-yHV5LzDlEK13vfob .node ellipse,#mermaid-svg-yHV5LzDlEK13vfob .node polygon,#mermaid-svg-yHV5LzDlEK13vfob .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yHV5LzDlEK13vfob .rough-node .label text,#mermaid-svg-yHV5LzDlEK13vfob .node .label text,#mermaid-svg-yHV5LzDlEK13vfob .image-shape .label,#mermaid-svg-yHV5LzDlEK13vfob .icon-shape .label{text-anchor:middle;}#mermaid-svg-yHV5LzDlEK13vfob .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yHV5LzDlEK13vfob .rough-node .label,#mermaid-svg-yHV5LzDlEK13vfob .node .label,#mermaid-svg-yHV5LzDlEK13vfob .image-shape .label,#mermaid-svg-yHV5LzDlEK13vfob .icon-shape .label{text-align:center;}#mermaid-svg-yHV5LzDlEK13vfob .node.clickable{cursor:pointer;}#mermaid-svg-yHV5LzDlEK13vfob .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yHV5LzDlEK13vfob .arrowheadPath{fill:#333333;}#mermaid-svg-yHV5LzDlEK13vfob .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yHV5LzDlEK13vfob .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yHV5LzDlEK13vfob .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yHV5LzDlEK13vfob .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yHV5LzDlEK13vfob .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yHV5LzDlEK13vfob .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yHV5LzDlEK13vfob .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yHV5LzDlEK13vfob .cluster text{fill:#333;}#mermaid-svg-yHV5LzDlEK13vfob .cluster span{color:#333;}#mermaid-svg-yHV5LzDlEK13vfob div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yHV5LzDlEK13vfob .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yHV5LzDlEK13vfob rect.text{fill:none;stroke-width:0;}#mermaid-svg-yHV5LzDlEK13vfob .icon-shape,#mermaid-svg-yHV5LzDlEK13vfob .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yHV5LzDlEK13vfob .icon-shape p,#mermaid-svg-yHV5LzDlEK13vfob .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yHV5LzDlEK13vfob .icon-shape .label rect,#mermaid-svg-yHV5LzDlEK13vfob .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yHV5LzDlEK13vfob .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yHV5LzDlEK13vfob .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yHV5LzDlEK13vfob :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 目标板 Target
主机 Host
target remote
TCP连接
aarch64-linux-gdb
交叉调试器
gdbserver
调试代理
被调试程序
out.elf
步骤1:在目标板上启动 gdbserver
bash
# 语法:gdbserver <host>:<port> <program> &
# <host>:主机名称或IP地址,可以省略
# <port>:网络端口号,可以是目标板上任何可用的端口号
# <program>:需要调试的程序名
# &:后台执行
$ gdbserver 192.168.110.124:2345 out.elf &
# 也可以省略 host(等待任意主机连接)
$ gdbserver :2345 out.elf &
步骤2:在主机端启动交叉调试器
bash
$ aarch64-linux-gdb out.elf
步骤3:在主机 GDB 控制台下建立与目标板的连接
bash
(gdb) target remote 192.168.110.125:2345
# <target>:目标板的IP地址
# <port>:gdbserver 监听的网络端口号
自动化连接: 可以将 target remote 命令写入 .gdbinit 配置文件,启动 GDB 时会自动读取:
bash
# .gdbinit 文件内容
target remote 192.168.110.125:2345
gdbserver 常用选项:
| 选项 | 说明 | 示例 |
|---|---|---|
--attach |
附加到已运行的进程 | gdbserver :2345 --attach 1234 |
--multi |
多进程模式,允许调试多个进程 | gdbserver --multi :2345 |
--once |
调试完一个程序后自动退出 | gdbserver --once :2345 ./program |
--remote-debug |
显示远程调试协议信息 | gdbserver --remote-debug :2345 ./program |
5.1.4 Coredump调试(事后崩溃分析)
Coredump(核心转储)是程序崩溃时操作系统将进程内存映像保存到文件的机制,用于事后分析崩溃原因。
配置 coredump:
bash
# 查看当前 coredump 设置
$ ulimit -a
# 关闭 coredump
$ ulimit -c 0
# 设置 coredump 大小限制(单位:KB)
$ ulimit -c <num>
# 生成 coredump 文件大小没有限制(推荐)
$ ulimit -c unlimited
指定 coredump 文件存放目录:
bash
# 默认在程序执行的当前目录生成文件 core
# 通过 core_pattern 指定存放目录和文件名格式
$ echo "/tmp/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
core_pattern 参数含义:
| 参数 | 含义 |
|---|---|
%e |
程序文件名 |
%h |
主机名 |
%p |
所 dump 进程的进程 ID |
%u |
所 dump 进程的实际用户 ID |
%g |
所 dump 进程的实际组 ID |
%t |
coredump 的时间戳 |
%s |
导致本次 coredump 的信号 |
%% |
单个 % 字符 |
注意事项:
| 注意点 | 说明 |
|---|---|
| 可移动磁盘 | 可能无法在可移动磁盘中生成 coredump |
| 磁盘空间 | 需要确保目标目录有足够的磁盘空间 |
| systemd 系统 | 使用 coredumpctl 管理(coredumpctl list、coredumpctl gdb <PID>) |
| ulimit 继承 | 子进程继承父进程的 ulimit 设置 |
使用 GDB 加载 coredump:
bash
# 方式1:直接加载
$ gdb <program> core
# 方式2:进入 gdb 后加载
$ gdb <program>
(gdb) core-file core
触发 coredump 的信号:
| 信号 | 触发方式 | 默认行为 |
|---|---|---|
| SIGQUIT | Ctrl-\ |
终止进程并生成 coredump |
| SIGSEGV | 段错误(访问非法内存) | 终止进程并生成 coredump |
| SIGABRT | 调用 abort() |
终止进程并生成 coredump |
| SIGBUS | 总线错误 | 终止进程并生成 coredump |
| SIGFPE | 算术异常(如除零) | 终止进程并生成 coredump |
| SIGILL | 非法指令 | 终止进程并生成 coredump |
| SIGTRAP | 断点/陷阱 | 终止进程并生成 coredump |
5.1.5 启动方式对比总结
| 调试场景 | 启动命令 | 适用条件 |
|---|---|---|
| 本地调试可执行文件 | gdb <program> |
有可执行文件和源码 |
| 附加运行进程 | gdb -p <pid> 或 gdb <program> <pid> |
进程正在运行 |
| 调试 coredump | gdb <program> core |
有 core 文件 |
| 远程交叉调试 | aarch64-linux-gdb + target remote |
有 gdbserver |
| 仅加载符号表 | gdb -s <file> |
只需要符号信息 |
| 批处理模式 | gdb -batch -ex "cmd" |
自动化脚本 |
5.2 程序执行控制
本节介绍 GDB 中控制程序执行的核心命令,包括运行与停止、单步执行、执行到指定位置和函数调用。这些是调试过程中使用频率最高的命令。
5.2.1 运行与停止(run/start/continue/kill)
| 命令 | 简写 | 描述 |
|---|---|---|
help |
查看命令的帮助。例如 help info 查看 info 的所有命令 |
|
file |
指定要调试的可执行程序 | |
load |
将动态库加载入内存 | |
kill |
终止正在调试的可执行程序 | |
make |
在不退出 gdb 的情况下运行 make 工具 | |
shell |
在不退出 gdb 的情况下运行 shell 命令 | |
run |
r |
开始执行程序 |
start |
运行到 main 函数暂停 | |
continue |
c |
继续执行程序 |
quit |
q |
结束 gdb 调试任务,也可以 Ctrl+D |
实战示例:
bash
# 在 GDB 中重新加载程序
(gdb) file ./new_program
# 在 GDB 中运行 make(修改代码后无需退出)
(gdb) make
# 在 GDB 中运行 shell 命令
(gdb) shell ls -la
# 终止当前调试的程序(但保留 GDB 会话)
(gdb) kill
# 重新运行
(gdb) run
5.2.2 单步执行(next/step/nexti/stepi)
| 命令 | 简写 | 描述 |
|---|---|---|
next |
n |
单步执行,不会进入函数内部(显示的代码是下一步将要执行的代码) |
nexti |
ni |
单步执行,不会进入函数内部(显示的指令是下一步将要执行的指令) |
step |
s |
单步执行,可以跟踪到函数内部 |
stepi |
si |
单步执行,可以跟踪到函数内部(指令级) |
next/step 与 nexti/stepi 的区别:
| 对比项 | next / step | nexti / stepi |
|---|---|---|
| 操作粒度 | 源代码行 | 汇编指令 |
| 显示内容 | 下一步将要执行的源代码 | 下一步将要执行的机器指令 |
| 适用场景 | 常规调试 | 分析汇编级行为、优化问题 |
| 是否进入函数 | next 不进入,step 进入 | nexti 不进入,stepi 进入 |
数字前缀: s、n、si、ni 等指令可以加数字表示执行多少次:
bash
(gdb) n 4 # 执行 4 步 next
(gdb) s 10 # 执行 10 步 step
(gdb) c 4 # 继续执行,遇到 4 次断点后停止
易错点:
| 易错点 | 说明 |
|---|---|
next 在循环中 |
每次只执行一行循环体,需要多次按回车,此时用 until 更高效 |
step 进入无源码函数 |
如果函数没有调试信息,step 会跳过该函数 |
| 优化代码单步 | -O2 编译的代码行号可能跳跃,建议用 -O0 或 -Og |
5.2.3 执行到指定位置(until/finish/return)
| 命令 | 简写 | 描述 |
|---|---|---|
until |
u |
直到运行到比原内存地址更高的内存地址的指令处停止(常用于跳出循环) |
until <line> |
u <line> |
直到运行到当前文件 line 行处停止 |
finish |
fin |
函数执行完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息 |
return |
函数直接从当前返回 | |
return <value> |
改变程序流程,直接结束当前函数,并将指定值 value 返回 |
until 的典型使用场景: 在循环体内时,next 每次只执行一行,需要多次按回车。until 可以一次性执行到循环结束:
bash
(gdb) u # 执行到循环结束(地址更高的位置)
(gdb) u 50 # 执行到当前文件第 50 行
finish 与 return 的区别:
| 对比项 | finish | return |
|---|---|---|
| 执行方式 | 继续执行到函数自然返回 | 立即从当前位置返回 |
| 剩余代码 | 会执行完函数剩余代码 | 跳过剩余代码 |
| 返回值 | 函数实际返回值 | 可指定返回值 |
| 适用场景 | 想看到函数完整执行结果 | 想跳过函数剩余部分 |
5.2.4 函数调用(call/print调用函数)
| 命令 | 描述 |
|---|---|
call <func> |
在当前位置执行所要运行的函数 func |
p <func> |
在当前位置执行所要运行的函数 func(print 也可以调用函数) |
bash
# 在断点处调用函数
(gdb) call printf("debug: i=%d\n", i)
(gdb) p malloc(1024)
注意事项:
| 注意点 | 说明 |
|---|---|
| 副作用 | 调用的函数会实际执行,可能修改程序状态 |
| 返回类型 | 如果函数返回 void,使用 call 而非 p |
| 栈帧要求 | 某些函数调用需要有效的栈帧上下文 |
5.2.5 快捷键与运行参数
| 快捷键/命令 | 说明 |
|---|---|
Ctrl+C |
主动暂停程序运行 |
Ctrl+D |
退出 gdb(同 quit) |
| 直接回车 | 重复上一次命令 |
set args <arg1> <arg2> |
设置运行参数 |
show args |
查看运行参数 |
cd <dir> |
切换工作目录 |
set environment <var>=<val> |
设置环境变量 |
show environment |
查看所有环境变量 |
5.2.6 执行控制命令速查对比表
| 需求 | 命令 | 说明 |
|---|---|---|
| 开始运行 | run / start |
start 停在 main 入口 |
| 继续运行 | continue |
运行到下一个断点 |
| 单步(不进函数) | next |
逐行执行 |
| 单步(进入函数) | step |
跟踪进入函数 |
| 跳出循环 | until |
执行到循环结束 |
| 执行完当前函数 | finish |
运行到函数返回 |
| 立即返回 | return <val> |
跳过剩余代码 |
| 执行 N 步 | n N / s N |
数字前缀 |
| 暂停运行中程序 | Ctrl+C |
发送 SIGINT |
5.3 断点与观察点
本节介绍 GDB 中设置和管理断点、观察点、捕捉点的方法。断点是调试的核心机制,能够在代码特定位置暂停程序执行,以便检查程序状态。
5.3.1 断点设置(break/tbreak)
| 命令 | 描述 | 示例 |
|---|---|---|
b |
当前位置设置断点 | b |
b <line> |
当前文件 line 行设置断点 | b 100 |
b <file>:<line> |
指定文件 line 行设置断点 | b main.c:100 |
b <func> |
指定函数 func 入口设置断点 | b malloc |
b <addr> |
指定内存 addr 处设置断点 | b *0x9000888a |
b +<offset> |
当前行号的后面的 offset 行设置断点 | b +5 |
b -<offset> |
当前行号的前面的 offset 行设置断点 | b -3 |
b ... if <condition> |
设置条件断点,在循环中使用非常方便 | b 10 if i==3 |
tb ... |
设置临时断点,到达后被自动删除,使用类似 break | tb main |
条件断点实战: 在循环中调试时,条件断点非常有用:
bash
# 在第 10 行设置断点,仅当 i==3 时中断
(gdb) b 10 if i==3
# 在函数入口设置条件断点
(gdb) b myadd if a > 100
# 临时断点(到达一次后自动删除)
(gdb) tb main.c:50
# 在地址上设置断点(无源码时)
(gdb) b *0x0000000000401149
条件断点支持的表达式:
| 表达式类型 | 示例 | 说明 |
|---|---|---|
| 简单比较 | i == 10 |
等于 |
| 范围判断 | i >= 5 && i <= 10 |
逻辑与 |
| 指针判断 | ptr != 0 |
非空 |
| 字符串比较 | strcmp(name, "test") == 0 |
调用函数 |
| 结构体成员 | node->val > 100 |
成员访问 |
5.3.2 观察点(watch/rwatch/awatch)
| 命令 | 描述 |
|---|---|
watch <val> |
设置观察点,指定 val 变量的值被软件改变后程序被暂停 |
watch <expr> |
设置观察点,指定 expr 表达式的值被软件改变后程序被暂停 |
watch *<addr> |
设置观察点,指定 addr 内存的值被软件改变后程序被暂停 |
awatch <val> |
设置观察点,指定 val 变量被读出或写入时程序被暂停 |
rwatch <val> |
设置观察点,指定 val 变量被读出时程序被暂停 |
twatch |
设置临时观察点 |
观察点注意事项:
| 注意点 | 说明 |
|---|---|
| 作用域 | watch 局部变量退出局部作用域会自动删除观察点 |
| 检测限制 | GDB 无法检测一个非当前进程或硬件对表达式值的改变 |
| 硬件限制 | 硬件观察点数量有限(x86 通常 4 个),超出后使用软件观察点 |
| 性能影响 | 软件观察点性能开销极大(每步都检查),硬件观察点开销小 |
观察点对比表:
| 命令 | 触发条件 | 典型用途 |
|---|---|---|
watch |
变量值被写入(改变) | 追踪谁修改了某个变量 |
rwatch |
变量被读取 | 追踪谁读取了某个变量 |
awatch |
变量被读取或写入 | 追踪对变量的所有访问 |
实战示例:
bash
# 监控变量 counter 的修改
(gdb) watch counter
Hardware watchpoint 2: counter
# 监控内存地址的写入
(gdb) watch *(int *)0x7fffffffe000
# 监控数组元素
(gdb) watch buf[5]
# 监控表达式
(gdb) watch x + y
5.3.3 捕捉点(catch/tcatch)
| 命令 | 描述 |
|---|---|
catch |
设置捕捉点,来捕捉程序运行时的一些事件,如:载入共享库(动态链接库)或是 C++ 的异常 |
tcatch |
只设置一次捕捉点,当程序停住以后,捕捉点被自动删除 |
bash
# 捕捉共享库加载事件
(gdb) catch load libfoo.so
# 捕捉 C++ 异常
(gdb) catch throw
# 捕捉系统调用
(gdb) catch syscall open
# 捕捉 fork 事件
(gdb) catch fork
# 捕捉 exec 事件
(gdb) catch exec
常用捕捉事件:
| 事件 | 命令 | 说明 |
|---|---|---|
| 共享库加载 | catch load [libname] |
动态库加载时中断 |
| C++ throw | catch throw |
抛出异常时中断 |
| C++ catch | catch catch |
捕获异常时中断 |
| 系统调用 | catch syscall [name] |
特定系统调用时中断 |
| fork | catch fork |
创建子进程时中断 |
| exec | catch exec |
执行新程序时中断 |
| 信号 | catch signal SIGSEGV |
收到特定信号时中断 |
5.3.4 断点管理(info/delete/disable/enable/ignore)
| 命令 | 描述 |
|---|---|
i b |
显示所有断点和观察点 |
clear <line> |
清除指定 line 行的断点或观察点 |
d |
清除所有断点和观察点 |
d <num> |
清除指定 num 编号的断点或观察点 |
dis |
所有断点和观察点失效 |
dis <num> |
指定 num 编号的断点或观察点失效,可将编号之间用空格隔开 |
en |
所有断点和观察点使能 |
en <num> |
指定 num 编号的断点或观察点使能,可将编号之间用空格隔开 |
en once <num> |
指定 num 编号的断点或观察点暂时使能一次 |
ignore <num> <times> |
指定 num 编号的断点或观察点到达后不停止 times 次 |
实践示例:
bash
# 查看所有断点
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000001139 in main at main.c:10
breakpoint already hit 3 times
2 watchpoint keep y counter
# 禁用断点 1
(gdb) disable 1
# 启用断点 1
(gdb) enable 1
# 暂时使能一次(触发后自动禁用)
(gdb) enable once 2
# 忽略断点 1 的前 5 次触发
(gdb) ignore 1 5
# 删除断点 1
(gdb) delete 1
# 删除所有断点
(gdb) delete
输出解读(info breakpoints):
| 字段 | 说明 |
|---|---|
| Num | 断点编号,用于 delete/disable/enable 等操作 |
| Type | 类型:breakpoint / watchpoint / catchpoint |
| Disp | disposition:keep(保留)/ del(触发后删除) |
| Enb | 是否启用:y / n |
| Address | 断点地址 |
| What | 断点位置描述(文件:行号 或 函数名) |
| already hit N times | 已触发次数 |
5.3.5 断点与观察点区别对比表
| 特性 | 断点(Breakpoint) | 观察点(Watchpoint) |
|---|---|---|
| 触发时机 | CPU 到某一地址取指令时中断 | CPU 到某一地址读写数据时中断 |
| 作用对象 | 代码地址(函数、行号) | 内存地址/变量 |
| 生命周期 | 持久(除非手动删除) | 局部变量退出作用域自动删除 |
| 硬件支持 | 通常由软件实现(修改指令) | 可能需要硬件调试寄存器支持 |
| 性能影响 | 较小 | 可能较大(软件实现时) |
| 操作通用性 | 所有使用于 breakpoint 的操作都适用于 watchpoint | 同左 |
| 数量限制 | 无限制 | 硬件观察点有限(x86 通常 4 个) |
5.4 数据查看与修改
本节介绍 GDB 中查看和修改程序数据的方法,包括内存查看、变量打印、自动显示、变量修改和类型查看。这些命令帮助开发者在程序暂停时深入理解程序状态。
5.4.1 内存查看(x命令)
语法: x /<数量><大小类型><显示类型> <内存地址>
大小类型:
| 字母 | 大小 | 说明 |
|---|---|---|
b |
1 字节 | byte |
h |
2 字节 | halfword |
w |
4 字节 | word(默认) |
g |
8 字节 | giant |
显示类型:
| 字母 | 格式 | 说明 |
|---|---|---|
o |
八进制 | octal |
x |
十六进制 | hex(默认) |
d |
十进制 | decimal |
u |
无符号十进制 | unsigned decimal |
t |
二进制 | binary |
f |
浮点数 | float |
a |
地址 | address |
i |
指令 | instruction |
c |
字符 | char |
s |
字符串 | string |
z |
十六进制(左侧补零) | hex, zero padded |
示例:
bash
# 以四字节为单位十六进制显示 0x90a2a7a0 开始的 64 个数据(默认大小 w)
(gdb) x /64x 0x90a2a7a0
# 以单字节为单位十六进制显示 64 个数据
(gdb) x /64bx 0x90a2a7a0
# 以八字节为单位十进制显示 10 个数据
(gdb) x /10gd 0x7fffffffe000
# 显示当前 PC 指针开始的 8 条指令
(gdb) x /8i $pc
# 以字符串形式显示
(gdb) x /s 0x4005d0
# 以字符形式显示 16 个字节
(gdb) x /16cb 0x7fffffffe000
输出解读(x /4xw $rsp):
0x7fffffffe000: 0x00000003 0x00000005 0x00000000 0x00007fff
| 部分 | 含义 |
|---|---|
0x7fffffffe000 |
起始内存地址 |
0x00000003 |
该地址处的 4 字节数据(十六进制) |
| 每行 4 个 | 默认每行显示 4 个单位 |
常用寄存器作为地址:
| 寄存器 | 含义 | 典型用法 |
|---|---|---|
$pc |
程序计数器 | x /10i $pc(查看当前指令) |
$sp / $rsp |
栈指针 | x /16xg $rsp(查看栈内容) |
$fp / $rbp |
帧指针 | x /8xg $fp(查看当前栈帧) |
$lr / $ra |
返回地址 | x /1i $lr(查看返回指令) |
5.4.2 变量打印(print命令)
| 命令 | 描述 |
|---|---|
p <var or expr> |
打印变量或表达式的值,如 p a+b |
p /<type> <var or expr> |
指定格式打印,如 p /x a+b |
p *<ptr>@<len> |
显示数组值,如 p *buf@sizeof(buf) |
p '<file>'::<var> |
显示特定文件 file 的变量值,如 p 'file1.c'::var1 |
p '<func>'::<var> |
显示特定函数 func 的变量值,如 p 'func1'::var1 |
p <var>=<value> |
改变变量或表达式的值,如 p i=3 |
显示格式同 x 命令的显示类型(o/x/d/u/t/f/a/i/c/s/z)。
实践示例:
bash
# 打印变量
(gdb) p counter
$1 = 42
# 十六进制打印
(gdb) p /x counter
$2 = 0x2a
# 打印表达式
(gdb) p a + b * 2
$3 = 100
# 打印数组内容(ptr 指向数组首地址,len 为元素个数)
(gdb) p *buf@sizeof(buf)
$4 = {0, 1, 2, 3, 4, 5, 6, 7}
# 打印特定文件中的静态变量
(gdb) p 'file1.c'::static_var
# 打印特定函数中的局部变量(需要停在该函数内)
(gdb) p 'myadd'::a
# 修改变量值
(gdb) p i = 100
print 输出格式说明:
| 前缀 | 含义 |
|---|---|
$1 |
第 1 次 print 的结果编号,可用 $1 引用 |
$2 |
第 2 次 print 的结果编号 |
$$ |
上一次 print 的结果 |
$$2 |
倒数第 2 次 print 的结果 |
5.4.3 自动显示(display)
display 命令设置程序每次暂停后自动显示的数据及其格式,非常适合持续监控某个变量的变化。
| 命令 | 描述 |
|---|---|
disp <var or expr> |
增加 display 用于显示变量或表达式的值,每当程序运行到断点处都会显示 |
disp /<type> <var or expr> |
指定格式输出显示,如 disp /c str |
disp /i $pc |
自动显示下一条汇编指令($pc 表示指令地址,/i 表示输出格式为机器指令码) |
i disp |
显示当前所有的 display |
undisp <num> |
删除指定 num 编号的 display |
d disp <num> |
删除指定 num 编号的 display |
dis disp <num> |
指定 num 编号的 display 失效 |
en disp <num> |
指定 num 编号的 display 使能 |
bash
# 每次断点停止时自动显示变量 i 的值
(gdb) display i
# 以十六进制格式自动显示
(gdb) display /x flags
# 自动显示下一条汇编指令(单步调试汇编时非常有用)
(gdb) display /i $pc
# 查看所有 display
(gdb) info display
# 删除编号为 1 的 display
(gdb) undisplay 1
5.4.4 变量修改(set命令)
| 命令 | 描述 |
|---|---|
set <var>=<value> |
改变变量或表达式的值,如 set i=3 |
p <var>=<value> |
打印并改变变量值,如 p i=3 |
set 与 p 修改变量的区别:
| 对比项 | set var=val |
p var=val |
|---|---|---|
| 输出 | 不打印结果 | 打印修改后的值 |
| 用途 | 纯粹修改变量 | 修改并确认值 |
| 表达式 | 支持 | 支持 |
5.4.5 GDB自定义变量(convenience variables)
bash
# 定义 GDB 调试环境中的自定义变量
# 第 1 次使用时会创建这个变量,后面再使用则直接对其赋值
(gdb) set $i=0
(gdb) set $i=$i+1
(gdb) p $i
$1 = 1
# 查看当前所设置的所有的环境变量(自定义变量)
(gdb) show convenience
$i = 1
自定义变量的用途:
| 用途 | 示例 |
|---|---|
| 计数器 | set $count = $count + 1 |
| 保存地址 | set $ptr = &my_struct |
| 条件断点中使用 | b main if $count > 10 |
| 与断点命令配合 | 在断点命令中累加计数 |
5.4.6 类型查看(whatis/ptype)
| 命令 | 描述 |
|---|---|
whatis <val> |
显示变量 val 的数据类型 |
ptype <val> |
显示变量的详细类型(包括结构体成员) |
bash
(gdb) whatis counter
type = int
(gdb) ptype my_struct
type = struct {
int x;
int y;
char name[32];
}
whatis 与 ptype 的区别:
| 对比项 | whatis | ptype |
|---|---|---|
| 输出详细度 | 仅显示类型名 | 显示完整类型定义 |
| 结构体 | type = struct my_struct |
显示所有成员及类型 |
| 适用场景 | 快速确认类型 | 查看结构体布局 |
5.4.7 结构体显示(set print pretty)
bash
# 改变 struct 显示格式为多行显示(更易读)
(gdb) set print pretty on
对比效果:
bash
# 关闭前:
$1 = {x = 1, y = 2, name = "test", data = {1, 2, 3}}
# 开启后:
$1 = {
x = 1,
y = 2,
name = "test",
data = {1, 2, 3}
}
其他打印格式设置:
| 命令 | 说明 |
|---|---|
set print pretty on |
结构体多行显示 |
set print array on |
数组多行显示 |
set print array-indexes on |
数组显示下标 |
set print elements <N> |
设置数组/字符串最大显示元素数(0=无限) |
set print null-stop on |
字符串遇到 NULL 停止显示 |
set print object on |
显示 C++ 对象的实际类型 |
set print vtbl on |
显示虚函数表 |
5.4.8 数据查看命令速查对比表
| 需求 | 命令 | 示例 |
|---|---|---|
| 查看内存 | x /<fmt> <addr> |
x /16xw $rsp |
| 打印变量 | p <var> |
p counter |
| 打印数组 | p *ptr@len |
p *buf@10 |
| 自动显示 | display <var> |
display i |
| 修改变量 | set var=val |
set i=100 |
| 查看类型 | whatis / ptype |
ptype my_struct |
| 自定义变量 | set $var=val |
set $i=0 |
5.5 堆栈分析
程序的"调用堆栈"是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个"帧"(frame),最近调用的函数在 0 号帧中("底部"帧)。
#mermaid-svg-PHBsaM8iILOfD3Xb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PHBsaM8iILOfD3Xb .error-icon{fill:#552222;}#mermaid-svg-PHBsaM8iILOfD3Xb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PHBsaM8iILOfD3Xb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PHBsaM8iILOfD3Xb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PHBsaM8iILOfD3Xb .marker.cross{stroke:#333333;}#mermaid-svg-PHBsaM8iILOfD3Xb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PHBsaM8iILOfD3Xb p{margin:0;}#mermaid-svg-PHBsaM8iILOfD3Xb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb .cluster-label text{fill:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb .cluster-label span{color:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb .cluster-label span p{background-color:transparent;}#mermaid-svg-PHBsaM8iILOfD3Xb .label text,#mermaid-svg-PHBsaM8iILOfD3Xb span{fill:#333;color:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb .node rect,#mermaid-svg-PHBsaM8iILOfD3Xb .node circle,#mermaid-svg-PHBsaM8iILOfD3Xb .node ellipse,#mermaid-svg-PHBsaM8iILOfD3Xb .node polygon,#mermaid-svg-PHBsaM8iILOfD3Xb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PHBsaM8iILOfD3Xb .rough-node .label text,#mermaid-svg-PHBsaM8iILOfD3Xb .node .label text,#mermaid-svg-PHBsaM8iILOfD3Xb .image-shape .label,#mermaid-svg-PHBsaM8iILOfD3Xb .icon-shape .label{text-anchor:middle;}#mermaid-svg-PHBsaM8iILOfD3Xb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PHBsaM8iILOfD3Xb .rough-node .label,#mermaid-svg-PHBsaM8iILOfD3Xb .node .label,#mermaid-svg-PHBsaM8iILOfD3Xb .image-shape .label,#mermaid-svg-PHBsaM8iILOfD3Xb .icon-shape .label{text-align:center;}#mermaid-svg-PHBsaM8iILOfD3Xb .node.clickable{cursor:pointer;}#mermaid-svg-PHBsaM8iILOfD3Xb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PHBsaM8iILOfD3Xb .arrowheadPath{fill:#333333;}#mermaid-svg-PHBsaM8iILOfD3Xb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PHBsaM8iILOfD3Xb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PHBsaM8iILOfD3Xb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PHBsaM8iILOfD3Xb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PHBsaM8iILOfD3Xb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PHBsaM8iILOfD3Xb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PHBsaM8iILOfD3Xb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PHBsaM8iILOfD3Xb .cluster text{fill:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb .cluster span{color:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PHBsaM8iILOfD3Xb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PHBsaM8iILOfD3Xb rect.text{fill:none;stroke-width:0;}#mermaid-svg-PHBsaM8iILOfD3Xb .icon-shape,#mermaid-svg-PHBsaM8iILOfD3Xb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PHBsaM8iILOfD3Xb .icon-shape p,#mermaid-svg-PHBsaM8iILOfD3Xb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PHBsaM8iILOfD3Xb .icon-shape .label rect,#mermaid-svg-PHBsaM8iILOfD3Xb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PHBsaM8iILOfD3Xb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PHBsaM8iILOfD3Xb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PHBsaM8iILOfD3Xb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} #0 帧:当前函数
(最近调用)
#1 帧:调用者
#2 帧:更上层调用者
...
#N 帧:main 函数
(最早调用)
5.5.1 栈帧查看(backtrace/bt)
| 命令 | 描述 |
|---|---|
bt |
打印栈帧,查看程序执行到此时函数调用层次是怎样的 |
bt full |
显示详细栈帧(包含每帧的局部变量) |
bt <num> |
显示前 num 层栈帧 |
bt -<num> |
显示最后 num 层栈帧 |
bash
(gdb) bt
#0 mydiv (a=3, b=0) at myop.c:16
#1 0x00000000000011a5 in main (argc=3, argv=0x7fffffffe1a8) at main.c:15
(gdb) bt full
#0 mydiv (a=3, b=0) at myop.c:16
result = 0
#1 0x00000000000011a5 in main (argc=3, argv=0x7fffffffe1a8) at main.c:15
a = 3
b = 0
输出解读:
| 字段 | 说明 |
|---|---|
#0 |
帧编号,0 为最内层(当前函数) |
mydiv (a=3, b=0) |
函数名和参数值 |
at myop.c:16 |
源文件和行号 |
0x00000000000011a5 |
函数调用地址 |
5.5.2 栈帧切换(frame/up/down)
| 命令 | 描述 |
|---|---|
f <num> / frame <num> |
切换到第 num 层栈 |
up |
切换到高一层栈(调用者方向) |
down |
切换到低一层栈(被调用者方向) |
bash
(gdb) bt
#0 helper (x=10) at utils.c:25
#1 0x... in process (data=0x...) at main.c:42
#2 0x... in main () at main.c:60
# 切换到第 1 帧(process 函数)
(gdb) frame 1
# 向上切换一帧(到 main)
(gdb) up
# 向下切换一帧(回到 process)
(gdb) down
切换栈帧后的操作: 切换栈帧后,info locals、info args、p 等命令都作用于当前帧。
5.5.3 栈信息查看(info stack/registers/frame)
| 命令 | 全称 | 描述 |
|---|---|---|
i s |
info stack |
查看堆栈使用情况(同 bt) |
i r |
info registers |
查看寄存器使用情况 |
i f |
info frame |
查看当前栈层的详细信息(函数地址、调用函数的地址、被调用函数的地址等) |
info frame 输出解读:
bash
(gdb) info frame
Stack level 0, frame at 0x7fffffffe020:
rip = 0x401149 in mydiv (myop.c:16); saved rip = 0x4011a5
called by frame at 0x7fffffffe040
source language c.
Arglist at 0x7fffffffe010, args: a=3, b=0
Locals at 0x7fffffffe010, Previous frame's sp is 0x7fffffffe020
Saved registers:
rbp at 0x7fffffffe010, rip at 0x7fffffffe018
| 字段 | 说明 |
|---|---|
| Stack level | 栈层级 |
| frame at | 帧地址 |
| rip | 当前指令地址 |
| saved rip | 返回地址 |
| called by frame at | 调用者帧地址 |
| Arglist | 参数列表地址 |
| Locals | 局部变量地址 |
| Saved registers | 保存的寄存器 |
5.5.4 函数参数与局部变量(info args/locals)
| 命令 | 描述 |
|---|---|
i args |
查看当前函数的参数名及其值 |
i locals |
查看当前函数中所有局部变量及其值 |
i catch |
查看当前函数中的异常处理信息 |
bash
(gdb) info args
a = 3
b = 5
(gdb) info locals
result = 8
temp = 0
5.5.5 堆栈分析命令速查表
| 需求 | 命令 | 说明 |
|---|---|---|
| 查看完整调用栈 | bt |
最常用 |
| 查看含局部变量的栈 | bt full |
信息更全 |
| 只看前 N 层 | bt N |
栈很深时有用 |
| 切换栈帧 | frame N / up / down |
查看不同层的变量 |
| 查看当前帧详情 | info frame |
地址、寄存器 |
| 查看函数参数 | info args |
当前帧的参数 |
| 查看局部变量 | info locals |
当前帧的局部变量 |
| 查看寄存器 | info registers |
所有寄存器值 |
5.6 多线程调试
本节介绍 GDB 中多线程程序的调试方法,包括线程查看与切换、线程断点、批量命令执行和调度锁定。多线程调试是并发编程问题排查的核心技能。
5.6.1 线程查看与切换(info threads/thread)
| 命令 | 描述 |
|---|---|
info threads |
查看所有线程信息。GDB 会为每个线程分配一个 ID(和系统 TID 不同),编号一般从 1 开始 |
thread <ID> |
切换当前调试的线程为指定 ID 的线程 |
bash
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7ffff7... (LWP 12345) "program" main () at main.c:10
2 Thread 0x7ffff6... (LWP 12346) "program" worker () at worker.c:25
3 Thread 0x7ffff5... (LWP 12347) "program" 0x00007ffff7... in pthread_cond_wait ()
# 切换到线程 2
(gdb) thread 2
输出解读:
| 字段 | 说明 |
|---|---|
* |
标记当前正在调试的线程 |
| Id | GDB 内部线程编号(从 1 开始) |
| Target Id | 系统级线程标识(LWP = Light Weight Process) |
| Frame | 线程当前停止的位置 |
注意: info threads 输出中的 * 标记表示当前正在调试的线程。
5.6.2 线程断点(thread all/thread N)
bash
# 所有线程都在文件 file 的第 line 行有断点
(gdb) b <file>:<line> thread all
(gdb) b malloc.c:128 thread all
# 仅在特定线程设置断点
(gdb) b main.c:50 thread 2
线程断点与普通断点的区别:
| 对比项 | 普通断点 | 线程断点 |
|---|---|---|
| 触发线程 | 任何线程到达都触发 | 仅指定线程触发 |
| 语法 | b main.c:50 |
b main.c:50 thread 2 |
| 适用场景 | 通用 | 只关注特定线程的行为 |
5.6.3 线程命令批量执行(thread apply)
| 命令 | 描述 |
|---|---|
thread apply <ID1> <ID2> ... <command> |
多个线程执行 gdb 命令 command |
thread apply all <command> |
所有线程执行 gdb 命令 command |
bash
# 查看所有线程的堆栈(多线程死锁调试必备)
(gdb) thread apply all bt
# 对线程 1 和 3 执行命令
(gdb) thread apply 1 3 info locals
5.6.4 调度锁定(set scheduler-locking)
| 命令 | 说明 |
|---|---|
set scheduler-locking off |
不锁定任何线程(默认值) |
set scheduler-locking on |
锁定其他线程,只有当前线程执行 |
set scheduler-locking step |
在 step(单步)时,只有被调试线程运行 |
各模式对比:
| 模式 | next / step 时 | continue 时 | 适用场景 |
|---|---|---|---|
off |
所有线程都可能运行 | 所有线程运行 | 默认,观察多线程交互 |
on |
只有当前线程运行 | 只有当前线程运行 | 严格单线程调试 |
step |
只有当前线程运行 | 所有线程运行 | 单步时隔离,继续时恢复 |
实战建议:
| 场景 | 推荐模式 |
|---|---|
| 调试单线程逻辑 | on |
| 观察多线程竞争 | off |
| 单步跟踪特定线程 | step |
| 死锁分析 | off(需要所有线程运行到死锁状态) |
5.6.5 多线程调试选项
| 命令 | 说明 |
|---|---|
set non-stop on/off |
在调试某一个线程时,其他线程是否运行 |
set pagination on/off |
在使用 backtrace 时,在分页时是否停止 |
set target-async on/off |
同步和异步。同步:gdb 在输出提示符之前等待程序报告一些线程已经终止的信息;异步:直接返回 |
set print thread-events on/off |
线程创建/销毁是否提醒 |
5.6.6 多线程调试命令速查表
| 需求 | 命令 | 说明 |
|---|---|---|
| 查看所有线程 | info threads |
显示线程状态 |
| 切换线程 | thread N |
N 为 GDB 线程 ID |
| 所有线程堆栈 | thread apply all bt |
死锁分析必备 |
| 线程断点 | b <loc> thread N |
仅特定线程触发 |
| 锁定调度 | set scheduler-locking on |
隔离调试 |
| 关闭线程事件提示 | set print thread-events off |
减少干扰 |
5.7 多进程调试
本节介绍 GDB 中多进程程序的调试方法,包括进程挂接、fork 跟踪、进程列表与切换和进程管理。
5.7.1 进程挂接(attach/detach)
| 命令 | 描述 |
|---|---|
attach <pid> |
挂接进程 |
detach |
取消挂接的进程 |
5.7.2 fork跟踪(set follow-fork-mode)
| 命令 | 说明 |
|---|---|
set follow-fork-mode parent |
fork 创建子进程时,调试父进程(默认) |
set follow-fork-mode child |
fork 创建子进程时,调试子进程 |
bash
# 调试 fork 后的子进程
(gdb) set follow-fork-mode child
(gdb) run
fork 调试模式对比:
| 模式 | fork 后调试 | 另一个进程 | 适用场景 |
|---|---|---|---|
parent |
父进程 | 子进程继续运行 | 调试父进程逻辑 |
child |
子进程 | 父进程继续运行 | 调试子进程逻辑 |
5.7.3 进程列表与切换(info inferiors/inferior)
| 命令 | 描述 |
|---|---|
info inferiors |
查看当前正在调试的所有进程列表 |
inferior <ID> |
切换到指定 ID 的进程进行调试 |
bash
(gdb) info inferiors
Num Description Executable
* 1 process 12345 ./program
2 process 12346 ./program
(gdb) inferior 2
5.7.4 进程管理(add-inferior/remove-inferiors/clone-inferior)
| 命令 | 描述 |
|---|---|
add-inferior |
添加一个新的待调试进程 |
remove-inferiors <ID> |
移除指定 ID 的进程(从调试列表中移除,不终止进程) |
clone-inferior |
克隆当前进程创建一个新的调试进程 |
5.7.5 进程内存信息(maint info sections)
| 命令 | 描述 |
|---|---|
maint info sections |
显示当前进程的内存段信息 |
注意: 每个进程有自己的地址空间,断点和观察点是进程特定的。
5.7.6 多进程调试命令速查表
| 需求 | 命令 | 说明 |
|---|---|---|
| 附加进程 | attach <pid> |
挂接运行中进程 |
| 分离进程 | detach |
取消挂接 |
| fork 跟踪子进程 | set follow-fork-mode child |
调试子进程 |
| 查看所有进程 | info inferiors |
进程列表 |
| 切换进程 | inferior N |
切换调试目标 |
| 查看内存段 | maint info sections |
进程内存布局 |
5.8 信号处理
本节介绍 GDB 中信号的处理方式,包括向被调试程序发送信号和控制 GDB 对信号的拦截行为。
5.8.1 信号发送与处理(signal/handle)
| 命令 | 描述 |
|---|---|
signal <num> |
发送一个信号量给被调试的程序 |
handle <signal> nostop |
忽略信号处理(GDB 不拦截该信号) |
bash
# 忽略 SIGPIPE 信号(网络编程中常用)
(gdb) handle SIGPIPE nostop
# 发送 SIGINT 信号给被调试程序
(gdb) signal 2
# 查看当前信号处理设置
(gdb) info handle
5.8.2 handle命令选项详解
handle 命令的常用选项组合:
| 选项 | 说明 |
|---|---|
nostop |
GDB 不拦截该信号,直接传递给程序 |
stop |
GDB 拦截该信号并暂停程序(默认) |
pass / noignore |
将信号传递给被调试程序 |
nopass / ignore |
不将信号传递给被调试程序 |
print |
收到信号时打印信息(默认) |
noprint |
收到信号时不打印信息 |
选项组合效果对比:
| 组合 | GDB 行为 | 程序行为 | 典型场景 |
|---|---|---|---|
stop print pass |
暂停并显示 | 收到信号 | 默认行为 |
nostop noprint pass |
不干预 | 正常处理 | 忽略高频信号(如 SIGPIPE) |
stop print nopass |
暂停并显示 | 不收到信号 | 屏蔽特定信号 |
nostop noprint nopass |
完全忽略 | 不收到信号 | 彻底屏蔽 |
实战示例:
bash
# 网络编程中忽略 SIGPIPE(连接断开时不中断调试)
(gdb) handle SIGPIPE nostop noprint pass
# 屏蔽 SIGCHLD(避免子进程退出干扰)
(gdb) handle SIGCHLD nostop noprint pass
# 查看 SIGSEGV 的处理设置
(gdb) info handle SIGSEGV
Signal Stop Print Pass to program Description
SIGSEGV Yes Yes Yes Segmentation fault
5.8.3 常见信号与调试关系
| 信号 | 编号 | 含义 | 调试中的处理 |
|---|---|---|---|
| SIGINT | 2 | 中断(Ctrl+C) | 用于暂停程序 |
| SIGSEGV | 11 | 段错误 | 定位崩溃位置 |
| SIGABRT | 6 | 异常终止 | assert 失败、abort() |
| SIGPIPE | 13 | 管道断裂 | 网络编程中常忽略 |
| SIGCHLD | 17 | 子进程状态变化 | 多进程调试中常忽略 |
| SIGUSR1/2 | 10/12 | 用户自定义信号 | 可用于调试触发 |
5.9 源码与反汇编查看
本节介绍 GDB 中查看源代码和反汇编代码的方法,以及 TUI 窗口模式的使用。
5.9.1 源码列表(list命令)
| 命令 | 说明 |
|---|---|
list / l |
部分列出源代码(当前位置附近) |
l <line> |
列出指定行附近的源代码 |
l <func> |
列出指定函数附近的源代码 |
l <file>:<line> |
列出指定文件指定行附近的源代码 |
l <start>,<end> |
列出从 start 到 end 行的源代码 |
bash
# 列出当前位置附近的源码(默认 10 行)
(gdb) list
# 列出 main 函数附近的源码
(gdb) list main
# 列出指定文件的指定行
(gdb) list main.c:50
# 列出第 10 到第 30 行
(gdb) list 10,30
# 设置每次显示的行数
(gdb) set listsize 20
5.9.2 反汇编(disassemble命令)
| 命令 | 说明 |
|---|---|
disassemble / disas |
反汇编当前函数 |
disas <func> |
反汇编指定函数 |
disas <addr> |
反汇编指定地址 |
disas /m <func> |
混合源码和汇编 |
disas /r <func> |
显示原始机器码 |
bash
# 反汇编当前函数
(gdb) disas
# 反汇编指定函数
(gdb) disas myadd
# 混合源码和汇编(需要 -g 编译)
(gdb) disas /m myadd
# 反汇编指定地址范围
(gdb) disas 0x401149, 0x4011a5
输出解读(disas /m):
Dump of assembler code for function myadd:
4 int myadd(int a, int b) {
0x0000000000001129 <+0>: push %rbp
0x000000000000112a <+1>: mov %rsp,%rbp
5 return a + b;
0x000000000000112d <+4>: mov %edi,-0x4(%rbp)
0x0000000000001130 <+7>: mov %esi,-0x8(%rbp)
0x0000000000001133 <+10>: mov -0x4(%rbp),%eax
0x0000000000001136 <+13>: add -0x8(%rbp),%eax
6 }
0x0000000000001139 <+16>: pop %rbp
0x000000000000113a <+17>: ret
5.9.3 窗口模式(TUI)
| 命令 | 描述 |
|---|---|
layout |
用于分割窗口,可以一边查看代码,一边测试 |
layout src |
显示源代码窗口 |
layout asm |
显示反汇编窗口 |
layout regs |
显示源代码/反汇编和 CPU 寄存器窗口 |
layout split |
显示源代码和反汇编窗口 |
Ctrl + L |
刷新窗口 |
tui enable |
启用 TUI 模式 |
tui disable |
禁用 TUI 模式 |
TUI 布局对比:
| 布局命令 | 上半窗口 | 下半窗口 | 适用场景 |
|---|---|---|---|
layout src |
源代码 | GDB 命令 | 常规源码调试 |
layout asm |
反汇编 | GDB 命令 | 汇编级调试 |
layout split |
源代码 | 反汇编 | 对照源码和汇编 |
layout regs |
源码/汇编 | 寄存器 | 查看寄存器变化 |
启动 TUI 的方式:
bash
# 方式1:启动时进入 TUI
$ gdb -tui ./program
# 方式2:在 GDB 中切换
(gdb) tui enable
# 方式3:快捷键
Ctrl+X, Ctrl+A # 切换 TUI 模式
Ctrl+X, 1 # 单窗口
Ctrl+X, 2 # 双窗口
5.10 自定义命令与脚本
本节介绍 GDB 中自定义命令的语法和用法,以及内存导出和核心转储文件的生成。自定义命令可以显著提升重复性调试操作的效率。
5.10.1 define/document语法(自定义命令)
自定义命令:
define 命令名
命令
...
end
自定义命令说明:
document 命令名
命令说明
...
end
5.10.2 自定义命令示例
bash
# 定义一个显示当前 PC 开始 8 条指令的命令
(gdb) define li
>x /8i $pc
>end
# 添加命令说明
(gdb) document li
>显示当前地址开始的 8 条指令
>end
# 使用自定义命令
(gdb) li
更多自定义命令示例:
bash
# 定义显示当前栈帧完整信息的命令
(gdb) define fi
>info frame
>info args
>info locals
>end
# 定义显示所有线程堆栈的命令
(gdb) define allbt
>thread apply all bt
>end
# 定义带参数的命令($arg0, $arg1...)
(gdb) define hexdump
>x /$arg0xw $arg1
>end
# 使用:hexdump 16 $rsp
5.10.3 dump内存(导出内存到文件)
bash
# 将内存内容导出到文件
(gdb) dump memory file.log start_addr end_addr
# 示例:导出 0x1000 到 0x2000 的内存内容
(gdb) dump memory /tmp/mem_dump.bin 0x1000 0x2000
# 导出特定变量所在内存
(gdb) dump memory /tmp/var_dump.bin &my_struct &my_struct+sizeof(my_struct)
dump 命令的其他形式:
| 命令 | 说明 |
|---|---|
dump memory <file> <start> <end> |
导出原始内存 |
dump ihex memory <file> <start> <end> |
Intel HEX 格式 |
dump srec memory <file> <start> <end> |
S-record 格式 |
dump verilog memory <file> <start> <end> |
Verilog 格式 |
5.10.4 generate-core-file(生成核心转储)
bash
# 生成内核转储文件(查看调试历史、保存当前调试状态)
(gdb) generate-core-file
Saved corefile core.12345
用途:
| 场景 | 说明 |
|---|---|
| 保存调试现场 | 将当前程序状态保存为 core 文件,之后可重新加载分析 |
| 分享问题 | 将 core 文件发给同事协助分析 |
| 对比分析 | 在不同时间点生成 core 文件,对比状态变化 |
5.10.5 GDB Python脚本(高级自动化)
GDB 内置 Python 解释器,支持使用 Python 编写复杂的调试脚本和自定义命令。
基本用法:
bash
# 在 GDB 中执行 Python 代码
(gdb) python print("Hello from Python")
# 执行 Python 脚本文件
(gdb) source my_script.py
# 多行 Python 代码
(gdb) python
>import gdb
>val = gdb.parse_and_eval("counter")
>print(f"counter = {val}")
>end
Python 自定义命令示例:
python
# 文件:my_commands.py
import gdb
class PrintStructCmd(gdb.Command):
"""打印结构体成员及偏移"""
def __init__(self):
super().__init__("pstruct", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
val = gdb.parse_and_eval(arg)
for field in val.type.fields():
print(f" offset {field.bitpos//8:3d}: {field.name} = {val[field.name]}")
PrintStructCmd()
bash
# 加载并使用
(gdb) source my_commands.py
(gdb) pstruct my_struct
Python API 常用类:
| 类/函数 | 说明 |
|---|---|
gdb.Command |
自定义命令基类 |
gdb.Breakpoint |
Python 断点(支持 stop 回调) |
gdb.parse_and_eval() |
解析并求值表达式 |
gdb.execute() |
执行 GDB 命令 |
gdb.selected_frame() |
获取当前栈帧 |
gdb.selected_inferior() |
获取当前进程 |
gdb.lookup_symbol() |
查找符号 |
5.10.6 命令文件(source命令)
bash
# 将常用命令写入文件
$ cat debug_commands.gdb
b main
run
display i
display /x flags
# 在 GDB 中加载执行
(gdb) source debug_commands.gdb
# 或启动时通过 -x 加载
$ gdb -x debug_commands.gdb ./program
5.11 GDB命令速查总表
本节将 GDB 所有命令按功能分类汇总,方便快速查阅。
5.11.1 按功能分类汇总表
| 分类 | 命令 | 简写 | 说明 |
|---|---|---|---|
| 启动连接 | gdb <program> |
启动调试 | |
gdb --pid <pid> |
调试运行进程 | ||
gdb <program> core |
调试 coredump | ||
target remote <host>:<port> |
远程调试 | ||
| 执行控制 | run |
r |
开始执行 |
start |
运行到 main | ||
continue |
c |
继续执行 | |
next |
n |
单步(不进入函数) | |
step |
s |
单步(进入函数) | |
nexti |
ni |
指令级单步(不进入) | |
stepi |
si |
指令级单步(进入) | |
until |
u |
执行到指定位置 | |
finish |
fin |
执行到函数返回 | |
return <value> |
直接返回指定值 | ||
call <func> |
调用函数 | ||
quit |
q |
退出 | |
| 断点 | break |
b |
设置断点 |
tbreak |
tb |
临时断点 | |
watch |
写观察点 | ||
rwatch |
读观察点 | ||
awatch |
读写观察点 | ||
catch |
捕捉点 | ||
delete |
d |
删除断点 | |
disable |
dis |
禁用断点 | |
enable |
en |
启用断点 | |
ignore <num> <times> |
忽略断点 | ||
info breakpoints |
i b |
查看断点 | |
| 数据 | print |
p |
打印变量 |
x /<fmt> <addr> |
查看内存 | ||
display |
disp |
自动显示 | |
undisplay |
undisp |
删除自动显示 | |
set <var>=<val> |
设置变量 | ||
whatis |
查看类型 | ||
ptype |
查看详细类型 | ||
| 堆栈 | backtrace |
bt |
查看堆栈 |
bt full |
详细堆栈 | ||
frame <num> |
f <num> |
切换栈帧 | |
up / down |
上/下切换帧 | ||
info registers |
i r |
查看寄存器 | |
info frame |
i f |
查看帧信息 | |
info args |
i args |
查看函数参数 | |
info locals |
i locals |
查看局部变量 | |
| 线程 | info threads |
查看线程 | |
thread <ID> |
切换线程 | ||
thread apply all <cmd> |
所有线程执行命令 | ||
set scheduler-locking |
调度锁定 | ||
| 进程 | info inferiors |
查看进程 | |
inferior <ID> |
切换进程 | ||
attach / detach |
挂接/分离进程 | ||
set follow-fork-mode |
fork 跟踪 | ||
| 信号 | signal <num> |
发送信号 | |
handle <sig> nostop |
设置信号处理 | ||
| 窗口 | layout src/asm/regs/split |
TUI 布局 | |
Ctrl+L |
刷新窗口 | ||
| 自定义 | define |
定义命令 | |
document |
命令说明 | ||
dump memory |
导出内存 | ||
generate-core-file |
生成转储文件 | ||
| 反向调试 | record |
开始记录 | |
reverse-step |
rs |
反向单步 | |
reverse-next |
rn |
反向单步(不进函数) | |
reverse-continue |
rc |
反向继续 | |
reverse-finish |
反向执行到函数入口 |
5.11.2 常用快捷键
| 快捷键 | 说明 |
|---|---|
Ctrl+C |
主动暂停程序运行 |
Ctrl+D |
退出 GDB(同 quit) |
| 直接回车 | 重复上一次命令 |
Ctrl+L |
刷新 TUI 窗口 |
Ctrl+X, Ctrl+A |
切换 TUI 模式 |
Ctrl+X, 1 |
TUI 单窗口 |
Ctrl+X, 2 |
TUI 双窗口 |
5.12 GDB调试完整流程示例
以下是一个完整的调试会话示例,综合运用本章介绍的各类命令:
bash
# 编译(带调试信息)
$ gcc -O0 -g -ggdb -o myop main.c myop.c -lm
# 启动调试
$ gdb ./myop
# 设置运行参数并运行到 main
(gdb) set args 3 0
(gdb) start
# 在 mydiv 函数设置断点
(gdb) b mydiv
# 继续运行
(gdb) c
# 到达断点后查看堆栈
(gdb) bt
#0 mydiv (a=3, b=0) at myop.c:16
#1 0x... in main () at main.c:15
# 查看参数和局部变量
(gdb) info args
(gdb) info locals
# 查看内存
(gdb) x /4xw $rsp
# 修改变量值(绕过除零)
(gdb) set b=1
# 继续运行
(gdb) c
# 退出
(gdb) q
调试流程总结:
#mermaid-svg-ANcVE8P71LIRVhiM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ANcVE8P71LIRVhiM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ANcVE8P71LIRVhiM .error-icon{fill:#552222;}#mermaid-svg-ANcVE8P71LIRVhiM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ANcVE8P71LIRVhiM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ANcVE8P71LIRVhiM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ANcVE8P71LIRVhiM .marker.cross{stroke:#333333;}#mermaid-svg-ANcVE8P71LIRVhiM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ANcVE8P71LIRVhiM p{margin:0;}#mermaid-svg-ANcVE8P71LIRVhiM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ANcVE8P71LIRVhiM .cluster-label text{fill:#333;}#mermaid-svg-ANcVE8P71LIRVhiM .cluster-label span{color:#333;}#mermaid-svg-ANcVE8P71LIRVhiM .cluster-label span p{background-color:transparent;}#mermaid-svg-ANcVE8P71LIRVhiM .label text,#mermaid-svg-ANcVE8P71LIRVhiM span{fill:#333;color:#333;}#mermaid-svg-ANcVE8P71LIRVhiM .node rect,#mermaid-svg-ANcVE8P71LIRVhiM .node circle,#mermaid-svg-ANcVE8P71LIRVhiM .node ellipse,#mermaid-svg-ANcVE8P71LIRVhiM .node polygon,#mermaid-svg-ANcVE8P71LIRVhiM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ANcVE8P71LIRVhiM .rough-node .label text,#mermaid-svg-ANcVE8P71LIRVhiM .node .label text,#mermaid-svg-ANcVE8P71LIRVhiM .image-shape .label,#mermaid-svg-ANcVE8P71LIRVhiM .icon-shape .label{text-anchor:middle;}#mermaid-svg-ANcVE8P71LIRVhiM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ANcVE8P71LIRVhiM .rough-node .label,#mermaid-svg-ANcVE8P71LIRVhiM .node .label,#mermaid-svg-ANcVE8P71LIRVhiM .image-shape .label,#mermaid-svg-ANcVE8P71LIRVhiM .icon-shape .label{text-align:center;}#mermaid-svg-ANcVE8P71LIRVhiM .node.clickable{cursor:pointer;}#mermaid-svg-ANcVE8P71LIRVhiM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ANcVE8P71LIRVhiM .arrowheadPath{fill:#333333;}#mermaid-svg-ANcVE8P71LIRVhiM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ANcVE8P71LIRVhiM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ANcVE8P71LIRVhiM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ANcVE8P71LIRVhiM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ANcVE8P71LIRVhiM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ANcVE8P71LIRVhiM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ANcVE8P71LIRVhiM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ANcVE8P71LIRVhiM .cluster text{fill:#333;}#mermaid-svg-ANcVE8P71LIRVhiM .cluster span{color:#333;}#mermaid-svg-ANcVE8P71LIRVhiM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ANcVE8P71LIRVhiM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ANcVE8P71LIRVhiM rect.text{fill:none;stroke-width:0;}#mermaid-svg-ANcVE8P71LIRVhiM .icon-shape,#mermaid-svg-ANcVE8P71LIRVhiM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ANcVE8P71LIRVhiM .icon-shape p,#mermaid-svg-ANcVE8P71LIRVhiM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ANcVE8P71LIRVhiM .icon-shape .label rect,#mermaid-svg-ANcVE8P71LIRVhiM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ANcVE8P71LIRVhiM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ANcVE8P71LIRVhiM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ANcVE8P71LIRVhiM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
崩溃
编译带调试信息
启动 GDB
设置参数/断点
运行程序
到达断点?
查看状态: bt/p/x
找到问题?
单步/继续
修改/验证
继续或退出
分析崩溃: bt/info frame
5.13 GDB调试实战场景
本节通过多个真实调试场景,展示如何综合运用 GDB 的各种功能解决实际开发中遇到的问题。
5.13.1 段错误调试(定位崩溃位置)
段错误(Segmentation Fault)是最常见的程序崩溃类型,通常由访问非法内存地址引起。GDB 可以精确定位段错误发生的位置和原因。
方法一:直接运行复现
bash
$ gcc -O0 -g -ggdb -o program program.c
$ gdb ./program
(gdb) run
Starting program: /home/user/program
Program received signal SIGSEGV, Segmentation fault.
0x0000000000001149 in main () at main.c:6
6 *p = 'h';
# 查看崩溃时的堆栈
(gdb) bt
#0 0x0000000000001149 in main () at main.c:6
# 查看导致崩溃的变量
(gdb) p p
$1 = 0x402004 "Hello world!"
# 查看内存权限(只读段不可写)
(gdb) x /4xw p
0x402004: 0x6c6c6548 0x6f77206f 0x21646c72 0x00000000
方法二:通过 coredump 事后分析
bash
# 确保 coredump 已开启
$ ulimit -c unlimited
# 运行程序触发崩溃
$ ./program
Segmentation fault (core dumped)
# 用 GDB 加载 coredump
$ gdb ./program core
(gdb) bt
#0 0x0000000000001149 in main () at main.c:6
方法三:结合 addr2line(无 GDB 环境时)
bash
# 从 dmesg 获取出错地址
$ dmesg | tail -2
a.out[13275]: segfault at 491f44 ip 0000000000400b80 sp 00007ffecb47fd50 error 7
# 使用 addr2line 定位
$ addr2line -fap -e a.out 0x0000000000400b80
0x0000000000400b80: main at /home/user/main.c:6
段错误常见原因与排查:
| 原因 | 典型表现 | 排查方法 |
|---|---|---|
| 空指针解引用 | 访问地址为 0x0 或极小值 | bt 查看堆栈,p 检查指针 |
| 野指针 | 访问已释放的内存 | 结合 ASan 或 valgrind |
| 栈溢出 | 递归过深或大数组 | bt 查看调用深度,info frame |
| 写入只读段 | 修改字符串常量 | x 查看内存地址所在段 |
| 数组越界 | 访问超出分配范围的地址 | p 检查数组下标和大小 |
5.13.2 内存越界调试(watchpoint定位)
bash
$ gdb ./program
(gdb) b main
(gdb) run
# 在可疑的数组操作处设置观察点
(gdb) watch buf[10] # 假设 buf 大小为 10,监控越界写入
# 或者使用条件断点
(gdb) b loop_func if i >= ARRAY_SIZE
# 继续运行,观察何时越界
(gdb) c
Hardware watchpoint 2: buf[10]
Old value = 0
New value = 42
loop_func (i=10) at main.c:25
输出解读: 当观察点触发时,GDB 会显示旧值(Old value)和新值(New value),以及触发位置,从而精确定位越界写入的代码位置。
5.13.3 多线程死锁调试(thread apply all bt)
多线程死锁是并发编程中最难调试的问题之一。GDB 提供了完整的线程调试支持。
bash
$ gdb ./program
(gdb) run
# 程序卡住后,按 Ctrl+C 中断
^C
Thread 1 "program" received signal SIGINT, Interrupt.
# 查看所有线程状态
(gdb) info threads
Id Target Id Frame
* 1 Thread 0x7ffff7... (LWP 12345) "program" __lll_lock_wait ()
2 Thread 0x7ffff6... (LWP 12346) "program" __lll_lock_wait ()
3 Thread 0x7ffff5... (LWP 12347) "program" 0x... in pthread_cond_wait ()
# 查看所有线程的堆栈(死锁分析的核心命令)
(gdb) thread apply all bt
Thread 3 (Thread 0x7ffff5...):
#0 pthread_cond_wait ()
#1 worker_thread () at worker.c:45
Thread 2 (Thread 0x7ffff6...):
#0 __lll_lock_wait ()
#1 pthread_mutex_lock ()
#2 process_data () at process.c:30 ← 等待 mutex_b
#3 thread_func_b () at main.c:55
Thread 1 (Thread 0x7ffff7...):
#0 __lll_lock_wait ()
#1 pthread_mutex_lock ()
#2 process_data () at process.c:25 ← 等待 mutex_a
#3 thread_func_a () at main.c:40
死锁分析要点:
| 步骤 | 操作 | 目的 |
|---|---|---|
| 1 | thread apply all bt |
查看所有线程的调用栈 |
| 2 | 关注 __lll_lock_wait 帧 |
确认哪些线程在等锁 |
| 3 | 查看各线程等待的锁 | 找出锁的持有者 |
| 4 | 对比持有和等待关系 | 找出循环依赖 |
死锁模式识别:
#mermaid-svg-2qHt1MpJS1yl5nqt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2qHt1MpJS1yl5nqt .error-icon{fill:#552222;}#mermaid-svg-2qHt1MpJS1yl5nqt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2qHt1MpJS1yl5nqt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2qHt1MpJS1yl5nqt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2qHt1MpJS1yl5nqt .marker.cross{stroke:#333333;}#mermaid-svg-2qHt1MpJS1yl5nqt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2qHt1MpJS1yl5nqt p{margin:0;}#mermaid-svg-2qHt1MpJS1yl5nqt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt .cluster-label text{fill:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt .cluster-label span{color:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt .cluster-label span p{background-color:transparent;}#mermaid-svg-2qHt1MpJS1yl5nqt .label text,#mermaid-svg-2qHt1MpJS1yl5nqt span{fill:#333;color:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt .node rect,#mermaid-svg-2qHt1MpJS1yl5nqt .node circle,#mermaid-svg-2qHt1MpJS1yl5nqt .node ellipse,#mermaid-svg-2qHt1MpJS1yl5nqt .node polygon,#mermaid-svg-2qHt1MpJS1yl5nqt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2qHt1MpJS1yl5nqt .rough-node .label text,#mermaid-svg-2qHt1MpJS1yl5nqt .node .label text,#mermaid-svg-2qHt1MpJS1yl5nqt .image-shape .label,#mermaid-svg-2qHt1MpJS1yl5nqt .icon-shape .label{text-anchor:middle;}#mermaid-svg-2qHt1MpJS1yl5nqt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2qHt1MpJS1yl5nqt .rough-node .label,#mermaid-svg-2qHt1MpJS1yl5nqt .node .label,#mermaid-svg-2qHt1MpJS1yl5nqt .image-shape .label,#mermaid-svg-2qHt1MpJS1yl5nqt .icon-shape .label{text-align:center;}#mermaid-svg-2qHt1MpJS1yl5nqt .node.clickable{cursor:pointer;}#mermaid-svg-2qHt1MpJS1yl5nqt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2qHt1MpJS1yl5nqt .arrowheadPath{fill:#333333;}#mermaid-svg-2qHt1MpJS1yl5nqt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2qHt1MpJS1yl5nqt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2qHt1MpJS1yl5nqt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2qHt1MpJS1yl5nqt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2qHt1MpJS1yl5nqt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2qHt1MpJS1yl5nqt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2qHt1MpJS1yl5nqt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2qHt1MpJS1yl5nqt .cluster text{fill:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt .cluster span{color:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2qHt1MpJS1yl5nqt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2qHt1MpJS1yl5nqt rect.text{fill:none;stroke-width:0;}#mermaid-svg-2qHt1MpJS1yl5nqt .icon-shape,#mermaid-svg-2qHt1MpJS1yl5nqt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2qHt1MpJS1yl5nqt .icon-shape p,#mermaid-svg-2qHt1MpJS1yl5nqt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2qHt1MpJS1yl5nqt .icon-shape .label rect,#mermaid-svg-2qHt1MpJS1yl5nqt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2qHt1MpJS1yl5nqt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2qHt1MpJS1yl5nqt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2qHt1MpJS1yl5nqt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程1
持有 mutex_a
等待 mutex_b
mutex_b
被线程2持有
线程2
持有 mutex_b
等待 mutex_a
mutex_a
被线程1持有
5.13.4 条件断点调试循环(精确定位迭代)
在循环中调试时,条件断点可以精确定位到特定迭代:
bash
(gdb) b process_item if i == 999
(gdb) run
# 到达第 999 次迭代时自动中断
Breakpoint 1, process_item (i=999) at main.c:42
# 查看此时的变量状态
(gdb) info locals
(gdb) p data[i]
# 也可以使用 ignore 跳过前 N 次
(gdb) b process_item
(gdb) ignore 1 998 # 前 998 次不停止
(gdb) c # 第 999 次停止
条件断点 vs ignore 对比:
| 对比项 | 条件断点 | ignore |
|---|---|---|
| 语法 | b <loc> if <cond> |
ignore <num> <times> |
| 灵活性 | 支持任意表达式 | 只能跳过固定次数 |
| 性能 | 每次到达都检查条件 | 无额外开销 |
| 适用场景 | 复杂条件(如特定值) | 简单跳过前 N 次 |
5.13.5 动态库调试(info sharedlibrary)
bash
# 设置动态库搜索路径
(gdb) set environment LD_LIBRARY_PATH=./lib
# 在动态库函数中设置断点
(gdb) b myadd
(gdb) run
# 查看动态库加载信息
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0x00007f... 0x00007f... Yes ./libmyop.so
0x00007f... 0x00007f... Yes /lib/x86_64-linux-gnu/libc.so.6
# 捕捉动态库加载事件
(gdb) catch load libmyop.so
(gdb) run
动态库调试常见问题:
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 断点 PENDING | 库尚未加载 | set breakpoint pending on |
| 找不到库 | 路径不对 | set environment LD_LIBRARY_PATH=... |
| 无符号 | 库被 strip | 使用未 strip 的库或分离的调试信息 |
5.13.6 修改程序流程调试(验证假设)
GDB 允许在调试时修改程序执行流程和变量值,用于验证假设:
bash
# 修改变量值(绕过某个条件)
(gdb) p flag = 1
# 直接返回指定值(跳过当前函数)
(gdb) return 0
# 跳转到指定行(跳过某段代码)
(gdb) jump 50
# 调用函数验证
(gdb) call printf("debug: x=%d, y=%d\n", x, y)
修改流程命令对比:
| 命令 | 效果 | 风险 |
|---|---|---|
set var=val |
修改变量值 | 低,仅影响数据 |
return <val> |
立即返回 | 中,跳过清理代码 |
jump <line> |
跳转到指定行 | 高,可能跳过初始化 |
call <func> |
调用函数 | 中,有副作用 |
5.13.7 反向调试(record/reverse-step)
反向调试允许程序"倒着执行",在发现 bug 后回溯到问题产生的源头。
bash
# 开始记录程序执行
(gdb) record
# 正常运行到发现问题
(gdb) c
# ... 发现变量值不对 ...
# 反向单步(回退到上一条语句)
(gdb) reverse-step
# 反向单步(不进入函数)
(gdb) reverse-next
# 反向继续(回退到上一个断点)
(gdb) reverse-continue
# 反向执行到函数入口
(gdb) reverse-finish
# 停止记录
(gdb) record stop
反向调试命令对比:
| 正向命令 | 反向命令 | 说明 |
|---|---|---|
step |
reverse-step (rs) |
反向进入函数 |
next |
reverse-next (rn) |
反向不进入函数 |
continue |
reverse-continue (rc) |
反向到断点 |
finish |
reverse-finish |
反向到函数入口 |
stepi |
reverse-stepi |
反向指令级 |
注意事项:
| 注意点 | 说明 |
|---|---|
| 性能开销 | 记录模式会显著降低执行速度 |
| 记录长度 | 默认记录 200000 条指令,可通过 set record insn-number-max 调整 |
| 不支持的操作 | 设备 I/O 等不可逆操作无法回退 |
| 适用场景 | 定位"变量何时被错误修改"类问题 |
5.14 GDB调试技巧与最佳实践
本节总结 GDB 使用中的高级技巧和最佳实践,包括配置文件、命令别名、优化代码调试注意事项和常见问题的解决方案。
5.14.1 .gdbinit 配置技巧(自动化调试环境)
.gdbinit 文件在 GDB 启动时自动执行,可以预设常用配置,提升调试效率。
加载顺序与作用范围:
| 位置 | 加载顺序 | 作用范围 | 安全要求 |
|---|---|---|---|
/etc/gdb/gdbinit |
最先 | 系统全局 | 需编译时启用 |
~/.config/gdb/gdbinit 或 ~/.gdbinit |
其次 | 用户全局 | 无限制 |
./.gdbinit |
再次 | 项目级 | 需要 set auto-load local-gdbinit |
-x 指定的文件 |
最后 | 命令行指定 | 无限制 |
安全注意事项:
GDB 默认不加载当前目录的 .gdbinit(出于安全考虑),需要设置:
bash
# 在 ~/.gdbinit 中添加
set auto-load local-gdbinit
这是因为恶意的 .gdbinit 文件可能在用户不知情的情况下执行任意命令。如果经常在不同项目间切换,建议只信任自己创建的项目级 .gdbinit。
常用配置示例(~/.gdbinit):
bash
# ===== 打印格式设置 =====
set print pretty on # 结构体多行显示
set print array on # 数组多行显示
set print array-indexes on # 显示数组下标
set print elements 200 # 最多显示 200 个元素
set print null-stop on # 字符串遇 NULL 停止
# ===== 历史记录设置 =====
set history filename ~/.gdb_history
set history save on
set history size 10000
# ===== 自定义命令:显示当前 PC 开始的 8 条指令 =====
define li
x /8i $pc
end
document li
显示当前地址开始的 8 条指令
end
# ===== 自定义命令:显示当前栈帧的详细信息 =====
define fi
info frame
info args
info locals
end
# ===== 自定义命令:显示寄存器 =====
define regs
info registers
end
# ===== 自定义命令:所有线程堆栈 =====
define allbt
thread apply all bt
end
项目级 .gdbinit 示例(嵌入式交叉调试):
bash
# 项目级 .gdbinit(放在项目根目录)
# 自动连接目标板
target remote 192.168.110.125:2345
# 设置符号文件路径
symbol-file ./build/out.elf
# 设置常用断点
b main
b error_handler
配置分类建议:
| 配置类型 | 放置位置 | 示例 |
|---|---|---|
| 通用格式设置 | ~/.gdbinit |
print pretty、history |
| 通用自定义命令 | ~/.gdbinit |
li、fi、regs |
| 项目特定配置 | ./.gdbinit |
target remote、项目断点 |
| 临时调试脚本 | -x file.gdb |
一次性调试任务 |
5.14.2 GDB命令别名与缩写
| 完整命令 | 缩写 | 说明 |
|---|---|---|
break |
b |
设置断点 |
run |
r |
运行 |
continue |
c |
继续 |
next |
n |
单步(不进入函数) |
step |
s |
单步(进入函数) |
finish |
fin |
执行到函数返回 |
until |
u |
执行到指定位置 |
print |
p |
打印 |
display |
disp |
自动显示 |
backtrace |
bt |
查看堆栈 |
frame |
f |
切换栈帧 |
info |
i |
查看信息 |
delete |
d |
删除 |
disable |
dis |
禁用 |
enable |
en |
启用 |
quit |
q |
退出 |
自定义别名(GDB 9+):
bash
# 使用 alias 命令创建别名
(gdb) alias -a bb = break
(gdb) alias -a ss = set scheduler-locking
# 在 .gdbinit 中设置
alias -a bb = break
alias -a ss = set scheduler-locking
5.14.3 调试优化后代码的注意事项
使用 -O2 或更高优化级别编译的代码,调试时可能遇到以下问题:
| 问题 | 原因 | 解决方法 |
|---|---|---|
变量显示 <optimized out> |
变量被优化到寄存器或被消除 | 使用 -O0 或 -Og 重新编译 |
| 代码行号跳跃 | 代码被重排或内联 | 使用 disas 查看实际执行顺序 |
| 断点无法设置 | 代码被优化掉 | 使用地址断点 b *0x... |
| 函数不可见 | 函数被内联 | 使用 -fno-inline 编译 |
| 参数值不正确 | 参数通过寄存器传递被复用 | 使用 disas 确认实际传参 |
推荐调试编译选项:
bash
# 最佳调试体验
gcc -O0 -g -ggdb -o program program.c
# 需要一定性能时的调试
gcc -Og -g -ggdb -o program program.c
# 必须使用高优化级别时(如复现特定 bug)
gcc -O2 -g -ggdb -fno-omit-frame-pointer -o program program.c
优化级别与调试体验对比:
| 优化级别 | 调试体验 | 性能 | 适用场景 |
|---|---|---|---|
-O0 |
最佳(所有变量可见) | 最慢 | 日常开发调试 |
-Og |
良好(大部分变量可见) | 中等 | 需要一定性能的调试 |
-O1 |
一般 | 较快 | 平衡选择 |
-O2 |
较差(变量可能消失) | 快 | 复现优化相关 bug |
-O3 |
最差 | 最快 | 仅在必要时使用 |
5.14.4 常见调试问题与解决方案
| 问题 | 原因 | 解决方法 |
|---|---|---|
(no debugging symbols found) |
编译时未加 -g |
重新编译并添加 -g -ggdb |
No source file named xxx.c |
源文件路径不匹配 | 使用 directory 或 -d 指定源文件路径 |
断点显示 PENDING |
共享库尚未加载 | 使用 set breakpoint pending on |
Cannot access memory at address |
指针无效或程序已退出 | 检查指针值,确认程序仍在运行 |
| 观察点不触发 | 硬件观察点数量限制 | 减少观察点数量,或使用软件观察点 |
attach: ptrace: Operation not permitted |
权限不足 | 使用 sudo 或修改 ptrace_scope |
warning: Could not load shared library symbols |
找不到动态库 | set solib-search-path <path> |
Dwarf Error: wrong version in compilation unit header |
调试信息版本不匹配 | 更新 GDB 或重新编译 |
修改 ptrace 权限(Ubuntu):
bash
# 临时修改
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# 永久修改
$ sudo vim /etc/sysctl.d/10-ptrace.conf
# 将 kernel.yama.ptrace_scope = 1 改为 0
5.14.5 GDB与其他工具配合
| 场景 | 工具组合 | 说明 |
|---|---|---|
| 崩溃分析 | dmesg + addr2line + gdb |
先定位地址,再深入分析 |
| 内存问题 | valgrind / ASan + gdb |
先检测问题,再用 GDB 定位 |
| 性能问题 | perf + gdb |
先用 perf 找热点,再用 GDB 分析逻辑 |
| 系统调用 | strace + gdb |
先用 strace 确认系统调用行为 |
| 多线程 | pstack + gdb |
先用 pstack 快速查看栈,再用 GDB 深入 |
| 内核交互 | ftrace + gdb |
内核行为用 ftrace,用户态用 GDB |
工具配合流程图:
#mermaid-svg-VlmhmfwhUmKuwtPO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VlmhmfwhUmKuwtPO .error-icon{fill:#552222;}#mermaid-svg-VlmhmfwhUmKuwtPO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VlmhmfwhUmKuwtPO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VlmhmfwhUmKuwtPO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VlmhmfwhUmKuwtPO .marker.cross{stroke:#333333;}#mermaid-svg-VlmhmfwhUmKuwtPO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VlmhmfwhUmKuwtPO p{margin:0;}#mermaid-svg-VlmhmfwhUmKuwtPO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO .cluster-label text{fill:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO .cluster-label span{color:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO .cluster-label span p{background-color:transparent;}#mermaid-svg-VlmhmfwhUmKuwtPO .label text,#mermaid-svg-VlmhmfwhUmKuwtPO span{fill:#333;color:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO .node rect,#mermaid-svg-VlmhmfwhUmKuwtPO .node circle,#mermaid-svg-VlmhmfwhUmKuwtPO .node ellipse,#mermaid-svg-VlmhmfwhUmKuwtPO .node polygon,#mermaid-svg-VlmhmfwhUmKuwtPO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VlmhmfwhUmKuwtPO .rough-node .label text,#mermaid-svg-VlmhmfwhUmKuwtPO .node .label text,#mermaid-svg-VlmhmfwhUmKuwtPO .image-shape .label,#mermaid-svg-VlmhmfwhUmKuwtPO .icon-shape .label{text-anchor:middle;}#mermaid-svg-VlmhmfwhUmKuwtPO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VlmhmfwhUmKuwtPO .rough-node .label,#mermaid-svg-VlmhmfwhUmKuwtPO .node .label,#mermaid-svg-VlmhmfwhUmKuwtPO .image-shape .label,#mermaid-svg-VlmhmfwhUmKuwtPO .icon-shape .label{text-align:center;}#mermaid-svg-VlmhmfwhUmKuwtPO .node.clickable{cursor:pointer;}#mermaid-svg-VlmhmfwhUmKuwtPO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VlmhmfwhUmKuwtPO .arrowheadPath{fill:#333333;}#mermaid-svg-VlmhmfwhUmKuwtPO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VlmhmfwhUmKuwtPO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VlmhmfwhUmKuwtPO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VlmhmfwhUmKuwtPO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VlmhmfwhUmKuwtPO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VlmhmfwhUmKuwtPO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VlmhmfwhUmKuwtPO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VlmhmfwhUmKuwtPO .cluster text{fill:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO .cluster span{color:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VlmhmfwhUmKuwtPO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VlmhmfwhUmKuwtPO rect.text{fill:none;stroke-width:0;}#mermaid-svg-VlmhmfwhUmKuwtPO .icon-shape,#mermaid-svg-VlmhmfwhUmKuwtPO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VlmhmfwhUmKuwtPO .icon-shape p,#mermaid-svg-VlmhmfwhUmKuwtPO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VlmhmfwhUmKuwtPO .icon-shape .label rect,#mermaid-svg-VlmhmfwhUmKuwtPO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VlmhmfwhUmKuwtPO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VlmhmfwhUmKuwtPO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VlmhmfwhUmKuwtPO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 崩溃
内存
性能
系统调用
发现问题
问题类型?
dmesg 获取地址
addr2line 定位
GDB 深入分析
Valgrind/ASan 检测
perf record 采样
perf report 找热点
strace 跟踪
5.14.6 GDB实用设置速查
| 设置命令 | 说明 | 推荐值 |
|---|---|---|
set print pretty on |
结构体美化显示 | on |
set print array on |
数组美化显示 | on |
set print elements 0 |
不限制数组显示长度 | 0 或 200 |
set history save on |
保存命令历史 | on |
set history size 10000 |
历史记录条数 | 10000 |
set pagination off |
关闭分页(脚本模式) | 脚本时 off |
set confirm off |
关闭确认提示 | 可选 |
set breakpoint pending on |
允许 pending 断点 | on |
set follow-fork-mode child |
跟踪子进程 | 按需 |
set scheduler-locking step |
单步时锁定线程 | 多线程调试时 |
5.15 本章小结
GDB 是 Linux 下最核心的调试工具,掌握其核心功能对嵌入式开发和系统编程至关重要。本章覆盖了 GDB 的完整知识体系:
#mermaid-svg-uCLa1fopgyce9Xiq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uCLa1fopgyce9Xiq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uCLa1fopgyce9Xiq .error-icon{fill:#552222;}#mermaid-svg-uCLa1fopgyce9Xiq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uCLa1fopgyce9Xiq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uCLa1fopgyce9Xiq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uCLa1fopgyce9Xiq .marker.cross{stroke:#333333;}#mermaid-svg-uCLa1fopgyce9Xiq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uCLa1fopgyce9Xiq p{margin:0;}#mermaid-svg-uCLa1fopgyce9Xiq .edge{stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .section--1 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section--1 path,#mermaid-svg-uCLa1fopgyce9Xiq .section--1 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section--1 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section--1 text{fill:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth--1{stroke-width:17;}#mermaid-svg-uCLa1fopgyce9Xiq .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-0 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-0 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-0 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-0 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-0 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-0{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-0{stroke-width:14;}#mermaid-svg-uCLa1fopgyce9Xiq .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-1 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-1 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-1 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-1 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-1 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-1{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-1{stroke-width:11;}#mermaid-svg-uCLa1fopgyce9Xiq .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-2 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-2 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-2 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-2 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-2 text{fill:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-2{stroke-width:8;}#mermaid-svg-uCLa1fopgyce9Xiq .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-3 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-3 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-3 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-3 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-3 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-3{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-3{stroke-width:5;}#mermaid-svg-uCLa1fopgyce9Xiq .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-4 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-4 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-4 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-4 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-4 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-4{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-4{stroke-width:2;}#mermaid-svg-uCLa1fopgyce9Xiq .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-5 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-5 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-5 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-5 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-5 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-5{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-5{stroke-width:-1;}#mermaid-svg-uCLa1fopgyce9Xiq .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-6 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-6 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-6 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-6 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-6 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-6{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-6{stroke-width:-4;}#mermaid-svg-uCLa1fopgyce9Xiq .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-7 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-7 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-7 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-7 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-7 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-7{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-7{stroke-width:-7;}#mermaid-svg-uCLa1fopgyce9Xiq .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-8 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-8 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-8 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-8 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-8 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-8{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-8{stroke-width:-10;}#mermaid-svg-uCLa1fopgyce9Xiq .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-9 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-9 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-9 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-9 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-9 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-9{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-9{stroke-width:-13;}#mermaid-svg-uCLa1fopgyce9Xiq .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-10 rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-10 path,#mermaid-svg-uCLa1fopgyce9Xiq .section-10 circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-10 polygon,#mermaid-svg-uCLa1fopgyce9Xiq .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-10 text{fill:black;}#mermaid-svg-uCLa1fopgyce9Xiq .node-icon-10{font-size:40px;color:black;}#mermaid-svg-uCLa1fopgyce9Xiq .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .edge-depth-10{stroke-width:-16;}#mermaid-svg-uCLa1fopgyce9Xiq .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled,#mermaid-svg-uCLa1fopgyce9Xiq .disabled circle,#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:lightgray;}#mermaid-svg-uCLa1fopgyce9Xiq .disabled text{fill:#efefef;}#mermaid-svg-uCLa1fopgyce9Xiq .section-root rect,#mermaid-svg-uCLa1fopgyce9Xiq .section-root path,#mermaid-svg-uCLa1fopgyce9Xiq .section-root circle,#mermaid-svg-uCLa1fopgyce9Xiq .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-uCLa1fopgyce9Xiq .section-root text{fill:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .section-root span{color:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .section-2 span{color:#ffffff;}#mermaid-svg-uCLa1fopgyce9Xiq .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-uCLa1fopgyce9Xiq .edge{fill:none;}#mermaid-svg-uCLa1fopgyce9Xiq .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-uCLa1fopgyce9Xiq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GDB调试
启动与连接
本地调试
远程交叉调试
Coredump调试
附加运行进程
执行控制
运行/停止
单步执行
执行到指定位置
函数调用
断点与观察点
条件断点
观察点
捕捉点
断点管理
数据操作
内存查看x
变量打印p
自动显示display
变量修改set
堆栈分析
backtrace
栈帧切换
寄存器查看
高级调试
多线程调试
多进程调试
信号处理
反向调试
自定义命令
Python脚本
核心命令记忆口诀:
| 类别 | 核心命令 |
|---|---|
| 运行 | r(run)、c(continue)、start |
| 单步 | n(next)、s(step)、fin(finish)、u(until) |
| 断点 | b(break)、watch、tb(tbreak) |
| 查看 | p(print)、x、bt(backtrace)、disp(display) |
| 堆栈 | bt、f(frame)、up、down |
| 线程 | info threads、thread N、thread apply all bt |
| 进程 | attach、detach、set follow-fork-mode |
| 管理 | d(delete)、dis(disable)、en(enable)、ignore |
| 反向 | record、rs(reverse-step)、rn(reverse-next)、rc(reverse-continue) |
GDB 调试场景速查总表:
| 调试场景 | 核心命令组合 | 说明 |
|---|---|---|
| 程序崩溃 | run → bt → p → x |
运行→堆栈→变量→内存 |
| 逻辑错误 | b → n/s → p → display |
断点→单步→打印→监控 |
| 内存越界 | watch → c → bt |
观察点→继续→堆栈 |
| 死锁 | Ctrl+C → thread apply all bt |
中断→所有线程堆栈 |
| 性能相关 | record → rs/rn |
记录→反向调试 |
| 远程调试 | target remote → b → c |
连接→断点→继续 |
| 事后分析 | gdb program core → bt |
加载→堆栈 |