我可以在 GDB 中的“内存访问”上设置断点吗?

Posted

技术标签:

【中文标题】我可以在 GDB 中的“内存访问”上设置断点吗?【英文标题】:Can I set a breakpoint on 'memory access' in GDB? 【发布时间】:2010-09-08 16:49:08 【问题描述】:

我正在通过 gdb 运行应用程序,并且我想在任何时候访问/更改特定变量时设置断点。有没有这样做的好方法?我也对在 C/C++ 中监视变量以查看它是否/何时更改的其他方法感兴趣。

【问题讨论】:

【参考方案1】:

watch 只在写入时中断,rwatch 让您在读取时中断,而 awatch 让您在读取/写入时中断。

您可以在内存位置设置读取观察点:

gdb$ rwatch *0xfeedface
Hardware read watchpoint 2: *0xfeedface

但有一个限制适用于 rwatch 和 awatch 命令;你不能使用 gdb 变量 在表达式中:

gdb$ rwatch $ebx+0xec1a04f
Expression cannot be implemented with read/access watchpoint.

所以你必须自己扩展它们:

gdb$ print $ebx 
$13 = 0x135700
gdb$ rwatch *0x135700+0xec1a04f
Hardware read watchpoint 3: *0x135700 + 0xec1a04f
gdb$ c
Hardware read watchpoint 3: *0x135700 + 0xec1a04f

Value = 0xec34daf
0x9527d6e7 in objc_msgSend ()

编辑:哦,顺便说一句。您需要硬件或软件支持。软件显然要慢得多。要了解您的操作系统是否支持硬件观察点,您可以查看 can-use-hw-watchpoints 环境设置。

gdb$ show can-use-hw-watchpoints
Debugger's willingness to use watchpoint hardware is 1.

【讨论】:

如果你想查看某个 C++ 方法的成员,我发现这个变体非常有用:watch -location mTextFormatted 如果我没有变量的地址怎么办?我可以只用它的名字吗? 您可以让 GDB 使用 address-of 运算符打印变量的地址。 print &variable 这个答案没有说明watch 命令正在监视的内存位置的size。同时,这是阅读上述内容后首先想到的问题。 rwatch *0xfeedface 实际会观看多少字节? @AnT,我假设它会观看一个字节,这似乎是这种情况,但您可以将其转换为特定类型,例如rwatch *(int *)0xfeedface 它将观看 sizeof(int) 字节:sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html【参考方案2】:

您要查找的内容称为观察点

用法

(gdb) watch foo:观察变量的值foo

(gdb) watch *(int*)0x12345678:观察地址指向的值,转换成你想要的任何类型

(gdb) watch a*b + c/d:观察任意复杂表达式,在程序母语中有效

观察点分为三种:

watch:当 write 发生时 gdb 会中断 rwatch:gdb 将在 read 发生时中断 awatch:gdb 将在两种情况下中断

您可以选择更适合您需要的。

欲了解更多信息,请查看this。

【讨论】:

我写了 another 答案,因为现有的答案对我来说似乎不是很简单......【参考方案3】:

假设第一个答案是指类似 C 的语法 (char *)(0x135700 +0xec1a04f),那么执行 rwatch *0x135700+0xec1a04f 的答案是不正确的。正确的语法是rwatch *(0x135700+0xec1a04f)

那里缺少()s 给我自己尝试使用观察点带来了很大的痛苦。

【讨论】:

【参考方案4】:

我刚刚尝试了以下方法:

 $ cat gdbtest.c
 int abc = 43;

 int main()
 
   abc = 10;
 
 $ gcc -g -o gdbtest gdbtest.c
 $ gdb gdbtest
 ...
 (gdb) watch abc
 Hardware watchpoint 1: abc
 (gdb) r
 Starting program: /home/mweerden/gdbtest 
 ...

 Old value = 43
 New value = 10
 main () at gdbtest.c:6
 6       
 (gdb) quit

所以看起来可能,但您确实需要一些硬件支持。

【讨论】:

如果您的平台不支持硬件观察点,gdb 应该回退到软件观察点。【参考方案5】:

使用 watch 查看何时写入变量,使用 rwatch 读取变量,使用 awatch 读取/写入/读取变量,如上所述。但是请注意,要使用这个命令,你必须要中断程序,并且当你中断程序时,变量必须在作用域内:

使用 watch 命令。 watch 命令的参数是 被评估的表达式。这意味着您想要的变量 设置观察点必须在当前范围内。所以,要设置一个 非全局变量上的观察点,您必须设置断点 当变量在范围内时,这将停止您的程序。你设置 程序中断后的观察点。

【讨论】:

【参考方案6】:

除了asksol和Paolo M已经回答/评论的内容

我一开始没看明白,为什么我们需要转换结果。虽然我读到了这个:https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html,但对我来说并不直观..

于是我做了一个实验,让结果更清晰: 代码:(假设 int main() 位于第 3 行;int i=0 位于第 5 行,其他代码.. 位于第 10 行)

int main()

int i = 0;
int j;
i = 3840 // binary 1100 0000 0000 to take into account endianness
other code..

然后我用可执行文件启动 gdb 在我的第一次尝试中,我在变量的位置设置断点而不进行强制转换,以下是显示的结果

Thread 1 "testing2" h
Breakpoint 2 at 0x10040109b: file testing2.c, line 10.
(gdb) s
7           i = 3840;
(gdb) p i
$1 = 0
(gdb) p &i
$2 = (int *) 0xffffcbfc
(gdb) watch *0xffffcbfc
Hardware watchpoint 3: *0xffffcbfc
(gdb) s
[New Thread 13168.0xa74]

Thread 1 "testing2" hit Breakpoint 2, main () at testing2.c:10
10          b = a;
(gdb) p i
$3 = 3840
(gdb) p *0xffffcbfc
$4 = 3840
(gdb) p/t *0xffffcbfc
$5 = 111100000000

正如我们所见,第 10 行的断点是由我设置的。 gdb 没有中断,因为尽管变量 i 发生了变化,但被监视的位置没有改变(由于字节顺序,因为它继续保持全 0)

在我的第二次尝试中,我对变量的地址进行了强制转换以监视所有 sizeof(int) 字节。这次:

(gdb) p &i
$6 = (int *) 0xffffcbfc
(gdb) p i
$7 = 0
(gdb) watch *(int *) 0xffffcbfc
Hardware watchpoint 6: *(int *) 0xffffcbfc
(gdb) b 10
Breakpoint 7 at 0x10040109b: file testing2.c, line 10.
(gdb) i b
Num     Type           Disp Enb Address            What
6       hw watchpoint  keep y                      *(int *) 0xffffcbfc
7       breakpoint     keep y   0x000000010040109b in main at testing2.c:10
(gdb) n
[New Thread 21508.0x3c30]

Thread 1 "testing2" hit Hardware watchpoint 6: *(int *) 0xffffcbfc

Old value = 0
New value = 3840

Thread 1 "testing2" hit Breakpoint 7, main () at testing2.c:10
10          b = a;

gdb 中断,因为它检测到值已更改。

【讨论】:

以上是关于我可以在 GDB 中的“内存访问”上设置断点吗?的主要内容,如果未能解决你的问题,请参考以下文章

跟踪所有变量以执行gdb中的函数

如何在达到给定次数的点之后使GDB断点断开?

关闭在 GDB 中设置断点的确认 [重复]

这是 ubuntu 16.04 中的 gdb 错误吗?

有没有办法使用纯 gdb 脚本来测试我们是不是在断点上?

无法设置 gdb 断点