转载至GDB调试-从入门实践到原理


gcc -g t1.c -o t1

需要加 -g 来产生调试符号, 否则GDB会出现 Make breakpoint pending on future shared library load?

断点

breakpoint

命令 作用
break [file]:function 在文件 file 的 function 函数入口设置断点
break [file]:line 在文件 file 的第 line 行设置断点
info breakpoints 查看断点列表
break [+-]offset 在当前位置偏移量为 [+-]offset 处设置断点
break \*addr 在地址 addr 处设置断点
break ... if expr 设置条件断点, 仅仅在条件满足时
ignore n count 接下来对于编号为n的断点忽略 count 次
clear 删除所有断点
clear function 删除所有位于 function 内的断点
delete n 删除指定编号的断点
enable n 启用指定编号的断点
disable n 禁用指定编号的断点
save breakpoints file 保存断点信息到指定文件
source file 导入文件中保存的断点信息
break 在下一个指令处设置断点
clear [file:]line 删除第 line 行的断点

watchpoint

watchpoint 要求GDB暂停程序执行的命令。区别在于 watchpoint 没有驻留某一行源代码中, 而是指示GDB每当某个表达式改变了值就暂停执行的命令

命令 作用
watch variable 设置变量数据断点
watch var1 + var2 设置表达式数据断点
rwatch variable 设置读断点, 仅支持硬件实现
awatch variable 设置读写断点, 仅支持硬件实现
info watchpoints 查看数据断点列表
set can-use-hw-watchpoints 0 强制基于软件方式实现

使用数据断点时, 需要注意:

  • 当监控变量为局部变量时, 一旦局部变量失效, 数据断点也会失效
  • 如果监控的是指针变量 p, 则 watch *p 监控的是 p 所指内存数据的变化情况, 而 watch p 监控的是 p 指针本身有没有改变指向

最常见的数据断点应用场景:「定位堆上的结构体内部成员何时被修改」

命令 作用
print &variable 查看变量的内存地址
watch \*(type \*)address 通过内存地址间接设置断点
watch -l variable 指定 location 参数
watch variable thread 1 仅编号为1的线程修改变量var值时会中断

catchpoint

主要监测信号的产生, 例如 c++ 的 throw , 或者加载库的时候, 产生断点行为

命令 作用
catch fork 程序调用 fork 时中断
tcatch fork 设置的断点只触发一次, 之后被自动删除
catch syscall ptrace 为 ptrace 系统调用设置断点

命令行

命令 作用
run arglist 以 arglist 为参数列表运行程序
set args arglist 指定启动命令行参数
set args 指定空的参数列表
show args 打印命令行列表

程序栈

命令 作用
backtrace [n] 打印栈帧
frame [n] 选择第 n 个栈帧, 如果不存在, 则打印当前栈帧
up n 选择当前栈帧编号 +n 的栈帧
down n 选择当前栈帧编号 -n 的栈帧
info frame [addr] 描述当前选择的栈帧
info args 当前栈帧的参数列表
info locals 当前栈帧的局部变量

多进程、多线程

多进程

GDB在调试多进程程序(程序含 fork 调用)时, 默认只追踪父进程。可以通过命令设置, 实现只追踪父进程或子进程, 或者同时调试父进程和子进程。

命令 作用
info inferiors 查看进程列表
attach pid 绑定进程 id
inferior num 切换到指定进程上进行调试
print $\_exitcode 显示程序退出时的返回值
set follow-fork-mode child 追踪子进程
set follow-fork-mode parent 追踪父进程
set detach-on-fork on fork 调用时只追踪其中一个进程
set detach-on-fork off fork 调用时会同时追踪父子进程

多线程

默认调试多线程时, **一旦程序中断, 所有线程都将暂停**。如果此时再继续执行当前线程, 其他线程也会同时执行

命令 作用
info threads 查看线程列表
print $\_thread 显示当前正在调试的线程编号
set scheduler-locking on 调试一个线程时, 其他线程暂停执行
set scheduler-locking off 调试一个线程时, 其他线程同步执行
set scheduler-locking step 仅用 step 调试线程时其他线程不执行, 用其他命令如 next 调试时仍执行

如果只关心当前线程, 建议临时设置 scheduler-locking 为 on, 避免其他线程同时运行, 导致命中其他断点分散注意力

打印输出

在调试的过程中, 我们需要查看某个变量的值, 以分析其是否符合预期, 这个时候就需要打印输出变量值

命令 作用
whatis variable 查看变量的类型
ptype variable 查看变量详细的类型信息
info variables var 查看定义该变量的文件, 不支持局部变量

打印字符串

使用 x/s 命令打印 ASCII 字符串, 如果是宽字符字符串, 需要先看宽字符的长度 print sizeof(str):如果长度为 2, 则使用 x/hs 打印;如果长度为 4 , 则使用 x/ws 打印

命令 作用
x/s str 打印字符串
set print elements 0 打印不限制字符串长度/或不限制数组长度
call printf("%s\n",xxx) 这时打印出的字符串不会含有多余的转义符
printf "%s\n",xxx 同上

打印数组

命令 作用
print \*array@10 打印从数组开头连续10个元素的值
print array[60]@10 打印array数组下标从60开始的10个元素, 即第60~69个元素
set print array-indexes on 打印数组元素时, 同时打印数组的下标

打印指针

命令 作用
print ptr 查看该指针指向的类型及指针地址
print \*(struct xxx \*)ptr 查看指向的结构体的内容

打印指定内存地址的值

使用 x 命令来打印内存的值, 格式为 x/nfu addr , 以 f 格式打印从 addr 开始的 n 个长度单元为u的内存值:

  • n:输出单元的个数
  • f:输出格式, 如 x 表示以 16 进制输出, o 表示以 8 进制输出, 默认为x
  • u:一个单元的长度, b 表示 1 个 byte , h 表示 2 个 byte (half word), w 表示 4 个 byte, g 表示 8 个 byte (giant word)
命令 作用
x/8xb array 以16进制打印数组 array 的前8个 byte 的值
x/8xw array 以16进制打印数组 array 的前16个 word 的值

打印局部变量

命令 作用
info locals 打印当前函数局部变量的值
backtrace full 打印当前栈帧各个函数的局部变量值, 命令可缩写为 bt
bt full n 从内到外显示 n 个栈帧及其局部变量
bt full -n 从外向内显示 n 个栈帧及其局部变量

打印结构体

命令 作用
set print pretty on 每行只显示结构体的一名成员
set print null-stop 不显示 ‘\000’ 这种

函数跳转

命令 作用
set step-mode on 不跳过不含调试信息的函数, 可以显示和调试汇编代码
finish 执行完当前函数并打印返回值, 然后触发中断
return 0 不再执行后面的指令, 直接返回, 可以指定返回值
call printf("%s\n", str) 调用 printf 函数, 打印字符串 (可以使用 call 或者 print 调用函数)
print func() 调用 func 函数 (可以使用 call 或者 print 调用函数)
set var variable=xxx 设置变量 variable 的值为 xxx
set {type}address = xxx 给存储地址为 address , 类型为 type 的变量赋值
info frame 显示函数堆栈的信息 (堆栈帧地址、指令寄存器的值等)