本文共 5485 字,大约阅读时间需要 18 分钟。
相信大家或多或少已经接触过 IDE 中集成的调试工具。今天介绍一款接近命令行、灵活强大的调试工具 gdb(GNU debugger), 它是由 GNU 提供的软件调试工具,提供了 TUI
界面,用好 gdb 可以极大提高我们定位问题的速度。这里推荐一个 方便查阅命令。
这部分不详述,网上相关信息已经很多。
gdb attach pid// start gdbaarch64-linux-gnu-gdb// load symbol(gdb) file xxx.elf// attach to the remote target(gdb) target remote :portnum// playground...
bt backtrace
bt , n 是一个正整数,表示只打印栈顶上算起 n 层的栈信息。也就是从最后的被调函数开始,从后往前打印 n 层。 bt <-n>,-n 是一个负整数,表示只打印栈底下算起 n 层的栈信息。也就是从最先的 caller 开始,从前往后打印 n 层。f frame
f ,查看当前栈层的信息,可以用 frame 或 f 会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。#0 __test_MATH_NEON () at math/test_math_neon.c:7676 {
(gdb) info fStack level 0, frame at 0x5cc8324ea0: pc = 0x4500b0 in __test_MATH_NEON (math/test_math_neon.c:76); saved pc = 0x44f4dc called by frame at 0x5cc8324ec0 source language c. Arglist at 0x5cc8324ea0, args: Locals at 0x5cc8324ea0, Previous frame's sp is 0x5cc8324ea0
info args,打印出当前函数的参数名及其值。
info locals,打印出当前函数中局部变量及其值。
up 表示栈帧向调用栈的上面移动 n 层,可以不打 n,表示向上移动一层。
down 表示栈帧向调用栈的下面移动 n 层,可以不打 n,表示向下移动一层。
上面的命令,都会打印出移动到的栈层的信息。如果不想让其打出这些信息。可以使用这三个命令:
select-frame 对应于 frame 命令。up-silently 对应于 up 命令。down-silently 对应于 down 命令。
// 查看当前的设置值show listsize// 设置新值set listsize// 已 offset 为中心,前后显示 5 行list <+offset>list <-offset>
info line xxx
查看源代码在内存中的地址。这个命令会打印出所指定的源码在运行时的内存地址。 info line后面可以跟 “行号”,“函数名”,“文件名:行号”,“文件名:函数名”。disassemble funcname
查看源程序的当前执行时的机器码,这个命令会把目前内存中的指令dump出来。x 按十六进制格式显示变量。
d 按十进制格式显示变量。 u 按十六进制格式显示无符号整型。 o 按八进制格式显示变量。 t 按二进制格式显示变量。 a 按十六进制格式显示变量,如果对应了某个变量的话,同时会显示变量名。 c 按字符格式显示变量。 f 按浮点数格式显示变量。// can veiw function prototype(gdb) whatis variable(gdb) ptype foo
int *array = (int *) malloc(len * sizeof (int));
p *array@len
“@”的左边是第一个内存的地址的值,“@”的右边是想查看内存的长度。 可使用 examine
命令(简写是 x)来查看内存地址中的值。x 命令的语法如下所示:
x/nfu address
f: format letter u: size letter
n 是一个正整数,表示显示的内存单元个数。
f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是 s,如果是指令地址,那么格式可以是 i。(gdb) x/i $pc=> 0x45003c: ldr q3, [x0, #32]
u 表示一个地址单位的长度。u 参数可以用下面的字符来代替,b 表示单字节,h 表示双字节,w 表示四字节,g 表示八字节。默认为 8 字节,即 g。
上述命令会从指定的内存地址开始,读写 n 个 u 指定格式的数据,并使用 f 指定的格式显示出来。因此如果想按单字节显示某地址开始的8个字节的数据可以使用 x/8xb buffer
。
info registers
查看通用寄存器的情况。
info all-registers
查看所有寄存器的情况。(包括浮点寄存器)
info registers <reg name>
查看所指定的寄存器组的情况。
(gdb) maint print reggroups Group Type general user float user system user vector user all user save internal restore internal
显示寄存器的值和变量有些差异,需要加上 $
符号。如 p $x0
// break -> b break file:linebreak funcname// 条件断点b xxx if// 查看断点info b// 禁用断点disable numenable numdisable // disable all brkenable // enable all brk// 删除断点d num // delete brk numd // delete all brkc // continuectrl + c // suspendnext // step to nexts // step intofinish / fin // step outuntil / u // run until
修改被调试程序运行时的变量值,使用 print
或 set
命令即可完成。print x=4
或者 set x=4
。
在某些时候,很有可能你的变量和GDB中的参数冲突,如 width,set width 是 GDB 的命令,所以会出现 “Invalid syntax inexpression” 的错误,此时,你可以使用 set var
命令来告诉GDB width 是程序的变量名,如:set var width=10
。
jump <address>
当然也可以使用 set $pc
来更改跳转执行的地址。如:set $pc = 0x485
call <...>
print 后面可以跟表达式,也可以用来调用函数。
returnreturn exp
signal
display
display/i $pc
$pc
表示指令的地址,/i
则表示输出格式为机器指令码。 于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的功能。 info display
查看display设置的自动显示的信息。GDB会打出一张表格,报告调试中设置了多少个自动显示设置,其中包括设置的编号,表达式,是否enable。display 的控制命令
undisplay delete display disable display num enable display numset print address
set print address on show print address 打开地址输出,当程序显示函数信息时,GDB 会显出函数的参数地址。系统默认为打开的。set print array
set print array on show print array 打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。这个选项默认是关闭的。set print elements
show print elements 这个选项主要是设置数组的,如果数组太大,那么就可以指定一个来指定数据显示的最大长度,当到达这个长度时,GDB就不再往下显示了。如果设置为0,则表示不限制。默认是200。set print null-stop
show print null-stop 如果打开了这个选项,那么当显示字符串时,遇到结束符则停止显示。默认为off。set print pretty on
show print pretty on 如果打开print pretty这个选项,那么当GDB显示结构体时会比较漂亮。默认off。set print sevenbit-strings
show print sevenbit-strings 设置字符显示,是否按“\nnn”的格式显示,如果打开,则字符串或字符数据按\nnn显示,如“\065”。默认 off。set print union
show print union 设置显示结构体时,是否显式其内的联合体数据。默认 on。set print object
show print object 在C++中,如果一个对象指针指向其派生类,如果打开这个选项,GDB会自动按照虚方法调用的规则显示输出,如果关闭这个选项的话,GDB就不管虚函数表了。这个选项默认是off。set print static-members
show print static-members 这个选项表示,当显示一个C++对象中的内容时是否显示其中的静态数据成员。默认是on。set print vtbl
show print vtbl 当此选项打开时,GDB将用比较规整的格式来显示虚函数表时。默认 off。当你用GDB的print查看程序运行时的数据时,你每一个print都会被GDB记录下来。GDB会以$1, $2, $3 …这样的方式为你每一个print命令编上号。于是,你可以使用这个编号访问以前的表达式,如$1。这个功能所带来的好处是,如果你先前输入了一个比较长的表达式,如果你还想查看这个表达式的值,你可以使用历史记录来访问,省去了重复输入。
可以在GDB的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。要定义一个GDB的变量很简单只需。使用GDB的set命令,GDB的环境变量和UNIX一样,也是以$起头。如:set $foo = *object_ptr
show convenience
该命令查看当前所设置的所有的环境变量。环境变量和程序变量的交互使用,将使得程序调试更为灵活便捷。例如:
set $i = 0print bar[$i++]->contents不必 print bar[0]->contents, printbar[1]->contents地输入命令了。只用敲回车,重复执行上一条语句,完成逐个输出的功能。
转载地址:http://lnstd.baihongyu.com/