我有一个不确定的内存损坏问题。因为它并不总是相同的地址,而且很少发生,所以我不能简单地用gdb
watchpoint
它。
问题是我的程序中 A 点和 B 点之间的值更改。唯一应该更改它的是 C 点,它不会在那个时间内运行(至少对于遇到意外修改的特定实例不会)。
我想做的是将值mprotect
在 A 点,这样机器就会在修改时捕获它,并在 C 点的有意修改周围再次取消保护它。当然,mprotect
并不意味着从字面上理解,因为我需要它来处理单词粒度。
简单地用gdb
手动观察A点是太多的辛苦,问题的频率只有千分之一左右。
理想情况下,我希望在修改它的点进行堆栈跟踪。
有什么想法吗?
更新:我刚刚发现了rr
http://rr-project.org/,据称该工具可以"确定"非确定性问题。我要试一试。
更新2:嗯,这是一次短途旅行:
[FATAL /build/rr-jR8ti5/rr-4.1.0/src/PerfCounters.cc:167:init_attributes() errno: 0 'Success']
-> Microarchitecture `Intel Merom' currently unsupported.
您正在经历未定义的行为,并且它是在其他地方引起的,调试这真的很难。
由于您显然使用的是Linux,请使用valgrind,它将对您有很大帮助。如果您不在Linux或(valgrind也支持的OS X)上,请为您的系统搜索等效的内存错误检测软件。
我发现用您知道的脚本语言编写gdb
脚本并不难(在我的情况下,Ruby
)。这减少了学习如何制作正确的gdb
脚本的需要!
目标程序和脚本之间的 API 是目标程序具有一个名为my_breakpoint
的空白函数,该函数接受单个机器字作为参数。呼叫my_breakpoint(1); my_breakpoint(addr);
会将地址添加到监视列表中,而使用常量2
相同的操作会从监视列表中删除地址。
要使用它,您需要启动gdbserver 127.0.0.1:7117 myapp myargs
,然后启动以下脚本。当脚本检测到问题时,它会与gdbserver
完全断开连接,以便您可以使用gdb -ex 'target remote 127.0.0.1:7117'
重新连接另一个gdb
实例,然后就可以了。
请注意,使用这样的软件观察点非常慢;也许有一天这样的东西可以作为valgrind工具实现。
#!/usr/bin/env ruby
system("rm -f /tmp/gdb_i /tmp/gdb_o");
system("mkfifo /tmp/gdb_i /tmp/gdb_o");
system("killall -w gdb");
system("gdb -ex 'target remote 127.0.0.1:7117' </tmp/gdb_i >/tmp/gdb_o &");
$fo = File.open("/tmp/gdb_i", "wb");
$fi = File.open("/tmp/gdb_o", "rb");
def gdb_put(l)
$stderr.puts("gdb_out: #{l}");
$fo.write((l + "n"));
$fo.flush;
end
gdb_put("b my_breakpoint");
gdb_put("set can-use-hw-watchpoints 0");
gdb_put("c");
$state = 0;
$watchpoint_ctr = 1; # start at 1 so the 1st watchpoint gets 2, etc. this is because the breakpoint gets 1.
$watchpoint_nr = {};
def gdb_got_my_breakpoint(x)
$stderr.puts("my_breakpoint #{x}");
if ((x == 1) || (x == 2))
raise if ($state != 0);
$state = x;
gdb_put("c");
else
if ($state == 1)
raise if ($watchpoint_nr[x].nil?.!);
$watchpoint_nr[x] = ($watchpoint_ctr += 1);
gdb_put("watch *#{x}");
elsif ($state == 2)
nr = $watchpoint_nr[x];
if (nr.nil?)
$stderr.puts("WARNING: ignoring delete request for watchpoint #{x} not previously established");
else
gdb_put("delete #{nr}");
$watchpoint_nr.delete(x);
end
end
$state = 0;
gdb_put("info breakpoints");
$stderr.puts("INFO: my current notion: #{$watchpoint_nr}");
gdb_put("c");
end
end
def gdb_got(l)
t = l.split;
if ((t[0] == "Breakpoint") && (t[2] == "my_breakpoint"))
gdb_got_my_breakpoint(t[3][3..-2].to_i);
end
if (l.start_with?("Program received signal ") || l.start_with?("Watchpoint "))
gdb_put("disconnect");
gdb_put("q");
sleep;
end
end
while (l = $fi.gets)
l = l.strip;
$stderr.puts("gdb_inp: #{l}");
gdb_got(l);
end