3. 启动与退出GDB:gdb命令的多种启动方式
调试器都启动不了,还谈什么调试?
这一节,我们来聊聊GDB的启动方式。说实话,我见过不少新手,只知道gdb ./a.out这一种用法。但实际工作中,场景远比这复杂。你想想看——程序已经跑起来了,突然卡死,你怎么办?程序崩溃了,core文件躺在那,你又怎么分析?
嗯,今天我把这些场景都给你捋一遍。
3.1 最基础的方式:直接加载可执行文件
这是最常用的方式,没有之一。
gdb ./my_program
启动后,GDB会加载my_program的符号表。这时候你还没运行程序,可以设置断点、查看源码。我个人习惯在启动时顺便带上参数:
gdb --args ./my_program --port=8080 --verbose
这样就不用进GDB后再敲set args了,省一步是一步。
-g选项,符号表是空的。你看到的只有地址,没有函数名和行号。我曾经在客户现场遇到过这种情况——对方说“我加了-g啊”,结果一看Makefile,-g被优化选项覆盖了。所以,编译时务必加上-g。
3.2 事后分析:加载core文件
程序崩溃了,生成了core dump。这时候别急着重启,先分析core文件。
gdb ./my_program core.12345
启动后,GDB会自动定位到崩溃点。你会看到类似这样的信息:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00005555555546a7 in foo (p=0x0) at main.c:15
15 *p = 42;
看到p=0x0了吗?空指针解引用。这就是崩溃的直接原因。
ulimit -c unlimited。我曾经帮一个同事排查问题,搞了半天才发现他根本没开core dump。嗯,这个坑我踩过。
3.3 动态调试:attach到正在运行的进程
程序跑着跑着卡住了,或者CPU飙到100%。这时候你不能重启它,因为状态丢了。怎么办?attach上去。
gdb -p 12345
或者先启动GDB,再attach:
gdb
(gdb) attach 12345
attach之后,程序会被暂停。你可以查看堆栈、变量、线程状态。分析完了,用detach让程序继续跑。
recv()系统调用上阻塞了——对方没发数据,它就这么傻等着。加上超时机制后,问题解决。
3.4 其他启动方式
除了上面三种,还有几种场景你可能会遇到:
| 场景 | 命令 | 说明 |
|---|---|---|
| 调试子进程 | gdb --follow-fork-mode child |
fork后跟踪子进程 |
| 远程调试 | gdb --remote localhost:1234 |
连接gdbserver |
| 静默模式 | gdb -q ./my_program |
不显示版本信息 |
| 批处理模式 | gdb -batch -x script.gdb ./my_program |
自动执行脚本后退出 |
批处理模式我特别喜欢。写个GDB脚本,自动化分析core文件,省时省力。
3.5 退出GDB
退出很简单:
(gdb) quit
(gdb) q
如果你attach了进程,退出时GDB会问你要不要detach。默认是detach,程序继续跑。如果你想杀掉被调试的进程,用:
(gdb) kill
(gdb) quit
嗯,这里要注意:如果你调试的是生产环境的服务,千万别手滑选了kill。我曾经见过有人调试线上服务,退出时忘了detach,直接kill掉了进程。后果嘛……你懂的。
3.6 本章知识体系
下面这张图,帮你理清GDB启动的几种方式及其适用场景:
这张图把GDB的启动方式分成了五类。核心区别在于:你是调试一个还没跑的程序,还是正在跑的程序,还是已经挂了的程序。搞清楚这个,你就知道该用哪种方式了。
3.7 避坑指南
最后,分享几个我踩过的坑:
- 权限问题: attach别人的进程时,你可能没有权限。用
sudo gdb -p PID,但注意别乱改生产环境的东西。 - core文件路径: 默认core文件生成在程序的工作目录。如果你找不到,检查
/proc/sys/kernel/core_pattern。 - 符号表不匹配: 加载core文件时,可执行文件必须和生成core文件时是同一个版本。否则你看到的堆栈可能是乱的。
- 退出时注意: attach到进程后退出,默认是detach。如果你用
kill命令杀掉了进程,那就真的杀掉了。
嗯,启动和退出就这么点事。但别小看它——启动方式选对了,调试就成功了一半。