调试技巧:GDB基础命令、断点与观察点、core dump分析、AddressSanitizer
调试这事儿,说白了就是跟程序「对质」。你写代码时觉得逻辑天衣无缝,跑起来却翻车——这种事我见得太多了。刚入行那会儿,我习惯到处加 printf 打日志,后来发现这招在复杂场景下根本不够用。真正靠谱的,还是得靠专业工具。
今天咱们聊四个硬核调试手段:GDB 基础操作、断点与观察点、core dump 分析,还有 AddressSanitizer。每个都是我实际项目中反复用过的,希望能帮你少走弯路。
GDB 基础命令:跟程序面对面
GDB 是 Linux 下最经典的调试器。你想想看,程序跑着跑着崩了,你总得知道它死在哪一行、变量值是什么吧?GDB 就是干这个的。
先说说怎么启动。编译时记得加 -g 选项,保留调试符号:
gcc -g -o myapp myapp.c
gdb ./myapp
进去之后,最常用的几个命令我列一下:
| 命令 | 作用 | 我常用的场景 |
|---|---|---|
run / r | 启动程序 | 刚设好断点,跑一把看看 |
break / b | 设置断点 | 怀疑某行有问题,停住检查 |
next / n | 单步执行(不进入函数) | 一步步走,看流程对不对 |
step / s | 单步执行(进入函数) | 想看看函数内部干了什么 |
print / p | 打印变量值 | 检查变量是不是预期值 |
backtrace / bt | 查看调用栈 | 程序崩了,看谁调了谁 |
continue / c | 继续执行 | 当前断点看完了,放行 |
quit / q | 退出 GDB | 收工 |
我个人习惯,一进 GDB 先设几个关键断点,然后 r 跑起来。如果程序崩了,第一时间打 bt 看调用栈——这能帮你快速定位到崩溃现场。
list 命令可以查看当前行附近的源码。配合 break 行号 设断点,效率很高。
断点与观察点:精准卡位
断点不光是停在某一行。你还可以设条件断点,比如:
(gdb) break 42 if i > 100
这样只有当 i 大于 100 时才会停下来。我在项目中遇到过循环跑几万次的情况,不加条件根本没法调试。
观察点(watchpoint)就更厉害了。它能监控某个变量,一旦值发生变化就停下来:
(gdb) watch my_var
嗯,这里要注意:观察点对性能有影响,别在超大循环里乱用。我曾经在一个嵌入式项目里用 watch 监控全局变量,结果程序跑得比蜗牛还慢——后来改成条件断点才搞定。
还有硬件断点和软件断点的区别。简单说,硬件断点数量有限(一般 4 个),但不会影响代码执行速度。软件断点无限,但会修改指令。日常调试用软件断点就够了。
Core Dump 分析:事后诸葛亮也得当
程序崩了没来得及调试?别急,core dump 就是你的「事后现场照片」。它记录了程序崩溃那一刻的内存快照。
首先确保系统允许生成 core 文件:
ulimit -c unlimited
然后运行程序,崩溃后就会生成 core 文件。用 GDB 加载它:
gdb ./myapp core
进去之后,直接打 bt 看调用栈。我一般会接着打 frame N 切换到具体栈帧,然后用 p 打印局部变量。这样基本能还原出事发经过。
-g,否则 core 文件里只有地址没有符号信息,分析起来很痛苦。
我曾经接手过一个线上崩溃的 bug,用户反馈说「偶尔崩一次」。本地死活复现不了,最后靠 core dump 分析才发现是某个指针在特定条件下没初始化。嗯,从那以后我写代码都会刻意检查指针初始化。
AddressSanitizer:内存错误的照妖镜
说到内存错误——越界、野指针、释放后使用——这些是 C 程序员的噩梦。AddressSanitizer(简称 ASan)就是专门抓这些问题的。
用法很简单,编译时加两个选项:
gcc -fsanitize=address -g -o myapp myapp.c
然后正常跑程序。如果触发了内存错误,ASan 会直接打印出详细的错误信息,包括:
- 错误类型(越界、释放后使用等)
- 发生位置(文件名、行号)
- 分配和释放的调用栈
举个例子,你写了个越界:
int arr[10];
arr[10] = 42; // 越界!
ASan 跑起来会输出类似这样的信息:
==12345==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc...
WRITE of size 4 at 0x7ffc... thread T0
#0 0x... in main /home/user/myapp.c:5
#1 0x... in __libc_start_main ...
#2 0x... in _start ...
你看,它直接告诉你第 5 行有问题。省去了你一行行排查的时间。
知识体系总览
下面这张图帮你理清这四种调试手段的关系:
说白了,这四种手段各有侧重。GDB 是日常主力,断点和观察点是精准武器,core dump 是事后补救,ASan 是预防性检查。我个人的工作流是:开发时开 ASan,遇到问题用 GDB 加断点,线上崩溃靠 core dump 复盘。这套组合拳打下来,绝大多数 bug 都无所遁形。
调试不是丢人的事。恰恰相反,能把调试工具用得溜,说明你对程序的掌控力强。别怕花时间学这些工具——它们会在你最需要的时候拉你一把。