2. GDB基本命令
好,咱们正式开始动手。这一章我带你过一遍GDB最核心的几个命令。说白了,你只要把这几个命令玩熟了,日常调试中80%的问题都能搞定。我当年刚入行时,就靠这几招在项目里抓了不少bug。
2.1 启动GDB:两种方式,你都得会
启动GDB其实就两种路子。第一种最直接:
gdb ./my_program
这样GDB会加载你的程序,但不会立刻跑起来。它等着你下命令呢。
第二种方式,是程序已经跑起来了,突然卡死或者出异常。这时候你可以:
gdb ./my_program core_dump_file
或者,如果你想让GDB挂到一个正在运行的进程上:
gdb -p <PID>
嗯,这里要注意:core dump文件不是默认生成的。你需要先执行ulimit -c unlimited。我曾经在一个服务器项目里,程序半夜崩溃了,结果发现core dump没开,那叫一个抓狂。从那以后,我每个项目的第一件事就是检查core dump配置。
-g选项。否则你看到的全是汇编,那画面太美我不敢看。
2.2 设置断点:让程序停在你想停的地方
断点是调试的灵魂。没有断点,你只能眼睁睁看着程序跑完,啥也抓不到。
最基本的用法:
(gdb) break main # 在main函数入口停住
(gdb) break 42 # 在当前文件的第42行停住
(gdb) break file.c:100 # 在file.c的第100行停住
你还可以加条件。比如我只想在第42行停住,但前提是i == 5:
(gdb) break 42 if i == 5
这个功能太实用了。我在调试一个循环时,经常遇到前100次都没问题,第101次就崩了。没有条件断点,你得手动按100次continue,手都按酸了。
查看你设了哪些断点:
(gdb) info breakpoints
删除断点:
(gdb) delete 1 # 删除编号为1的断点
(gdb) delete # 删除所有断点
2.3 运行程序:run和start的区别
设好断点后,就该让程序跑起来了。最常用的命令是run:
(gdb) run
(gdb) run arg1 arg2 # 带命令行参数运行
(gdb) run < input.txt # 重定向输入
还有一个命令叫start。它相当于break main + run。也就是说,它会在main函数的第一行停住。我个人习惯用start,省得再敲一遍断点命令。
run就行。GDB会问你是否重启,回答y即可。
2.4 单步执行:next和step,别搞混了
这两个命令是调试时用得最频繁的。很多人一开始分不清,其实很简单:
| 命令 | 含义 | 会不会进入函数内部 |
|---|---|---|
next(或n) |
执行下一行 | 不会 |
step(或s) |
执行下一行 | 会 |
举个例子。假设你有这么一段代码:
int a = foo(); // 第10行
int b = bar(); // 第11行
如果当前在第10行,你敲next,GDB会直接执行完foo(),然后停在第11行。你根本看不到foo()内部发生了什么。
如果你敲step,GDB会跳进foo()函数内部,让你一行一行地看它怎么执行的。
我刚开始学的时候,经常在调试库函数时不小心step进去了,结果看到一堆汇编代码,吓得赶紧按finish跳出来。后来学乖了,不确定的函数先用next跳过。
step进了一个函数,可以用finish命令直接执行完当前函数,然后回到调用处。
2.5 查看变量:print和它的变体
调试的核心就是看变量值。最基础的命令是print:
(gdb) print i # 打印变量i的值
(gdb) print &i # 打印变量i的地址
(gdb) print *ptr # 打印指针指向的值
(gdb) print array[5] # 打印数组第6个元素
你还可以打印表达式:
(gdb) print i + j
(gdb) print sizeof(arr)
有时候你想一直盯着某个变量看,每次手动敲print太麻烦。这时候可以用display:
(gdb) display i # 每次程序停住时,自动打印i的值
(gdb) info display # 查看所有自动显示的变量
(gdb) undisplay 1 # 取消编号为1的自动显示
还有一个命令叫whatis,可以查看变量的类型:
(gdb) whatis i
type = int
嗯,这个命令在调试复杂结构体时特别有用。有时候你看到一个变量名,但不确定它是什么类型,whatis一下就知道。
print看值,display自动看值,whatis看类型。这三个命令配合使用,基本能搞定所有变量查看需求。
2.6 本章知识体系
下面这张图帮你梳理了本章的核心命令和它们之间的关系:
这张图把本章的核心命令串起来了。你从启动GDB开始,设置断点,然后运行程序。程序在断点处停住后,你用next或step单步执行,同时用print查看变量值。发现问题后,修改代码,重新编译,再来一轮。这就是调试的基本循环。
好了,这一章的内容就这些。你先把这几个命令练熟,后面我们会在这个基础上,讲更多高级技巧。