首页 > 技术文章 > GDB调试C/C++程序命令整理

JCpeng 2021-08-13 22:51 原文

Linux操作系统下要对C/C++程序进行调试,就必须在编译源文件时,使用-g参数,编译时使用的命令为(gcc -g test.c -o test)。

编译完成后,启动GDB调试工具,命名为:gdb + 要调试程序的程序名。GDB调试基本命令如下:

1 list/l 命令

可以使用list/l命令查看程序,方便添加断点时查看信息:

  1)list + lineNumber:打印指定行的代码

  2)list + Function:打印函数名称为Function的函数上下文的源程序

  3)list 输出当前行后面的代码

  4)list -:显示当前行前面的代码

2 run/r命令

在gdb中运行程序使用run命令,也可以设置程序运行参数。

3 break/b命令

  1)break < function >:在进入指定的函数function时既停止运行,C++中可以使用class::function或function(type, type)格式来指定函数名称

  2)break < lineNumber>:在指定的代码行打断点

  3)break +offset/break -offset:在当前行的前面或后面的offset行打断点,offset为自然数

  4)break filename:lineNumber:在名称为filename的文件中的第lineNumber行打断点

  5)break filename:function:在名称为filename的文件中的function函数入口处打断点

  6)break *address:在程序运行的内存地址处打断点

  7)break:在下一条命令处停止运行

  8)break … if < condition>:在处理某些循环体中可使用此方法进行调试,其中…可以是上述的break lineNumber、break +offset/break -offset中的参数,其中condition表示条件,在条件成立时程序即停止运行,如设置break if i=100表示当i为100时程序停止运行。

查看断点时,也可以使用info命令如info breakpoints [n]、info break [n]其中n 表示断点号来查看断点信息。

4 逐步调试命令

  1)next < count>:单步跟踪,如果有函数调用不会进入函数,如果后面不加count表示一条一条的执行,加count表示执行后面的count条指令,

  2)step < count>:单步跟踪,如果有函数调用则进入该函数(进入该函数前提是此函数编译有Debug信息),与next类似,其不加count表示一条一条执行,加上count表示自当前行开始执行count条代码指令

  3)set step-mode.set step-mode on:用于打开step-mode模式,这样在进行单步跟踪时,程序不会因为没有debug信息而不停止运行,这很有利于查看机器码,可以通过set step-mode off关闭step-mode模式

  4)finish:运行程序直到当前函数完成并打印函数返回时的堆栈地址和返回值及参数值等信息。

  5)until:运行程序直到退出循环体

  6)stepi(缩写si)和nexti(缩写ni):stepi和nexti用于单步跟踪一条及其指令,一条程序代码有可能由数条机器指令完成,stepi和nexi可以单步执行机器指令。

5 continue/c命令

当程序遇到断点停止运行后可以使用continue命令恢复程序的运行到下一个断点或直到程序结束。

6 print命令

print命令的格式是:

  1)print xxx

  2)p xxx

1. print 操作符

  @:是一个和数组有关的操作符,在后面会有更详细的说明。

  :: 指定一个在文件或是一个函数中的变量。

  {}:表示一个指向内存地址的类型为type的一个对象。

2. 察看内容

  全局变量(所有文件可见的)

  静态全局变量(当前文件可见的)

  局部变量(当前Scope可见的)

  如果你的局部变量和全局变量发生冲突(也就是重名),一般情况下是局部变量会隐藏全局变量。如果此时你想查看全局变量的值时,你可以使用“::”操作符:

  file::variable

  function::variable

  eg:查看文件f2.c中的全局变量x的值:(gdb) p 'f2.c'::x 

     注:如果你的程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。对付这种情况时,需要在编译程序时关闭编译优化。GCC,你可以使用“-gstabs” 选项来解决这个问题。

3. 察看数组

  动态数组:

  p *array@len 

  array:数组的首地址,len:数据的长度

  eg:

  (gdb) p *array@len

  $1 = {2, 4, 6, 8, 10}

  静态数组

  可以直接用print数组名,就可以显示数组中所有数据的内容了。

4. 输出格式

  x 按十六进制格式显示变量。

  d 按十进制格式显示变量。

  u 按十六进制格式显示无符号整型。

  o 按八进制格式显示变量。

  t 按二进制格式显示变量。

  a 按十六进制格式显示变量。

  c 按字符格式显示变量。

   f 按浮点数格式显示变量。
eg:
   (gdb) p i

  $21 = 101

  (gdb) p/a i

  $22 = 0x65

  (gdb) p/c i

  $23 = 101 'e'

5. 察看内存

  使用examine(简写x)来查看内存地址中的值。语法:

  x/

  n、f、u是可选的参数。

  1)n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。

  2)f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。

  3)u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。

  eg:

  x/3uh 0x54320 :从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。

6. 察看寄存器

  1)info registers:查看寄存器的值

  2)info all-registers:查看寄存器的情况(除了浮点寄存器)

  3)info registers:查看所有寄存器的情况(包括浮点寄存器)

  4)查看所指定的寄存器的情况:

   寄存器中放置了程序运行时的数据,比如程序当前运行的指令地址(ip),程序的当前堆栈地址(sp)等等。你同样可以使用print命令来访问寄存器的情况,只需要在寄存器名字前加一个$符号就可以了。如:p $eip。

7. display自动显示的变量

  1)格式:display[/i|s] [expression | addr]

  eg:

  display/i $pc

  $pc是GDB的环境变量,表示着指令的地址,/i则表示输出格式为机器指令码,也就是汇编。于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的功能。

  2)其他

  undisplay

  delete display

  删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)

  disable display

  enable display

  disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。

  info display:查看display设置的自动显示的信息。GDB会打出一张表格,向你报告当然调试中设置了多少个自动显示设置,其中包括,设置的编号,表达式,是否enable。

8. 设置

  1)set print address on:打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。

  2)set print array on:打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。

  3)set print elements:这个选项主要是设置数组的,如果你的数组太大了,那么就可以指定一个来指定数据显示的最大长度,当到达这个长度时,GDB就不再往下显示了。如果设置为0,则表示不限制。

  4)set print null-stop:如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。这个选项默认为off。

  5)set print pretty on:如果打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮。如:

  $1 = {
  next = 0x0,
  flags = {
    sweet = 1,
    sour = 1
  },
  meat = 0x54 "Pork"
  }
  6)set print union:设置显示结构体时,是否显式其内的联合体数据。

  7)set print object:在C++中,如果一个对象指针指向其派生类,如果打开这个选项,GDB会自动按照虚方法调用的规则显示输出,如果关闭这个选项的话,GDB就不管虚函数表了。

7 watch命令

watch命令一般来观察某个表达式(变量也可视为一种表达式)的值是否发生了变化,如果由变化则程序立即停止运行,其具体用法如下:

  1)watch < expr>:为表达式(变量)expr设置一个观察点一旦其数值由变化,程序立即停止运行

  2)rwatch < expr>:当表达式expr被读时,程序立即停止运行

  3)awatch < expr>:当表达式expr的值被读或被写时程序立即停止运行

  4)info watchpoints:列出当前所设置的所有观察点

8 return命令

  如果在函数中设置了调试断点,在断点后还有语句没有执行完,这个时候我们可以使用return命令强制函数忽略还没有执行的语句并返回。可以直接使用return命令用于取消当前函数的执行并立即返回函数值,也可以指定表达式如 return < expression>那么该表达式的值会被作为函数的返回值。

9 info命令

info命令可以用来在调试时查看寄存器、断点、观察点和信号等信息。其用法如下:

  1)info registers:查看除了浮点寄存器以外的寄存器

  2)info all-registers:查看所有的寄存器包括浮点寄存器

  3)info registers < registersName>:查看指定寄存器

  4)info break:查看所有断点信息

  5)info watchpoints:查看当前设置的所有观察点

  6)info signals info handle:查看有哪些信号正在被gdb检测

  7)info line:查看源代码在内存中的地址

  8)info threads:可以查看多线程

10 finish命令

  执行完当前的函数。

11 其他命令

  run(缩写r)和quit(缩写q)分别可以开始运行程序和退出gdb调试

  whatis或ptype显示变量的类型

  bt显示函数调用路径

  pwd命令用于显示当前所在目录

推荐阅读