1. GDB入门:调试,从认识GDB开始

说实话,我刚开始做嵌入式开发那会儿,调试基本靠printf。代码里到处塞打印语句,编译、下载、看串口输出,改一行代码就得重复一遍流程。直到有位老同事看我折腾了半天,丢过来一句:「你试试GDB。」

嗯,从那以后,我再也没回到printf调试的老路上去。

1.1 什么是GDB

GDB,全称GNU Project Debugger。说白了,就是一个让你能「看穿」程序运行状态的工具。

你想想看,程序跑起来之后,变量值是多少?函数调用到了哪一层?内存里到底存了什么?这些平时看不到的东西,GDB都能让你实时观察。它就像程序的CT机,哪里有问题,扫一眼就知道了。

我个人习惯把GDB分成两种用法:

  • 本地调试:在PC上直接调试本机的程序
  • 远程调试:调试嵌入式设备、服务器上的程序

做嵌入式的人,大部分时间都在用远程调试。我在项目中遇到过最典型的情况——板子跑着跑着就死机了,串口打印最后一条信息停在某个函数入口。这时候用GDB连上去,看调用栈,问题往往一目了然。

1.2 GDB能做什么

我总结了一下,GDB的核心能力就这五条:

能力 说明 我常用的场景
启动程序 指定参数、环境变量来运行程序 调试带命令行参数的工具
设置断点 在指定行、函数、条件处暂停 定位崩溃前的最后状态
查看变量 打印变量值、内存内容、寄存器 检查指针是否越界
单步执行 一行一行或一条指令一条指令地跑 追踪逻辑分支走向
分析堆栈 查看函数调用链、回溯崩溃点 排查段错误、死锁

其中堆栈分析这块,是我们这门课的核心。为什么?因为90%的疑难杂症,最后都落在调用栈上。我曾经花了两天查一个内存踩踏问题,最后用GDB的backtrace命令,十分钟就定位到了。

一句话总结:GDB能让你在程序运行时「暂停时间」,然后仔细检查每一处细节。

1.3 GDB的安装与启动

安装GDB其实很简单。不同平台略有差异,我列个表:

平台 安装命令
Ubuntu/Debian sudo apt install gdb
CentOS/RHEL sudo yum install gdb
macOS brew install gdb
Windows 安装MinGW或WSL后使用

装完之后,验证一下:

$ gdb --version
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
...

看到版本号,就说明装好了。

启动GDB有两种方式:

  1. 直接调试可执行文件:gdb ./my_program
  2. 附加到正在运行的进程:gdb -p 1234(1234是进程PID)

我个人更常用第一种。但如果你遇到程序已经跑起来、突然卡住的情况,第二种方式就派上用场了。我记得有一次,一个服务进程CPU飙到100%,我直接gdb -p附上去,看到它在死循环里打转——嗯,一个循环条件写错了。

小技巧:编译程序时记得加-g选项,这样GDB才能看到源码和符号信息。不加的话,你看到的全是地址,没法读。

1.4 GDB的基本命令概览

GDB的命令很多,但常用的其实就十几个。我刚开始学的时候,记不住那么多,就只记了下面这些:

命令 缩写 作用
run r 运行程序
break b 设置断点
next n 单步跳过(不进入函数)
step s 单步进入(进入函数内部)
continue c 继续运行到下一个断点
print p 打印变量值
backtrace bt 查看函数调用栈
info i 查看各种信息(如info registers)
quit q 退出GDB

来一个最简单的例子。假设我们有这么一段代码:

// test.c
#include <stdio.h>

void func() {
    int a = 10;
    printf("a = %d\n", a);
}

int main() {
    func();
    return 0;
}

编译:gcc -g -o test test.c

启动GDB:

$ gdb ./test
(gdb) break func          # 在func函数入口设断点
(gdb) run                 # 运行程序
(gdb) next                # 单步执行
(gdb) print a             # 打印变量a的值
(gdb) continue            # 继续运行
(gdb) quit                # 退出

看到没?就这么几行命令,你就能控制程序的每一步执行,还能随时查看变量。我当时第一次用的时候,感觉就像开了上帝视角。

注意:GDB默认使用AT&T风格的汇编语法。如果你更习惯Intel风格,可以在GDB里执行set disassembly-flavor intel。我个人偏好Intel风格,看着更直观。

知识体系总览

下面这张图,是我梳理的GDB入门知识结构。你可以把它当作整个课程的地图:

GDB入门 什么是GDB • GNU调试器 • 本地/远程调试 • 程序CT机 GDB能做什么 • 启动程序 • 设置断点 • 查看变量 • 单步执行 • 分析堆栈 安装与启动 • apt/yum/brew安装 • gdb ./program • gdb -p PID • 编译加 -g 选项 基本命令概览 • run / break / next • step / continue • print / backtrace • info / quit 堆栈分析(核心) • 函数调用链回溯 • 崩溃点定位 • 内存踩踏排查 • 死锁分析 本课程核心:堆栈分析与回溯实战

这张图把GDB入门的知识点串起来了。你可以看到,堆栈分析是我们这门课的核心,后面的章节都会围绕它展开。

好了,第一章就到这里。记住,调试不是靠猜,而是靠工具。GDB就是你手里最趁手的工具之一。下一章,我们开始真正动手——编译带调试信息的程序,然后跑第一个GDB会话。

课后小练习:写一个简单的C程序,包含至少两个函数调用。用gcc -g编译,然后用GDB启动,试试break、run、next、print这四个命令。感受一下「控制程序执行」是什么体验。

可可精品资料库