gdb 基本操作

gdb

相信大家或多或少已经接触过 IDE 中集成的调试工具。今天介绍一款接近命令行、灵活强大的调试工具 gdb(GNU debugger), 它是由 GNU 提供的软件调试工具,提供了 TUI 界面,用好 gdb 可以极大提高我们定位问题的速度。这里推荐一个 GDB QUICK REFERENCE 方便查阅命令。

开启调试

这部分不详述,网上相关信息已经很多。

gdb attach pid

// start gdb
aarch64-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:76
76        {
    
    
  • info f,这个命令会打印出更为详细的当前栈层的信息,只不过大多数都是运行时的内存地址。
    比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。
(gdb) info f
Stack 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 命令。

源码显示

  • list
    一般是打印当前行的上 5 行和后 4 行,如果显示函数是是上 2 行后 8 行,默认总共 10 行。当然,你也可以定制显示的范围,使用下面命令可以设置一次显示源程序的行数。
// 查看当前的设置值
show listsize
// 设置新值
set listsize

// 已 offset 为中心,前后显示 5 行
list <+offset>
list <-offset>

源码搜索

  • forward-search,向前面搜索。
  • reverse-search,全部搜索。
  • show directories,显示定义了的源文件搜索路径。

源码内存

  • info line xxx
    查看源代码在内存中的地址。这个命令会打印出所指定的源码在运行时的内存地址。
    info line后面可以跟 “行号”,“函数名”,“文件名:行号”,“文件名:函数名”。

  • disassemble funcname
    查看源程序的当前执行时的机器码,这个命令会把目前内存中的指令dump出来。

运行时数据显示

  • p <variable>, print
  • p/<format> <variable>, i.e., p /x a, 16进制显示变量 a 的值
  • p <file>::<variable>, 显示某个文件的某个变量
  • p <function>::<variable>, 显示某个函数的某个变量

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
n: number of memory block
f: format letter
u: size letter

上述命令会从指定的内存地址开始,读写 n 个 u 指定格式的数据,并使用 f 指定的格式显示出来。

  • n 是一个正整数,表示显示的内存单元个数。
  • f 表示显示的格式 format,参见上面。如果地址所指的是字符串,那么格式可以是 s,如果是指令地址,那么格式可以是 i。
  • u 表示一个地址单位的长度。u 参数可以用下面的字符来代替,b 表示单字节,h 表示半字,即双字节,w 表示四字节,g(giant) 表示八字节。默认为 8 字节,即 g。
(gdb) x/i $pc
=> 0x45003c <add_float_neon1+20>:       ldr     q3, [x0, #32]

因此如果想按单字节的方式读取 buffer 地址开始的 8 个字节的数据,并按16进制显示,可以使用 x/8xb buffer

查看寄存器

  • info registers 查看通用寄存器的情况。
  • info all-registers 查看所有寄存器的情况。包括浮点寄存器
  • info registers 查看所指定的寄存器组的情况。
(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:line
break funcname

// 条件断点
b xxx if <condition>

// 查看断点
info b

// 禁用断点
disable num
enable num
disable // disable all brk
enable // enable all brk

// 删除断点
d num // delete brk num
d // delete all brk

// continue
c

// suspend
ctrl + c

// step to next
next

// step into
s

// step out
finish / fin

// run until
until / u

变量修改

修改被调试程序运行时的变量值,使用 printset 命令即可完成。print x=4 或者 set x=4
在某些时候,很有可能你的变量和GDB中的参数冲突,如 width,set width 是 GDB 的命令,所以会出现 “Invalid syntax inexpression” 的错误,此时,你可以使用 set var 命令来告诉GDB width 是程序的变量名,如:set var width=10

执行流更改

跳转执行

jump <address>
注意,jump命令不会改变当前的程序栈中的内容,所以当从一个函数跳到另一个函数时,返回时进行弹栈操作时必然会发生错误,可能结果还非常奇怪,所以最好是同一个函数中进行跳转。

当然也可以使用 set $pc 来更改跳转执行的地址。如:set $pc = 0x485

call <...>
表达式中可以是函数,以此达到强制调用函数的目的。如果有返回值则显示函数的返回值。

print 后面可以跟表达式,也可以用来调用函数。

返回值修改

return
return exp

信号

signal

自动显示

  • display
    display/i $pc
    $pc 表示指令的地址,/i 则表示输出格式为机器指令码。
    于是当程序停下后,就会出现源代码和机器指令码相对应的情形,这是一个很有意思的功能。
    在这里插入图片描述

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

  • display 的控制命令
    undisplay
    delete display
    disable display num
    enable display num

显示选项

  • set 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的变量很简单只需使用GDB的set命令,GDB的环境变量和UNIX一样,也是以$起头。如:set $foo = *object_ptr
使用环境变量时,GDB会在你第一次使用时创建这个变量,而在以后的使用中,则直接对其賦值。环境变量没有类型,你可以给环境变量定义任一的类型。包括结构体和数组。

show convenience
该命令查看当前所设置的所有的环境变量。

环境变量和程序变量的交互使用,将使得程序调试更为灵活便捷。例如:

set $i = 0
print bar[$i++]->contents
不必 print bar[0]->contents, printbar[1]->contents地输入命令了。
只用敲回车,重复执行上一条语句,完成逐个输出的功能。

猜你喜欢

转载自blog.csdn.net/FJDJFKDJFKDJFKD/article/details/99965571
今日推荐