GDB之常用命令介绍

GDB是一个由GNU开源组织发布的,基于命令行的、功能强大的程序调试工具。可以让开发者能看到程序在执行时“内部”发生了什么,或者程序崩溃的现场。下图是gdb的吉祥物--弓箭鱼。

image-20230415163046991

GDB主要做以下4种事情:

  1. 启动程序
  2. 使程序在指定条件下停止(比如打断点)
  3. 当程序停止时,检查发生了什么
  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 函数,可以是自定义函数也可是库函数,如open
filename:function 指定文件中的函数
condtion 条件
*address 地址,可是函数,变量的地址,
delete d 删除断点,参数格式是基于标识符的,不带参数时删掉所有断点
clear 清除断点,参数格式与break相同
enable enable 启用某个断点
disable disable 禁用某个断点
watch watch 监视某一个变量或内存地址的值是否发生变化,可通过delete删除
tbreak tb 零时断点,设置方法与break相同,只不过tbreak只在断点停一次,过后会自动将断点删除

查看变量或内存:

命令 命令缩写 命令说明
print p 打印变量或寄存器值
info i 查看断点 / 线程等信息
examine x 以指定格式查看内存
display 会在每次暂停的时候输出表达式的值,display 会根据指定的模式自动选择使用 x 或是 print,取消使用undisplay n,不带参数将取消所有的
list l 显示源码清单
disassemble dis 查看汇编代码
backtrace bt 查看当前线程的调用堆栈
dump dump 一段内存内容:dump memory <filename> <start-address> <end-address>

设置变量、寄存器或内存:

命令 命令缩写 命令说明
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打印所有寄存器

常用x指令:

查看内存命令语法为:

x /<Nuf> <addr>
# 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

参考:

  1. GDB: The GNU Project Debugger (sourceware.org)
  2. dump_stack介绍以及内核符号表的生成和查找过程
  3. 原来gdb的底层调试原理这么简单
  4. 深入LUA脚本语言,让你彻底明白调试原理
  5. RISC-V 入门 Part4: 编译、链接、加载
posted @ 2023-04-16 10:09  sureZ_ok  阅读(401)  评论(0编辑  收藏  举报