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 本章知识体系

为了让你更直观地理解整个流程,我画了一张图:

GDB快速上手流程 步骤1:编译 gcc -g -o prog prog.c 步骤2:启动GDB gdb prog 步骤3:运行 (gdb) run 调试命令 break, next, print, continue... 退出GDB (gdb) quit 或 q 继续调试?再次 run

这张图把整个流程串起来了。你从左上角开始,编译、启动、运行,然后进入调试循环。调试完了,从右边退出。就这么简单。

2.6 避坑指南

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

  • 编译优化问题:我曾经用 -O2 配合 -g 编译,结果调试时变量值显示不对,跳转行号也乱。这是因为优化器重排了代码。调试阶段建议用 -O0 关闭优化。
  • 路径问题:如果你在GDB里 run 程序,当前工作目录是GDB启动时的目录,不是程序所在目录。需要文件路径时,记得用绝对路径或者先 cd。
  • 权限问题:有些嵌入式调试需要root权限。如果GDB启动时报权限错误,试试 sudo gdb。但注意,别在生产环境随便用sudo。

好了,这一章的内容就这些。你现在应该能自己编译一个带调试信息的程序,启动GDB,让它跑起来,然后优雅地退出了。下一章咱们会深入进去,看看怎么设置断点、单步执行——那才是GDB真正发力的地方。

可可精品资料库