3. 启动与退出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了吗?空指针解引用。这就是崩溃的直接原因。

注意: 很多Linux系统默认不生成core文件。你需要先执行ulimit -c unlimited。我曾经帮一个同事排查问题,搞了半天才发现他根本没开core dump。嗯,这个坑我踩过。

3.3 动态调试:attach到正在运行的进程

程序跑着跑着卡住了,或者CPU飙到100%。这时候你不能重启它,因为状态丢了。怎么办?attach上去。

gdb -p 12345

或者先启动GDB,再attach:

gdb
(gdb) attach 12345

attach之后,程序会被暂停。你可以查看堆栈、变量、线程状态。分析完了,用detach让程序继续跑。

实战场景: 我记得有一次,一个后台服务突然不响应了。我attach上去,发现它在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启动方式 直接加载可执行文件 gdb ./my_program gdb --args ./my_program ... 加载core文件 gdb ./prog core.12345 事后分析崩溃现场 attach到进程 gdb -p 12345 动态调试运行中程序 远程调试 gdb --remote host:port 嵌入式/跨平台调试 批处理模式 gdb -batch -x script.gdb 自动化分析/CI集成 退出: quit / q

这张图把GDB的启动方式分成了五类。核心区别在于:你是调试一个还没跑的程序,还是正在跑的程序,还是已经挂了的程序。搞清楚这个,你就知道该用哪种方式了。

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 权限问题: attach别人的进程时,你可能没有权限。用sudo gdb -p PID,但注意别乱改生产环境的东西。
  • core文件路径: 默认core文件生成在程序的工作目录。如果你找不到,检查/proc/sys/kernel/core_pattern。
  • 符号表不匹配: 加载core文件时,可执行文件必须和生成core文件时是同一个版本。否则你看到的堆栈可能是乱的。
  • 退出时注意: attach到进程后退出,默认是detach。如果你用kill命令杀掉了进程,那就真的杀掉了。

嗯,启动和退出就这么点事。但别小看它——启动方式选对了,调试就成功了一半。