编译 Debug 版本
为 gcc 命令加入 -g 选项,编译 Debug 版本。
之后就可以使用 GDB 来调试程序了。为 GDB 指定要运行的程序的方法有两种:
或
进入 GDB 交互界面后,使用 file <executable> 指令加载可执行文件:
1 | (gdb) file hello |
执行程序
使用 run 指令让程序运行直至完成:
1 | (gdb) run |
在 GDB 环境下也可以进行标准输入输出。
退出 GDB
使用 quit 指令退出 GDB 交互界面:
1 | (gdb) quit |
传入参数
考虑下面这个 echo 程序:
1 | // echo.c |
编译 Debug 版本:
这个 echo 可执行文件需要传入一个参数,我们仍然有两种方法:
-
在命令行中传入参数:
--agrs 选项包含了 ./echo 这条指令,它将作为一个参数传递给可执行文件,因此必须用 ./echo 而不是 echo。
-
在 GDB 交互界面传入参数:
1 | (gdb) file echo |
提示
set args [arg1] ... 和 run 两条指令可以合并为 run [arg1] ...。
追踪程序运行
以这个斐波那契数列计算程序为例:
1 | // fibo.c |
开始/重启 start
先进入 GDB 交互界面,这次不用 run 来运行,而用 start:
1 | (gdb) start |
start 指令会让程序开始运行并停在 main 函数的起始处。观察到(应当类似如下内容):
1 | Temporary breakpoint 1, main (argc=1, argv=0x7ffeb3641978) at fibo.c:15 |
表明即将执行第 15 行代码。
在程序运行中使用 start 会让程序重启。
步入 step
要一步一步执行程序,使用步入 step。
1 | (gdb) step |
接下来如果想要继续步入,输入 ⏎ 即可。
步出 finish
继续步入,直到进入 fibo(9) 函数体:
1 | fibo (n=9) at fibo.c:6 |
如果继续步入下去,还会看到 fibo(8) fibo(7) 等等的执行过程,实在太浪费时间了!我们需要一个从函数内部跳出的指令:步出 finish。
1 | (gdb) finish |
我们跳过了 fibo(9) 的运算,并得到了结果。
步过 next
如果我们继续步入,就会开始执行 fibo(8),这样又要步出一次,太麻烦了!我们需要一个跳过这一行所有函数的指令:步过 next。
1 | (gdb) next |
这个神秘的右括号表示的是函数最后一条指令执行后 和 函数返回前的时刻。
之后我们可以再用一次步过或步入就能离开 fibo(10) 这个函数。
1 | main (argc=1, argv=0x7ffeb3641978) at fibo.c:17 |
继续 continue
剩下的就是输出结果了,我们已经不关心了,可以用继续 continue 来让程序继续运行直至退出。
停止 kill
杀死进程。
1 | (gdb) kill |
断点调试
在 GDB 中查看源代码
使用 list 指令查看源代码,共有五种:
1 | list |
设置断点
使用 break 指令设置断点,与 list 一样有五种,比如对第 10 行设置断点可以写为:
1 | (gdb) break 10 |
删除/禁用断点
首先用 info breakpoints 查看所有断点信息。各列信息分别是编号、类型、是否为临时断点、是否启用、位置、当前状态。
记住断点的编号,然后使用 disable breakpoints [num] 来禁用之。
如果不再需要这个断点,使用 delete breakpoints [num] 删除之。
临时断点
临时断点只会中断一次。start 相当于在 main 函数处设置了一个临时断点。
使用 tbreak 来创建临时断点。
后记
事实上 GDB 还有条件断点、观察点等等厉害的工具,但考虑到使用命令行进行这些操作实在太费事了,就只写到这里。