GDB调试学习

GDB 代码调试常用命令总结。

前言

笔者近几年使用 Python 语言来做深度学习、计算机视觉领域相关科研与工作,对于调试程序,使用较多的工具除了 JetBrains 家的各个 IDE 以外,就是使用 Python 自带的 pdb 模块来调试 Python 程序,命令行界面调试代码既灵活,而且速度也快,非常好用。最近,因为项目原因接触 c/c++ 代码更多一些,通过本篇博客对 GDB 学习并且记录相关常用命令。

GDB是一个由GNU开源组织发布的、UNIX / LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c/c++程序员,gdb是必不可少的工具,另外,GDB可以调试Ada、C、 C++、 Asm、 Minimal、 D、 Fortran、 Objective-c、 Go、 Java、 Pascal等语言。标准的 GDB 是纯命令行式的,但也有一些基于它的图形化工具(比如 DDD、Data Display Debugger),但用好 GDB 命令行调试,还是我们的一项基本素质。

本篇文章将简单地介绍 GDB 常见用法,更多内容通过本博客下面的参考资料学习。

GDB 使用流程

启动 GDB 调试

学习 GDB 调试测试的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include<stdio.h>
int square(int x) {
    return x * x;
}

int get_num(int x) {
    int x_2 = square(x);
    return  x_2;
}

int main(){
    int a = 10;
    int b = get_num(a);
}

通过下面命令编译 C++ 代码,注意需要使用 -g 参数,在编译生成的目标文件中加入源码信息,并启动 GDB 调试代码:

1
2
3
gcc -g test.cpp -o test # 编译源文件
gdb test # 启动 gdb 开始调试代码
gdb -q test # -q 表示不打印gdb版本信息,界面较为干净;

查看源码

list(简写 l): 查看源程序代码,默认显示10行,按回车键或继续输入l查看下10行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Reading symbols from test...
(gdb) l
1       #include<stdio.h>
2       int square(int x) {
3           return x * x;
4       }
5
6       int get_num(int x) {
7           int x_2 = square(x);
8           return  x_2;
9       }
10
(gdb) l
11      int main(){
12          int a = 10;
13          int b = get_num(a);
14      }
(gdb) l
Line number 15 out of range; test.cpp has 14 lines.

运行程序

run(简写 r) :运行程序直到遇到 结束或者遇到断点等待下一个命令:

1
2
3
4
5
(gdb) c
The program is not being run.
(gdb) r 
Starting program: /data1/workplace/tmp/test 
[Inferior 1 (process 1044104) exited normally]

设置断点

break(简写 b) :命令格式 b 行号,在某行设置断点: info breakpoints (简写 i b) :显示断点信息。

  • Num: 断点编号
  • Type:类型,breakpoint 或者 watchpoint
  • Disp:断点执行一次之后是否有效 kep:有效 dis:无效
  • Enb: 当前断点是否有效 y:有效 n:无效
  • Address:内存地址
  • What:位置
1
2
3
4
5
6
(gdb) b 13
Breakpoint 1 at 0x555555555170: file test.cpp, line 13.
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000555555555170 in main() at test.cpp:13
(gdb) 

条件断点:

1
(gdb) b ... if cond

具体参考:GDB条件断点详解

单步执行

首先需要输入 run 启动程序,运行到第一个断点处,然后可以选择 step 单步调试(如果有函数调用则进入函数),next 单步跟踪程序(遇到函数调用,直接调用函数,不会进入函数体内),continue 继续运行到下一个断点处。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(gdb) r
Starting program: /data1/workplace/tmp/test 

Breakpoint 2, main () at test.cpp:12
12          int a = 10;
(gdb) s

Breakpoint 1, main () at test.cpp:13
13          int b = get_num(a);
(gdb) s
get_num (x=21845) at test.cpp:6
6       int get_num(int x) {
(gdb) s
7           int x_2 = square(x);
(gdb) n
8           return  x_2;
(gdb) l 8
3           return x * x;
4       }
5
6       int get_num(int x) {
7           int x_2 = square(x);
8           return  x_2;
9       }
10
11      int main(){
12          int a = 10;
(gdb) 

运行程序相关命令如下:

  • run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令;
  • continue (简写 c ):继续执行,到下一个断点处(或运行结束);
  • next:(简写 n ),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。
  • step:(简写s):单步调试如果有函数调用,则进入函数;与命令 n 不同,n 是不进入调用的函数的;
  • until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体;
  • until 行号: 运行至某行,不仅仅用来跳出循环;
  • finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
  • call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)

查看变量

  • print :简记 p,打印变量或表达式的值;
  • whatis :查询变量或函数的类型;
  • pt :跟 whatis 作用类似,可能是 print type 的简写;
  • display:在单步调试的时候很有用,使用 display 命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值;
  • info dispaly,查看当前被设置的变量,然后可以用 undisplay num(变量对应的编号) 取消设置;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
(gdb) p x
$11 = 10
(gdb) whatis x
type = int
(gdb) whatis square
type = int (int)
(gdb) pt square
type = int (int)
(gdb) display x*x + 2
1: x*x + 2 = 102
(gdb) display
1: x*x + 2 = 102
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1:   y  x*x + 2
(gdb) 

退出 GDB

使用 quit (简记 q )命令退出即可。

GDB 常用命令

下面列出最常用的GDB命令:

  • pt:查看变量的真实类型,不受 typedef 的影响;
  • bt:显示当前调用堆栈;
  • up/down:在函数调用栈里上下移动。或者使用frame 函数帧号跳转;
  • fin:直接运行到函数结束;
  • i b:查看所有的断点信息;
  • delete:删除所有断点;
  • delete breakpoint_list:删除某个序号处断点;
  • i locals:查看当前堆栈页的所有变量;
  • wh:启动“可视化调试”。这个是我最喜欢的命令,可以把屏幕分成上下两个窗口,上面显示源码,下面是 GDB 命令输出,不必再用“l”频繁地列出源码了,能够大大提高调试的效率;
  • ctrl x + a :退出或进入可视化模式;
  • layout regs:显示源代码/汇编和寄存器窗口;
  • layout split:显示源代码和汇编窗口;
  • p *array@10:查看数组内容,这里10表示查看数组的大小;
  • ptype:查看变量的类型,这里打印出变量的结构定义;
  • set args config.txt:指定运行时的参数;

GDB 多线程命令

  • info thread:查看被调试的线程;
  • thread apply all bt:打印所有线程堆栈信息;
  • thread <ID> :切换调试的线程为指定ID的线程;
  • break file.c:100 thread all:在file.c文件第100行处为所有经过这里的线程设置断点;
  • set scheduler-locking off|on|step:在调试多线程程序时,默认除了被调试的线程在执行外,其他线程也都在运行,我们可以通过命令来控制这一切:off表示不锁定任何线程on表示只有当前调试的线程会继续运行,step表示在但不执行时只有当前线程会运行;
  • thread apply <ID1> <ID2> <command>:让一个或者多个线程执行GDB命令command;

参考

GDB调试入门指南
gdb调试多线程程序总结
GDB学习笔记
学习使用 GDB 调试代码
linux下gdb调试方法与技巧整理
100个gdb小技巧

comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计