转载至GDB调试-从入门实践到原理
需要加 -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 |
显示函数堆栈的信息 (堆栈帧地址、指令寄存器的值等) |