我有一个小的单线程C ++应用程序,使用Visual Studio 2005编译和链接,它使用boost(crc,program_options和tokenizer),一小部分STL和各种其他系统头。
(它的主要目的是读取.csv并生成自定义二进制.dat和配对的.h声明结构,“解释”.dat的格式。)
当在调试器外部运行时,该工具正在崩溃(对NULL的访问冲突),仅在发布时。例如。按F5不会导致工具崩溃,Ctrl-F5会崩溃。当我重新连接调试器时,我得到了这个堆栈:
ntdll.dll!_RtlAllocateHeap@12() + 0x26916 bytes
csv2bin.exe!malloc(unsigned int size=0x00000014) Line 163 + 0x63 bytes C
csv2bin.exe!operator new(unsigned int size=0x00000014) Line 59 + 0x8 bytes C++
>csv2bin.exe!Record::addField(const char * string=0x0034aac8) Line 62 + 0x7 bytes C++
csv2bin.exe!main(int argc=0x00000007, char * * argv=0x00343998) Line 253 C++
csv2bin.exe!__tmainCRTStartup() Line 327 + 0x12 bytes C
它崩溃的界限是一种看似无害的分配:
pField = new NumberField(this, static_cast<NumberFieldInfo*>(pFieldInfo));
...我不相信它已经到达了构造函数,它只是在跳转到构造函数之前分配内存。它在崩溃时也会执行此代码几十次,通常是在一致的(但不是非可疑的)位置。
使用/ MTd或/ MDd(调试运行时)编译时问题消失,使用/ MT或/ MD时返回。
从堆栈加载NULL,我可以在内存视图中看到它。 _RtlAllocateHeap @ 12 + 0x26916字节看起来像巨大的偏移量,就像跳转不正确一样。
我在调试版本中尝试了_HAS_ITERATOR_DEBUGGING
,并没有带来任何可疑的东西。
在Record :: addField的开头和结尾删除HeapValidate,直到它崩溃时显示一个OK堆。
这曾经起作用 - 我不完全确定现在和我们上次编译工具之间发生了什么变化(可能是几年前,可能是在较旧的VS之下)。我们尝试过较旧版本的增强版(1.36 vs 1.38)。
在回退到手动调查代码或将其提供给PC-Lint并梳理其输出之前,有关如何有效调试此代码的任何建议吗?
[如果您在评论中请求信息,我将很乐意通过更多信息更新问题。]
答案 0 :(得分:12)
运行调试器与否之间的一个小知识是操作系统调试堆(另请参阅Why does my code run slowly when I have debugger attached?)。您可以使用环境变量_NO_DEBUG_HEAP关闭调试堆。您可以在计算机属性中或在Visual Studio的“项目设置”中指定它。
关闭调试堆后,即使连接了调试器,也应该看到相同的崩溃。
尽管如此,请注意内存损坏可能很难调试,因为腐败的真正原因(如某些缓冲区溢出)通常可能与您看到症状(崩溃)的位置相距甚远。
答案 1 :(得分:4)
Application Verifier对解决这个问题非常有用,请参阅此处接受的答案:Finding where memory was last freed?
我可能也值得一提pageheap,我在查看Application Verifier时发现了{{3}}。看起来它涵盖了一些类似的基础。
(仅供参考,这是一个单字符缓冲区溢出:
m_pEnumName = (char*)malloc(strlen(data) /* missing +1 here */);
strcpy(m_pEnumName, data);
......另一个不直接使用strcpy
的好理由。)
答案 2 :(得分:2)
在new或malloc中崩溃通常暗示malloc实现的(内部)结构已被破坏。这大部分时间是通过写入过去的分配(缓冲区溢出)完成的。然后在下一次调用new或malloc时,应用程序崩溃,因为内部结构现在包含无效数据。
检查您是否可以覆盖以前分配的任何空间。
如果您的应用程序是可移植的,您可以尝试在Linux上构建它并在Valgrind下运行它。