目录
前言
本文主要对GDB常用调试命令做一个简单总结,以期能在调试时做到灵活运用,主要参考了《GDB完全中文手册》文档。
调试准备
- 在编译时, 我们必须要把调试信息加到可执行文件中。 使用编译器( cc/gcc/g++) 的-g 参数可以做到这一点。如果没有-g, 你将看不见程序的函数名、 变量名, 所代替的全是运行时的内存地址。
注:内核编译的时候需要注意修改Makefile为O1,否则会乱序,不利于调试;
启动GDB的三种方式
-
gdb [program]
program也就是你的执行文件, 一般在当然目录下。 -
gdb [program] [core]
用gdb同时调试一个运行程序和core文件, core是程序非法执行后core dump后产生的文件。 -
gdb [program] [PID]
如果程序是一个服务程序, 可以指定这个服务程序运行时的进程ID。 gdb会自动attach上去, 并调试。
注:program应该在PATH环境变量中搜索得到
GDB启动时, 可以加上一些GDB的启动开关, 详细的开关可以用gdb -help查看。 我在下面只例举一些比较常用的参数:
-symbols file
-s file
从指定文件中读取符号表。
-se file
从指定文件中读取符号表信息, 并把他用在可执行文件中。
-core file
-c file
调试时core dump的core文件。
-directory directory
-d directory
加入一个源文件的搜索路径。 默认搜索路径是环境变量中PATH所定义的路径。
设置断点(BreakPoint)
|- -b
b 43 设置断点为43行
b function 在某个函数设置断点
b +offset 在当前行号的后面offset行停住
b -offset 在当前行号的前面offset行停住
b filename:linenum 在filename的linenum行停住
b filename:function 在源文件filename的function函数的入口处停住
b *address 在程序运行的内存地址处停住
b … if [condition] …可以是上述的参数, condition表示条件, 在条件成立时停住
注:b是bredak的缩写
i b 查看有多少个断点
设置观察点(WatchPoint)
观察点一般来观察某个表达式( 变量也是一种表达式) 的值是否有变化了, 如果有变化, 马上停住程序。
|- -watch expr
为表达式( 变量) expr设置一个观察点。 一旦表达式值有变化时, 马上停住程序。
|- -rwatch expr
当表达式( 变量) expr被读时, 停住程序。
|- -awatch expr
当表达式( 变量) 的值被读或被写时, 停住程序。
|- -info watchpoints
列出当前所设置了的所有观察点
设置捕捉点( CatchPoint)
你可设置捕捉点来补捉程序运行时的一些事件。如: 载入共享库( 动态链接库) 或是C++的异常。 设置捕捉点的格式为:
|- -catch event
当event发生时, 停住程序。 event可以是下面的内容:
1、 throw 一个C++抛出的异常。( throw为关键字)
2、 catch 一个C++捕捉到的异常。( catch为关键字)
3、 exec 调用系统调用exec时。( exec为关键字, 目前此功能只在HP-UX下有用)
4、 fork 调用系统调用fork时。( fork为关键字, 目前此功能只在HP-UX下有用)
5、 vfork 调用系统调用vfork时。( vfork为关键字, 目前此功能只在HP-UX下有用)
6、 load 或 load 载入共享库( 动态链接库)时。( load为关键字, 目前此功能只在HP-UX下有用)
7、 unload 或 unload 卸载共享库( 动态链接库)时。( unload为关键字, 目前此功能只在HP-UX下有用)
|- -tcatch event
只设置一次捕捉点, 当程序停住以后, 捕捉点被自动删除
维护停止点
上面说了如何设置程序的停止点, GDB中的停止点也就是上述的三类。在GDB中, 如果你觉得已定义好的停止点没有用了, 你可以使用delete、 clear、 disable、 enable这几个命令来进行维护。
|- -clear
清除所有的已定义的停止点。
|- -clear function
clear filename:function
清除所有设置在函数上的停止点。
|- -clear linenum
clear filename:linenum
清除所有设置在指定行上的停止点。
|- -delete [breakpoints] [range…]
删除指定的断点, breakpoints为断点号。 如果不指定断点号, 则表示删除所有的断点。 range 表示断点号的范围(如:3-7)。 其简写命令为d。
比删除更好的一种方法是disable停止点, disable了的停止点, GDB不会删除, 当你还需要时, enable即可, 就好像回收站一样。
|- -delete 3
删除第3个断点
|- -disable [breakpoints] [range…]
disable所指定的停止点, breakpoints为停止点号。 如果什么都不指定, 表示disable所有的停止点。 简写命令是dis.
|- -enable [breakpoints] [range…]
enable所指定的停止点, breakpoints为停止点号。
|- -enable [breakpoints] once range…
enable所指定的停止点一次, 当程序停止后, 该停止点马上被GDB自动disable。
|- -enable [breakpoints] delete range…
enable所指定的停止点一次, 当程序停止后, 该停止点马上被GDB自动删除。
停止条件维护
|- -condition bnum expression
修改断点号为bnum的停止条件为expression。
|- -condition bnum
清除断点号为bnum的停止条件。
|- -ignore bnum count
表示忽略断点号为bnum的停止条件count次
为停止点设定运行命令
|- -commands [bnum] … command-list … end
为断点号bnum指写一个命令列表。 当程序被该断点停住时, gdb会依次运行命令列表中的命令
|- -commands… command-list … end
为断点号bnum清除命令
恢复程序运行
|- -continue [ignore-count]
|- -c [ignore-count]
|- -fg [ignore-count]
恢复程序运行, 直到程序结束, 或是下一个断点到来。 ignore-count表示忽略其后的断点次数。 continue,c, fg三个命令都是一样的意思。
单步执行
|- -step count
单步跟踪, 如果有函数调用, 他会进入该函数。 进入函数的前提是, 此函数被编译有debug信息。 很像VC等工具中的step in。 后面可以加count也可以不加, 不加表示一条条地执行, 加表示执行后面的count条指令, 然后再停住。
|- -next count
同样单步跟踪, 如果有函数调用, 他不会进入该函数。 很像VC等工具中的step over。 后面可以加count也可以不加,不加表示一条条地执行, 加表示执行后面的count条指令, 然后再停住。
|- -set step-mode
set step-mode on
打开step-mode模式, 于是, 在进行单步跟踪时, 程序不会因为没有debug信息而不停住。 这个参数有很利于查看机器码。
set step-mod off
关闭step-mode模式。
|- -finish
运行程序, 直到当前函数完成返回。 并打印函数返回时的堆栈地址和返回值及参数值等信息。
|- -until 或 u
当你厌倦了在一个循环体内单步跟踪时, 这个命令可以运行程序直到退出循环体。
|- -stepi 或 si
|- -nexti 或 ni
单步跟踪一条机器指令! 一条程序代码有可能由数条机器指令完成, stepi和nexti可以单步执行机器指令。 与之一样有相同功能的命令是“display/i $pc” , 当运行完这个命令后, 单步跟踪会在打出程序代码的同时打出机器指令( 也就是汇编代码)
|- -set
set *(int *)0x804a02=2019
修改内存的内容
set $pc=0xfffff7fd2950
修改寄存器的内容
|- -enter
执行上次执行的命令
强制函数返回
如果你的调试断点在某个函数中, 并还有语句没有执行完。 你可以使用return命令强制函数忽略还没有执行的语句并返回。
|- -return expression
使用return命令取消当前函数的执行, 并立即返回, 如果指定了expression, 那么该表达式的值会被认作函数的返回值
强制调用函数
|- -call expr
表达式中可以是函数, 以此达到强制调用函数的目的。 并显示函数的返回值, 如果函数返回值是void, 那么就不显示。
查看栈信息
|- -bt
查看当前运行的函数调用栈
注:bt最常用,如果程序hang死,可以用bt查看程序现场;watch查看谁踏坏了变量;经常需要ni和si来查看指令级的单步。
|- -backtrace n
|- -bt n
n是一个正整数, 表示只打印栈顶上n层的栈信息。
|- -backtrace -n
|- -bt -n
-n是一个负整数, 表示只打印栈底下n层的栈信息。
|- -frame n 或 f n
n是一个从0开始的整数, 是栈中的层编号。 比如: frame 0, 表示栈顶, frame 1, 表示栈的第二层。
|- -up n
表示向栈的上面移动n层, 可以不打n, 表示向上移动一层。
|- -down n
表示向栈的下面移动n层, 可以不打n, 表示向下移动一层。
|- -frame 或 f
会打印出这些信息: 栈的层编号, 当前的函数名, 函数参数值, 函数所在文件及行号, 函数执行到的语句
|- -info frame 或 info f
这个命令会打印出更为详细的当前栈层的信息, 只不过, 大多数都是运行时的内内地址。 比如: 函数地址, 调用函数的地址, 被调用函数的地址, 目前的函数是由什么样的程序语言写成的、 函数参数地址及值、 局部变量的地址等等。
|- -info args
打印出当前函数的参数名及其值
|- -info locals
打印出当前函数中所有局部变量及其值。
|- -info catch
打印出当前的函数中的异常处理信息。
显示源代码
|- -list linenum
显示程序第linenum行的周围的源程序。
|- -list function
显示函数名为function的函数的源程序。
|- -list
显示当前行后面的源程序。
|- -list –
显示当前行前面的源程序。
一般是打印当前行的上5行和下5行, 如果显示函数是是上2行下8行, 默认是10行
|- -ptype 结构体变量名
显示结构体定义
源代码的内存
|- -info line tst.c:func
可以使用info line命令来查看源代码在内存中的地址。 info line后面可以跟“ 行号”,“ 函数名”,“ 文件名:行号”,“ 文件名:函数名”, 这个命令会打印出所指定的源码在运行时的内存地址
|- -disassemble func
可以查看源程序的当前执行时的机器码, 这个命令会把目前内存中的指令dump出来
查看运行时数据
|- -print expr 或 p expr 或 print/f expr
使用print命令( 简写命令为p), 或是同义命令inspect来查看当前程序的运行数据,其中f为格式
p bk
查看变量bk的值
p bk.year=2010
修改变量的值
p &bk
查看bk变量的地址
p/x 结构体变量
16进制查看结构体变量值
p/x *address
打印地址的数据值
p/x *(type *)address
打印地址address地址对应的符号
p/x *arrary@10
查看array数组前10个元素的值
p/x *tp->args@10
查看tp结构体指针下args数组前10个元素的值
p ‘‘f2.c’’::x
查看f2.c文件中的全局变量x
p ‘‘function’’::x
查看function函数中的变量x
注:x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
查看内存
使用examine命令( 简写是x) 来查看内存地址中的值。x命令的语法如下所示:
|- -x/n/f/u addr
x/8w 0x804a028
查看0x804a028开始地址的8个字内容
x/8b 0x804a028
查看0x804a028开始地址的8个字节内容
x/8h 0x804a028
查看0x804a028开始地址的8个半字内容
x/16x 0x804a028
以16进制查看0x804a028开始地址的16个字内容
x/g 0x804a028
查看0x804a028开始地址的8个字节内容
x/20i 函数名
查看函数的指令代码
查看寄存器
|- -info registers
查看寄存器的情况。( 除了浮点寄存器)
|- -info all-registers
查看所有寄存器的情况。( 包括浮点寄存器)
|- -info registers <regname …>
查看所指定的寄存器的情况。
修改变量值
|- -print x=4
x=4这个表达式是C/C++的语法, 意为把变量x的值修改为4
显示选项
|- -gdb -tui
启动gdb,并且分屏显示源代码,使用了’-tui’选项,启动可以直接将屏幕分成两个部分,上面显示源代码,比用list方便多了。这时候使用上下方向键可以查看源代码,想要命令行使用上下键就用[Ctrl]n和[Ctrl]p.
|- -set print address on
打开地址输出,当程序显示函数信息时,GDB会显出函数的参数地址。系统默认为打开的
|- -set print address off
关闭函数的参数地址显示
|- -show print address
查看当前地址显示选项是否打开。
|- -set print array on
打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。这个选项默认是关闭的。
|- -set print array off
关闭数组显示,关闭后当数组显示时,每个元素则以逗号分隔。这个选项默认是关闭的。
|- -show print array
显示数组的显示格式
|- -set print elements
这个选项主要是设置数组的,如果你的数组太大了,那么就可以指定一个来指定数据显示的最大长度,当到达这个长度时,GDB就不再往下显示了。如果设置为0,则表示不限制。
|- -show print elements
查看print elements的选项信息。
|- -set print null-stop
如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。这个选项默认为off。
|- -set print pretty on
如果打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮
|- -set print union <on/off>
设置显示结构体时, 是否显式其内的联合体数据
|- -show print union
查看联合体数据的显示方式
参考文档
GDB完全手册.txt