我能在GDB中设置“内存访问”断点吗?

270

我正在通过gdb运行一个应用程序,并希望为特定变量被访问/更改的任何时候设置断点。有没有好方法可以做到这一点? 我还对其他监视C / C ++变量的方法感兴趣,以查看它何时发生更改。

6个回答

320
< p > 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.

8
如果你想要观察 C++ 方法中的成员,我发现这个变量非常有用:watch -location mTextFormatted - Ivan Vučica
1
如果我没有变量的地址怎么办?我能直接使用它的名称吗? - khatchad
6
您可以使用取地址运算符让GDB打印变量的地址。命令为:print &variable - Loduwijk
在使用awatch时如何区分由于读取还是写入而导致的中断? - user1065101
3
这个回答并没有涉及watch命令所监视的内存位置的大小。读完上面的内容后,第一个浮现在脑海中的问题是:rwatch *0xfeedface会实际监视多少字节?请注意,不要改变原意。 - AnT stands with Russia
8
@AnT,我假设它只会监视一个字节,这似乎是情况,但你可以将其转换为特定类型,例如rwatch *(int *)0xfeedface,它将监视sizeof(int)个字节:https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html - asksol

43
你要找的是叫做“监视点”的东西。
用法:
(gdb) watch foo:监视变量foo的值。
(gdb) watch *(int*)0x12345678:监视指针地址所指向的值,可以转换成任何类型。
(gdb) watch a*b + c/d:监视一个任意复杂的表达式,该表达式在程序的本地语言中有效。
监视点有三种类型:
1. watch:当发生写操作时,gdb会中断程序执行。 2. rwatch:当发生读操作时,gdb会中断程序执行。 3. awatch:在两种情况下都会中断程序执行。
你可以根据需要选择最适合你的类型。
欲了解更多信息,请参见this

6
我撰写了另一个回答,因为现有的回答对我来说似乎不是很直观... - Paolo M

25
假设第一个答案是指C语言类似的语法 (char *)(0x135700 +0xec1a04f),那么执行 rwatch *0x135700+0xec1a04f 是错误的。正确的语法应该是 rwatch *(0x135700+0xec1a04f)
缺少括号会使我自己使用观察点时感到很痛苦。

9

我刚刚尝试了以下内容:

 $ 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应该回退到软件监视点。 - Tod

2
使用 watch 命令来监视变量何时被写入,使用 rwatch 命令来监视变量的读取,使用 awatch 命令来监视变量的读/写操作,如上所述。但请注意,要使用此命令,您必须中断程序,并且在程序中断时变量必须在作用域内:
使用 watch 命令。watch 命令的参数是一个表达式,该表达式将被计算。这意味着您要设置观察点的变量必须在当前作用域内。因此,要在非全局变量上设置监视点,您必须设置一个断点,以便在变量在作用域内时停止程序。在程序中断后,设置观察点。

0

除了asksolPaolo 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断点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接