懒人李冰

记录我的生活、学习

如何使用 GDB

GDB, The GNU Project debugger, allows you to see what is going on inside another program while it executes – or what another program was doing at the moment it crashed.

GDB 主要完成以下四件事:

  1. 启动程序,指定影响程序运行的条件。
  2. 使程序在特定的条件下停止。
  3. 程序停止时,检查程序锁发生的事。
  4. 动态的改变程序的执行环境。

前期准备

GDB 一般用于调试C/C++程序,要想能够使用GDB调试C/C++程序,首先必须将调试信息添加到可执行程序中。使用gcc/g++-g参数可以做到这一点。如:

1
gcc -g programe.c -o programe

此时,可执行程序programe中就包含了调试需要的各种信息,如程序函数名、变量名等。 对于 MAC OSX 系统,调试信息会包含在另外一个programe.dSYM(debug symbols)文件夹下面,可以使用dwarfdump programe.dSYM直接查看各符号信息。

启动GDB方法

  1. gdb program就是执行的文件,一般在当前目录下。
  2. gdb core 用GDB同事调试一个运行程序和 core 文件,core 是程序非法执行后 core dump 后产生的文件。
  3. gdb 如果程序是一个服务程序,则可以指定服务程序运行时的进程ID。gdb 自动 attach 上去,并调试它。 program 应该在 PATH 环境变量中搜索得到。

如果出现Segment Fault,可以通过方法 2 来进行 Debug 程序,启动方式为gdb {executable} {dump file},如果没有产生 core 文件,需要在执行 executable 之前先执行如下命令:

1
$ulimit -c unlimited

设置运行参数

set args 可指定运行时参数。(如:set args 10 20 30)
show args 命令可以查看设置好的运行参数。

查看源码

1
2
list linenum  //查看linenum行的源码
list function //查看function的源码

断点break 使用

设置断点的方法

1
2
3
4
5
break linenum  //在 linenum 处设置断点
break function //在进入指定 function 时停住
break filename:linenum  //在源文件 filename 的 linenum 行处停住
break filename:function //在源文件 filename 的 function 函数的入口处停住
break *address          //在程序运行的内存地址处停住

查看断点信息

1
2
info break    //查看所有 break 的信息
info break n //查看 n 断点号的信息

条件断点,就是在上述设置断点的同时,指定断点的进入条件。

1
break 10 if index == 3

忽略前面的某个断点

1
ignore 2 5 //忽略断点2 的前面 5 次,到第 6 次才停止

运行程序

运行程序如下

1
2
next //单步执行
continue //继续执行程序,直到程序结束或遇到下一个断点

查看运行时数据

1
2
print parm //打印变量parm的值
bt         //查看函数堆栈信息

当运行程序到某个位置时,我们希望看看此时程序的状态,比如某个变量的值是否按照预期改变、某块内存的值是否被改。此时就需要用到查看程序运行数据的集中方法。

查看格式 print <expr>print /<f> <expr>,其中<expr>是要查看的表达式,可以是一个变量、数组、表达式等,<f>是输出时的格式,比如想要按照 16 进制输出,就使用/x

可以使用 examine 命令查看内存地址中的值。格式是x /<n/f/u> <addr>,其中<addr>是内存地址。

查看内存数据

在调试代码时,经常需要查看某块内存的数据,此时就需要使用GDB中的Examining memory
可以使用命令x(即examine)来检查任意格式的内存数据,不管你的程序数据类型。使用的格式为:

1
2
x  /nfu addr
x addr  

其中n/f/u 是选项参数,指定内存的大小及显示格式;addr 指定显示的内存的起始地址。n 是十进制的整数,指定小时内存的大小;f 指定显示的格式,它的使用与 GDB 中的 print 使用的格式一样,如x指定使用 16 进制显示, d按十进制格式显示等;u 是指每个显示单元的大小,如b是指每个显示单元为 byte,h是指每个显示单元为半字(两个 byte)等;addr 指定要显示的内存的起始地址。

如果需要查看的数据比较多,比如我们需要 dump 一块 buffer 的数据,与特定的数据进行比较,上面提到的examine就很难实现了。此时需要将块内存 dump 出来。使用到的命令是 dumpappendrestore。此处主要介绍dump命令。
它的格式为:

1
dump [format] memory filename start_addr end_addr

从格式可以看出,它的含义是从start_addr开始到end_addr结束的 memory dump 到 指定的文件 filename 中。

分割窗口

layout 用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:

  • layout src:显示源代码窗口
  • layout asm:显示汇编窗口
  • layout regs:显示源代码汇编和寄存器窗口
  • layout split:显示源代码和汇编窗口
  • layout next:显示下一个 layout
  • layout prev:显示上一个 layout
  • Ctrl+L:刷新窗口
  • Ctrl+x,再按1:单窗口模式,显示一个窗口
  • Ctrl+x,再按2:双窗口模式,显示两个窗口
  • Ctrl+x,再按a:回到传统模式,即退出 layout, 回到执行 layout 之前的调试窗口

查看寄存器

查看寄存器的值,很简单,可以使用如下命令:

  • info registers, 查看寄存器的情况,除了浮点寄存器。
  • info all-registers, 查看所有寄存器的情况,包括浮点寄存器。
  • info registers , 查看所指定的寄存器的情况。

同样可以使用 print 命令来访问寄存器的情况,只需要在寄存器名字前加一个$符号就可以了,例如p $eip

汇编调试

在做 ARM 汇编时,需要使用 GDB 对汇编语言进行单步调试,但很多时候,没办法直接通过stepinexti进入汇编函数内调试。使用了一个迂回的方法:

  1. 设置断点,在汇编函数名处先设置断点。 break
  2. 直接显示汇编代码,layout asm;显示寄存器值,layout regs。
  3. 单步执行汇编命令stepinexti

问题汇总

  1. 在工作中经常遇到把编译处的.o.bin可执行文件,拿到别的路径下运行、Debug,经常遇到GDB找不到debug src info的情况,此时可以通过directory命令指定寻找的路径。

参考文献

  1. GNU Debugger Tutorial
  2. GDB: The GNU Project Debugger
  3. GNU Debugger
  4. How to Debug Using GDB
  5. Debugging with GDB
  6. 用GDB调试程序
  7. Debugging with GDB: The GNU Source-Level Debugger
  8. GDB Pocket Reference: Debugging Quickly & Painlessly with GDB
  9. The Art of Debugging with GDB, DDD, and Eclipse