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


gcc -g t1.c -o t1

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

断点

breakpoint

命令 作用
break [file]:function 在文件 filefunction 函数入口设置断点
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-lockingon, 避免其他线程同时运行, 导致命中其他断点分散注意力

打印输出

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

命令 作用
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 表示 1byte , h 表示 2byte (half word), w 表示 4byte, g 表示 8byte (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 显示函数堆栈的信息 (堆栈帧地址、指令寄存器的值等)