关于调试:查找特定内存位置的数据上次修改时间? (C、gcc、gdb、valgrid、clang?)

Finding when data at a particular memory location was last modified? (C, gcc, gdb, valgrid, clang?)

我有一个令人讨厌的段错误,它一直困扰着我一段时间。它与代码从 32 位迁移到 64 位有关,但这是一个偶然的错误,很难追踪。

我想知道 - 是否有任何工具(首选 Linux、FOSS)可用于从段错误追溯,以查找在我的代码中非法(越界)指针被分配给它的位置价值?

例如,如果我尝试读取由我的变量 int *a 指向的 int 值(该值已在我的代码中某处远处的其他地方分配)而得到段错误,我如何找到该值在代码中的何处分配?

似乎可以用 clang/llvm 做那种事情,但我真的不知道去哪里找。我想这样的想法真的不能用 gdb 或 valgrind 来完成,因为 IFAIK 他们没有办法在程序执行期间存储所需的信息。

如果有人提出任何建议,我们将不胜感激!

编辑:经过多次挖掘,我发现了我一直在寻找的错误。基本上,\\'unsigned long *\\' 被强制转换为 \\'int *\\',以某种方式抑制警告(http://ascend4.org/b564)。但是,问题仍然存在,因为我的错误搜索非常手动且乏味:如果我的程序中有一个变量,我如何回溯以找到导致它采用当前值的语句序列/链/树?是否有任何工具可以自动执行此操作?这包括将参数传递给函数、赋值语句(包括通过解引用指针赋值)等。


您应该用于解决此类问题的工具是 valgrind。例如,尝试使用 Valgrind 执行以下代码:

1
2
char *str = malloc(10);
str[10] = '\\0';

它打印:

1
2
3
4
5
==14272== Invalid write of size 1
==14272==    at 0x80483E4: main (in /path/to/a.out)
==14272==  Address 0x4025032 is 0 bytes after a block of size 10 alloc'd
==14272==    at 0x4005BDC: malloc (vg_replace_malloc.c:195)
==14272==    by 0x80483D8: main (in /path/to/a.out)

然而,如果 valgrind 不适合你,一个选项是用 mmap 替换 malloc分配的块未映射。由于块大小通常不是页面大小的倍数,因此您只能选择其中一个选项,而不能同时选择两者。但是您可以分别使用"离开开始未映射"和"离开结束未映射"策略来运行您的问题,以捕获这两种错误。

不幸的是,关于如何用 mmap 替换 malloc 的代码对于这个答案来说太长了。


内存断点(GDB 文档中的观察点)听起来不错。使用 -g 编译调试符号,然后像这样放置一个内存写入断点:

1
2
print &a
watch *0xdeadbeef

如果您也想包含读取,您可以使用 awatch。查看 GDB 文档以获取更多信息。
这样,您应该能够在分段错误发生之前跟踪最后一次写入。