1. GDB入门
1.1 GDB到底是什么?
GDB,全称GNU Debugger。说白了,就是一个让你能「看穿」程序运行过程的工具。
我刚开始接触GDB时,觉得它就是个黑乎乎的终端工具。远不如IDE里点几下鼠标来得方便。但后来我遇到一个诡异的崩溃问题——程序在Release模式下跑得好好的,Debug模式下却时不时崩溃。IDE的调试器根本抓不住规律。最后是GDB帮我定位到了问题:一个未初始化的成员变量。
从那以后,我对GDB的看法彻底变了。
GDB能做什么?简单来说:
- 让你在任意位置暂停程序(断点)
- 查看变量、内存、寄存器的值
- 单步执行,观察每一行代码的效果
- 修改程序状态,测试不同场景
- 分析崩溃现场(core dump)
嗯,这些功能IDE调试器也都有。但GDB有一个杀手锏——它不依赖图形界面。这意味着你可以在服务器上、在嵌入式设备上、在没有显示器的环境里调试程序。这一点,我在远程调试Qt程序时深有体会。
1.2 GDB与Qt的关系
Qt程序本质上就是C++程序。所以GDB能调试C++程序,自然也能调试Qt程序。
但这里有个坑——Qt程序用了很多宏、信号槽、元对象系统。这些东西在编译后会被展开成复杂的代码。如果你直接用GDB调试,看到的可能是moc生成的文件,而不是你写的代码。
我个人习惯的做法是:
- 编译时加上
-g选项,保留调试符号 - 不要使用
-O2或更高优化级别,否则变量可能被优化掉 - 对于Qt项目,建议使用
qmake CONFIG+=debug或CMake的Debug配置
举个例子,你在Qt里写了一个信号槽连接:
connect(button, &QPushButton::clicked, this, &MyClass::onClicked);
在GDB里,你看到的可能是:
QMetaObject::Connection QObject::connect(const QObject *, const char *,
const QObject *, const char *,
Qt::ConnectionType)
是不是有点懵?别担心,后面我会专门讲怎么在GDB里处理Qt的元对象系统。
qSetMessagePattern("[%{file}:%{line}] %{message}"),这样GDB配合日志,定位问题会快很多。
1.3 GDB的安装
安装GDB其实很简单。不同系统略有差异,我列个表方便你对照:
| 操作系统 | 安装命令 | 备注 |
|---|---|---|
| Ubuntu/Debian | sudo apt install gdb |
一般系统自带,版本较新 |
| CentOS/RHEL | sudo yum install gdb |
可能需要启用EPEL源 |
| macOS | brew install gdb |
需要签名,否则无法调试 |
| Windows | MinGW或MSYS2自带 | 推荐使用Qt自带的MinGW |
安装完成后,在终端输入 gdb --version,看到版本信息就说明装好了。
codesign 签名才能正常使用。我曾经在这上面折腾了半天,后来发现直接用 lldb 反而更省事。如果你在macOS上开发Qt,也可以考虑用LLDB,命令和GDB基本兼容。
1.4 启动GDB的几种方式
启动GDB的方式有好几种,我根据场景不同会选择不同的方式:
方式一:直接调试可执行文件
gdb ./my_qt_app
这是最常用的方式。启动后,GDB会加载程序,但不会立即运行。你可以先设置断点,然后输入 run 启动。
方式二:调试正在运行的程序
gdb -p 12345 # 12345是进程ID
这个场景我遇到过好几次——程序跑起来后卡住了,或者CPU飙到100%。这时候用 -p 附加到进程,就能看到它在干什么。
方式三:分析core dump文件
gdb ./my_qt_app core.12345
程序崩溃后,如果生成了core dump文件,用这种方式可以查看崩溃时的现场。我个人强烈建议在开发环境开启core dump:
ulimit -c unlimited # 取消core文件大小限制
方式四:Qt Creator集成
其实Qt Creator底层用的就是GDB。你在IDE里点「调试」按钮时,背后就是GDB在工作。但有时候IDE会隐藏一些细节,比如某些变量值被优化掉了,IDE显示「optimized out」,这时候切换到终端用GDB,反而能看到更多信息。
1.5 本章知识体系
下面这张图帮你梳理本章的核心内容:
这张图把本章的三个核心内容串起来了。GDB是什么解决「是什么」的问题,GDB与Qt的关系解决「为什么重要」的问题,安装与启动解决「怎么用」的问题。三者缺一不可。
gdb --version 查看版本号,如果太低,考虑从源码编译安装。