GDB是一个由GNU开源组织发布的,基于命令行的、功能强大的程序调试工具。可以让开发者能看到程序在执行时“内部”发生了什么,或者程序崩溃的现场。下图是gdb的吉祥物--弓箭鱼。
GDB主要做以下4种事情:
启动程序
使程序在指定条件下停止(比如打断点)
当程序停止时,检查发生了什么
改变程序的内容,这样可以方便debug程序
1 gdb常用命令
以下主要讲一些gdb的常用命令,按照功能进行简单的区分。
程序运行相关:
命令
命令缩写
命令说明
run
r
从开始运行一个待调试的程序,直到遇见断点或退出,每执行一次就会重开始执行
continue
c
让暂停的程序继续运行,直至遇到下一个断点
start
start 指令会执行程序至 main() 主函数的起始位置,即在 main() 函数的第一行语句处停止执行(该行代码尚未执行,相当于在main函数打断点然后run)。
quit
q
退出gdb调试
next
n
让 gdb跳到下一条命令去执行,并不一定是下一行,而是根据程序逻辑跳转到相应的位置
step
s
单步执行,遇到函数会进入
until
u
运行到指定行停下来
finish
fin
结束当前调用函数(直到当前函数运行完毕返回再返回),回到上一层调用函数处
return
return
结束当前调用函数并返回指定值,到上一层函数调用处
jump
j
将当前程序执行流跳转到指定行或地址
断点相关:
命令
命令缩写
命令说明
break
b
添加断点,可带如下参数:linenum 本地行号,即list命令可见的行号filename:linenum 指定个文件的行号function 函数,可以是自定义函数也可是库函数,如openfilename:function 指定文件中的函数condtion 条件*address 地址,可是函数,变量的地址,
delete
d
删除断点,参数格式是基于标识符的,不带参数时删掉所有断点
clear
清除断点,参数格式与break相同
enable
enable
启用某个断点
disable
disable
禁用某个断点
watch
watch
监视某一个变量或内存地址的值是否发生变化,可通过delete删除
tbreak
tb
零时断点,设置方法与break相同,只不过tbreak只在断点停一次,过后会自动将断点删除
查看变量或内存:
命令
命令缩写
命令说明
p
打印变量或寄存器值
info
i
查看断点 / 线程等信息
examine
x
以指定格式查看内存
display
会在每次暂停的时候输出表达式的值,display 会根据指定的模式自动选择使用 x 或是 print,取消使用undisplay n,不带参数将取消所有的
list
l
显示源码清单
disassemble
dis
查看汇编代码
backtrace
bt
查看当前线程的调用堆栈
dump
dump 一段内存内容:dump memory
设置变量、寄存器或内存:
命令
命令缩写
命令说明
set val i = 5
将变量i的值设置为5
set val $pc = 0xxxxx
将pc寄存器设置为某值
set {int}&i=5
同样是将变量i的值设置为5,改变地址中的值,等价于set *(int *)&i=5
set {int} 0xA0000000=5
改写内存0xA0000000 处的值为5
2 常用命令举例
2.1 print info x display等命令的区别
常用的print指令:
命令
作用
p var
打印变量var,变量可以是整数、浮点、字符串,数组、结构体变量等
p <表达式>
例如:p sizeof(long) 打印系统long类型所占的字节数
p i=10
改变变量的值i=10
常用的info指令:
命令
作用
info args
输出入参:argc与argv
info b
查看断点情况
info watchpoints
查看观察点情况
info display
显示display 命令查看的目标变量或表达式
info reg a4
查看寄存器a4, info reg打印所有寄存器
info line
查找行号以及文件名
常用x指令:
查看内存命令语法为:
x /
# N 要打印的单元数,可以为负值,表示往前数
# u表示每个单元的大小(b(byte), h(halfword), w(word), g(giant, 8 bytes))
# f表示打印的格式(o(octal), x(hex), d(decimal), u(unsigned decimal),t(binary), f(float), a(address),
# i(instruction), c(char), s(string)and z(hex, zero padded on the left))
例如:
命令
作用
x /16x addr
以16进制显示地址以addr为起始地址的 16个值
x /-16x addr
以16进制显示地址以addr为起始地址的往前数(倒着数)16个值
x /i $pc
/i 表示以机器指令显示当前pc处的指令
display命令:
display 命令也用于调试阶段查看某个变量或表达式的值,区别是会在每次暂停的时候输出表达式的值,display 会根据指定的模式自动选择使用 x 或是 print,这样方便我们观察变量。
3 调试中遇见的一些问题
3.1 打开gdb的日志功能
当我们在调试一个复杂的程序,gdb过程比较多,我们可以开启gdb的日志功能记录我们使用的gdb命令以及输出,记录在当前文件夹的gdb.txt中。
# 打开
set logging on
# 关闭
set logging off
3.2 gdb的TUI模式(Text User Interface)
在终端界面(TUI, Text User Interface)模式下, GDB可以和Visual Studio或者CLion一样像IDE一下显示和跟踪代码。
gdb -tui
# 或可在运行的过程中,执行:
Ctrl + x + a 或 tui enable
# 关闭tui模式同样执行 Ctrl + x + a 或tui disable
另外需要注意一个细节:
# 有个细节需要注意,此时的上下按键被锁定到了源码位置,如果想上下更新命令,需要如下快捷键:
Ctrl + n,下一命令(Next)
Ctrl + p,上一命令(Prev)
# 焦点切换:
focus next,切换到命令行。
focus prev,切换至源码。
进入后可使用命令行:layout用于分割窗口,可以一边查看代码,一遍测试。
layout src # 显示源代码窗口
layout asm # 显示汇编窗口
layout regs # 显示源代码/汇编和寄存器窗口
layout split # 显示源代码/汇编窗口
Ctrl+x后按2 #切换窗口类型, 可试一下Ctrl+x后按1
3.3 GDB远程调试方法
gdb远程调试用到了gdbserver,它允许gdb与调试的程序运行在不同的机器上,其实就是一个CS系统。
# 目标端开启gdbserver,启动端口
gdbserver host:2345
# 主机端运行gdb,并连接目标端口
gdb
target remote host:1234
load xxx.elf
# 查看v0-v31寄存器、vl寄存器
info reg v0
info reg vl
参考:
GDB: The GNU Project Debugger (sourceware.org)
dump_stack介绍以及内核符号表的生成和查找过程
原来gdb的底层调试原理这么简单
深入LUA脚本语言,让你彻底明白调试原理
RISC-V 入门 Part4: 编译、链接、加载