1. GDB入门
GDB到底是什么?
GDB,全称GNU Debugger。说白了,就是一个能让你「钻进程序内部」看个究竟的工具。
我刚开始做嵌入式开发那会儿,调试基本靠printf。遇到bug就加打印,编译,运行,看输出,再改代码……循环往复。效率低不说,有时候加个打印反而把问题掩盖了。后来一位老同事跟我说:「你试试GDB吧,别再用printf硬扛了。」
嗯,这一试就回不去了。
GDB能让你在程序运行时暂停、查看变量、修改内存、单步执行、查看堆栈……说白了,它就像给程序装了个「显微镜」。你想想看,如果代码是一栋大楼,printf就像站在楼外喊话,而GDB能让你走进每个房间翻箱倒柜。
GDB能做什么?
我总结了一下,GDB的核心能力其实就这几条:
- 启动程序:可以指定参数、环境变量来启动你的程序
- 设置断点:让程序在指定行、指定函数、甚至指定条件时停下来
- 查看变量:停下来之后,你可以看变量的值、内存的内容
- 修改状态:甚至可以临时修改变量的值,看看程序会怎么走
- 单步执行:一行一行地走,看程序到底是怎么跑的
- 查看堆栈:程序崩溃了?看看是谁调了谁,一路回溯上去
- 调试多线程/多进程:哪个线程卡住了?哪个进程挂了?GDB都能管
一句话总结:GDB就是程序员的「后悔药」和「透视镜」。
GDB的安装与版本检查
安装GDB其实很简单。不同的系统,命令略有不同。
Ubuntu/Debian 系
sudo apt-get update
sudo apt-get install gdb
CentOS/RHEL/Fedora 系
sudo yum install gdb
# 或者
sudo dnf install gdb
macOS
brew install gdb
装完之后,检查一下版本:
gdb --version
你会看到类似这样的输出:
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
...
我个人习惯:装完GDB后第一件事就是跑一下 gdb --version。确认版本号,心里踏实。我曾经遇到过系统自带的GDB版本太老,不支持某些新特性,折腾了半天才发现是版本问题。
第一个GDB调试会话
光说不练假把式。我们来写一个简单的C程序,然后用GDB调试它。
准备一个示例程序
创建一个文件 hello.c:
#include <stdio.h>
int add(int a, int b) {
int result = a + b;
return result;
}
int main() {
int x = 10;
int y = 20;
int sum = add(x, y);
printf("sum = %d\n", sum);
return 0;
}
编译时一定要加 -g 选项,这样才能保留调试信息:
gcc -g -o hello hello.c
注意:不加 -g 编译出来的程序,GDB也能调试,但你看不到源码、变量名这些信息。说白了就是「盲调」,非常痛苦。我曾经有一次忘了加 -g,对着汇编代码看了半天……嗯,那感觉就像在黑暗里找开关。
启动GDB
gdb ./hello
你会看到GDB的提示符 (gdb)。这就说明你已经进入GDB的世界了。
设置断点
我们在 main 函数入口处设一个断点:
(gdb) break main
Breakpoint 1 at 0x11a9: file hello.c, line 9.
也可以在某一行设断点:
(gdb) break hello.c:12
Breakpoint 2 at 0x11c0: file hello.c, line 12.
运行程序
(gdb) run
Starting program: /home/user/hello
Breakpoint 1, main () at hello.c:9
9 int x = 10;
程序停在了第9行。注意看,GDB告诉你:停在了 main 函数,文件 hello.c 的第9行。
单步执行
用 next 命令(简写 n)可以执行下一行:
(gdb) next
10 int y = 20;
再执行一次:
(gdb) n
11 int sum = add(x, y);
查看变量
用 print 命令(简写 p)查看变量:
(gdb) print x
$1 = 10
(gdb) print y
$2 = 20
看到没?x 是10,y 是20,跟代码里写的一样。
进入函数内部
用 step 命令(简写 s)可以进入函数内部:
(gdb) step
add (a=10, b=20) at hello.c:4
4 int result = a + b;
你看,GDB带着我们跳进了 add 函数。参数 a=10、b=20 都显示出来了。
继续执行到结束
用 continue 命令(简写 c)让程序继续跑完:
(gdb) continue
Continuing.
sum = 30
[Inferior 1 (process 12345) exited normally]
程序正常退出,打印了 sum = 30。
退出GDB
(gdb) quit
避坑指南:我曾经在调试一个复杂的嵌入式程序时,忘记设断点就直接 run,结果程序跑完了,什么都没抓到。后来我养成了一个习惯:先设好断点,再 run。如果程序已经跑起来了,可以用 Ctrl+C 中断它,再设断点。
本章知识体系
下面这张图帮你理清GDB入门的核心脉络:
这张图把GDB入门分成了三大块:它是什么、它能做什么、怎么安装检查。最下面就是咱们刚刚亲手操作过的「第一个调试会话」。你看,其实没那么复杂,对吧?
核心要点回顾:
- GDB是GNU调试器,让你能「钻进程序内部」
- 编译时加
-g选项才能保留调试信息 - 常用命令:
break(断点)、run(运行)、next(下一步)、step(进入函数)、print(查看变量)、continue(继续)、quit(退出)
好了,第一章就到这儿。你已经在GDB的世界里迈出了第一步。后面我们会一步步深入,从单步调试到多线程、从core dump分析到Makefile集成……嗯,路还长,但每一步都值得。