2、GDB快速上手:编译带调试信息的程序、启动GDB、运行程序、退出GDB
好,咱们正式开始动手了。这一章的目标很简单——让你能在5分钟内跑通GDB的基本流程。说白了,就是学会三件事:怎么编译出带调试信息的程序、怎么把GDB叫出来、怎么让它跑起来再退出去。
别小看这几步。我见过太多新手卡在第一步——编译参数没加对,结果GDB进去一脸懵,符号表全是问号。嗯,咱们先把地基打牢。
2.1 编译带调试信息的程序
GDB要能看懂你的代码,需要编译器把调试信息塞进可执行文件里。这个开关就是 -g 选项。
核心命令:
gcc -g -o myprogram myprogram.c
g++ -g -o myprogram myprogram.cpp # C++项目同理
我个人习惯在开发阶段永远带着 -g 编译。为什么?因为你永远不知道下一秒会不会崩。我曾经在一个项目里为了省那几百KB的磁盘空间,去掉了 -g,结果线上出core dump,反汇编看了半天才定位到问题。从那以后,我宁愿编译完用 strip 命令剥离符号,也绝不在开发阶段省这个参数。
这里有个小细节:-g 不会影响程序的运行逻辑,它只是往文件里多写了一张「符号地图」。这张地图让GDB能把地址翻译成变量名、行号、函数名。
| 编译选项 | 效果 | 文件大小 |
|---|---|---|
-g |
包含完整调试信息(变量名、行号、宏定义等) | 较大 |
-g0 |
不包含调试信息 | 较小 |
-g3 |
包含额外信息(如宏定义展开) | 最大 |
我的建议:日常调试用 -g 就够了。如果你需要调试宏定义,可以用 -g3。但别用 -g3 编译整个项目,文件会大得离谱。
2.2 启动GDB
编译好了,接下来把程序交给GDB。启动方式有两种,看你习惯哪种。
方式一:直接加载可执行文件
gdb myprogram
这是最常用的方式。GDB启动后,会加载程序符号表,然后停在(gdb)提示符下等你发号施令。
方式二:先启动GDB,再加载文件
gdb
(gdb) file myprogram
这种方式适合你忘了文件名,或者想先看看GDB版本信息。我个人很少用,除非是在调试脚本里动态切换目标文件。
启动后你会看到类似这样的输出:
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
...
Reading symbols from myprogram...
(gdb)
看到 Reading symbols from myprogram... 这行,就说明调试信息加载成功了。如果显示 No debugging symbols found,那你得回去检查编译命令——八成是忘了加 -g。
注意:如果你在嵌入式环境下调试,可能需要指定远程调试目标。比如 gdb myprogram.elf 后,再用 target remote localhost:2331 连接开发板。这个咱们后面章节会细讲。
2.3 运行程序
程序加载好了,怎么让它跑起来?很简单,输入 run 命令(可以简写为 r)。
(gdb) run
Starting program: /home/user/myprogram
Hello, World!
[Inferior 1 (process 12345) exited normally]
(gdb)
你看,程序正常跑完了,打印了"Hello, World!",然后退出了。GDB会告诉你进程ID和退出状态。
如果你的程序需要命令行参数,直接跟在 run 后面:
(gdb) run arg1 arg2 arg3
或者你想设置环境变量:
(gdb) set env PATH /usr/local/bin
(gdb) run
这里有个坑,我曾经踩过:如果你在GDB里 run 了程序,然后想重新跑,不需要退出GDB再进来。直接再输入 run 就行,GDB会自动杀掉之前的进程,重新启动。但要注意——如果程序卡住了,你可能需要先 kill 掉它。
2.4 退出GDB
调试完了,怎么退出?两个命令任选:
(gdb) quit
# 或者简写
(gdb) q
如果你在调试过程中修改了源代码,GDB会提醒你:
(gdb) quit
The program is running. Exit anyway? (y or n) y
按 y 确认就行。GDB会帮你清理子进程,然后退出。
一个小技巧:如果你在调试脚本里用GDB,可以用 -batch 模式。这样GDB执行完命令后自动退出,不需要手动输入 quit。比如:gdb -batch -ex "run" -ex "bt" myprogram。这个在自动化测试里非常有用。
2.5 本章知识体系
为了让你更直观地理解整个流程,我画了一张图:
这张图把整个流程串起来了。你从左上角开始,编译、启动、运行,然后进入调试循环。调试完了,从右边退出。就这么简单。
2.6 避坑指南
最后,分享几个我实际踩过的坑:
- 编译优化问题:我曾经用
-O2配合-g编译,结果调试时变量值显示不对,跳转行号也乱。这是因为优化器重排了代码。调试阶段建议用-O0关闭优化。 - 路径问题:如果你在GDB里
run程序,当前工作目录是GDB启动时的目录,不是程序所在目录。需要文件路径时,记得用绝对路径或者先cd。 - 权限问题:有些嵌入式调试需要root权限。如果GDB启动时报权限错误,试试
sudo gdb。但注意,别在生产环境随便用sudo。
好了,这一章的内容就这些。你现在应该能自己编译一个带调试信息的程序,启动GDB,让它跑起来,然后优雅地退出了。下一章咱们会深入进去,看看怎么设置断点、单步执行——那才是GDB真正发力的地方。