2、GDB核心概念:断点、观察点、捕获点、调试符号、程序状态

说实话,GDB 这东西,你把它拆开来看,核心就那么几个概念。我当年刚学的时候,被一堆术语搞得晕头转向。后来发现,只要你搞懂了断点、观察点、捕获点、调试符号和程序状态这五样东西,GDB 的大门基本就对你敞开了。

这五个概念,说白了就是:你在哪停、你盯着谁、你抓什么事件、你怎么知道代码对应哪、程序现在啥样。咱们一个一个来聊。

核心一句话:调试的本质,就是让程序在特定时刻停下来,然后你去看它的状态。这五个概念,全是围绕这个目的服务的。

2.1 断点(Breakpoint)—— 最常用的“刹车”

断点,就是告诉 GDB:“嘿,程序跑到这一行,给我停下来!”

我个人习惯,调试 C/C++ 程序时,最常用的就是行号断点和函数断点。

// 行号断点
(gdb) break main.c:42

// 函数断点
(gdb) break my_function

// 条件断点(只有满足条件才停)
(gdb) break main.c:42 if count > 100

我在项目中遇到过一个问题:一个循环跑了十万次,到第九万九千次才崩溃。你总不能手动按 c 九万次吧?这时候条件断点就救命了。设一个 if i == 99000,直接一步到位。

小技巧:用 info breakpoints 查看所有断点。用 disable 临时禁用,不用每次都删掉重设。

2.2 观察点(Watchpoint)—— 盯着变量不放

断点是停在一行代码上。观察点呢?它是盯着一个变量或内存地址。只要这个值变了,GDB 就自动停下来。

你想想看,有时候你根本不知道变量是在哪一行被改坏的。断点一个个设,太累了。观察点直接帮你抓“凶手”。

// 观察变量
(gdb) watch my_var

// 观察内存地址
(gdb) watch *0x7fffffffe000

// 观察读操作(硬件支持才行)
(gdb) rwatch my_var

// 观察读写操作
(gdb) awatch my_var

我曾经排查过一个内存越界问题。一个全局数组,莫名其妙被改写了。我设了 watch my_array[100],程序一跑,GDB 立刻停在了那个越界写入的地方。嗯,那一刻的感觉,就像在黑暗里突然开了灯。

注意:观察点是有代价的。软件观察点会让程序跑得极慢。硬件观察点数量有限(通常 4 个)。别一口气设几十个,不然你的电脑会哭的。

2.3 捕获点(Catchpoint)—— 抓异常和系统事件

捕获点,是 GDB 里比较“高级”的玩法。它不盯着某一行代码,也不盯着某个变量。它盯着事件——比如 C++ 异常抛出、系统调用、库加载等等。

// 捕获 C++ 异常
(gdb) catch throw

// 捕获系统调用
(gdb) catch syscall write

// 捕获 fork
(gdb) catch fork

我记得有一次,一个 C++ 程序老是莫名其妙地崩溃,但就是抓不到在哪抛的异常。我设了个 catch throw,然后 backtrace 一看,好家伙,异常是在一个深埋了三层的回调函数里抛出来的。没有捕获点,这问题够我查一整天。

类型 命令 用途
断点 break 停在指定代码位置
观察点 watch 变量值变化时停
捕获点 catch 事件发生时停

2.4 调试符号(Debug Symbols)—— 让机器码说人话

你编译程序的时候,如果不加 -g 选项,GDB 看到的全是地址和汇编。加了 -g,GDB 才能告诉你:main.c:42 对应的是哪一行,my_var 的值是多少。

说白了,调试符号就是一张“翻译表”。它把机器地址翻译成你熟悉的函数名、变量名、行号。

// 编译时加调试符号
gcc -g -o my_program my_program.c

// 查看是否有调试符号
(gdb) info sources

// 查看符号表
(gdb) info functions
(gdb) info variables

重要:发布版本通常用 -g -O2 编译。优化后的代码,行号对应关系会有点“飘”,但总比没有强。我见过有人为了性能不加 -g,结果线上出问题只能看汇编——那叫一个酸爽。

2.5 程序状态(Program State)—— 停下来之后看什么

程序停下来了,然后呢?你得看它的“体检报告”。GDB 提供了全套工具,让你把程序的五脏六腑翻个底朝天。

// 看调用栈
(gdb) backtrace

// 看当前栈帧的局部变量
(gdb) info locals

// 看当前函数的参数
(gdb) info args

// 看寄存器
(gdb) info registers

// 看内存
(gdb) x/10x 0x7fffffffe000

// 看线程
(gdb) info threads

我个人最常用的组合是:backtrace 看调用链,frame N 切到指定层,然后 info locals 看变量。这一套下来,90% 的问题都能定位到。

避坑指南:我曾经在调试多线程程序时,只看了当前线程的栈,结果死活找不到问题。后来才发现,是另一个线程把共享数据写坏了。记住:多线程调试,一定要用 thread apply all bt 看所有线程的栈。

知识体系总览

下面这张图,是我自己总结的 GDB 核心概念关系图。你看一眼,心里就有谱了。

GDB 调试核心 控制执行流 断点 (break) 观察点 (watch) 捕获点 (catch) 查看与分析 调试符号 (-g) 调用栈 (backtrace) 变量/内存/寄存器 停下来 → 看状态 → 找到问题 这就是 GDB 调试的完整闭环

这五个概念,你掌握了,GDB 就算入门了。剩下的,就是多练。我刚开始用 GDB 的时候,也是记不住命令,经常 help 翻半天。没关系,用多了就熟了。

记住一句话:调试不是猜谜,是取证。断点、观察点、捕获点就是你的取证工具,调试符号是翻译器,程序状态是证据本身。用好它们,bug 无处可逃。

可可精品资料库