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)。但是,问题仍然存在,因为我的错误搜索非常手动且乏味:如果我的程序中有一个变量,我如何回溯以找到导致它采用当前值的语句序列/链/树?是否有任何工具可以自动执行此操作?这包括将参数传递给函数、赋值语句(包括通过解引用指针赋值)等。
- 您可以尝试将 malloc 替换为 mmap 以保留数组开头之前或结尾之后的字节未映射。但是为什么 valgrind 不起作用?我认为 valgrind 应该是解决此类问题的工具。
-
gdb 有一个回溯:gnu.org/software/libc/manual/html_node/Backtraces.html。
-
@juhist:我认为替换 malloc 不是诀窍。这不是访问先前释放的内存、查找访问未分配内存的位置或类似问题的问题。我需要的是可以帮助我追踪创建/计算不可靠指针的位置的东西。换句话说,导致特定指针具有当前值的操作的依赖图是什么?我相信,FWIW 尝试了 dmalloc.com,与您提到的类似。
您应该用于解决此类问题的工具是 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 的代码对于这个答案来说太长了。
-
我不认为 valgrind 在这种情况下有帮助,因为问题不在于分配/读取/释放内存(好吧,它正在读取,但我正在从我无意的某个地方读取......为什么? )。问题在于确定指针运算的特定位、变量错误分配等导致了我的错误指针。考虑到我的问题只发生在 64 位系统上,似乎它可能来自通过 32 位整数转换传递的指针,或类似的东西。我不知道 valgrind 将如何帮助我追踪它。
-
用 str[10]='\\ 参考你的例子
',我的可能本质上是"10"可能是一个变量,我想知道"10"的值是从哪里来的。它是从某个地方计算出来的……但在哪里?应该可以追踪类似的东西。
-
哦,我想我当时误解了你的问题。内存断点方法可能会有所帮助,也可能没有任何帮助。
内存断点(GDB 文档中的观察点)听起来不错。使用 -g 编译调试符号,然后像这样放置一个内存写入断点:
1 2
| print &a
watch *0xdeadbeef |
如果您也想包含读取,您可以使用 awatch。查看 GDB 文档以获取更多信息。
这样,您应该能够在分段错误发生之前跟踪最后一次写入。
- 我将对该方法进行一些调查。我想我希望的是有点像堆栈跟踪的东西,除了向我展示创建最终触发段错误的无效指针所涉及的代码。这样的事情可能是自动的,潜在地,使用正确的聪明的调试工具......
-
在进一步调查中,我认为观察特定内存位置的方法也不完全正确。因为包含无效指针的内存位置在堆栈中(一个传入的参数),当相同的内存用于其他目的时,添加内存监视只会导致大量中断。如果它在堆上(malloc'ed 等),它会有所帮助。