我这里有一个"Schroedinger's Cat"类型的问题 - 我的程序(实际上是我的程序的测试套件,但是程序仍然是)崩溃,但只有在发布模式下构建时才会发生,并且只有在从命令行启动时.通过穴居人调试(即整个地方讨厌的printf()消息),我已经确定了代码崩溃的测试方法,但遗憾的是实际的崩溃似乎发生在某些析构函数中,因为我看到的最后一条跟踪消息都在其他执行干净的析构函数.
当我尝试在Visual Studio中运行此程序时,它不会崩溃.从WinDbg.exe启动时也是如此.仅从命令行启动时才会发生崩溃.这是在Windows Vista,btw下发生的,不幸的是我现在无法访问XP机器进行测试.
这将是非常好的,如果我能得到的Windows打印出堆栈跟踪,或一些其他不是简单地结束,如果它已经退出干净方案.有没有人对如何在这里获得更有意义的信息有任何建议,希望能解决这个问题?
编辑:问题确实是由一个越界数组引起的,我在这篇文章中对此进行了更多描述.感谢大家帮忙找到这个问题!
在我见过或听说过的100%的情况下,C或C++程序在调试器中运行良好但在外部运行时失败,原因是写入函数本地数组的末尾.(调试器会在堆栈上放置更多内容,因此您不太可能覆盖重要的内容.)
当我遇到这样的问题之前,它通常是由于变量初始化.在调试模式下,变量和指针会自动初始化为零,但在发布模式下则不会.因此,如果您有这样的代码
int* p; .... if (p == 0) { // do stuff }
在调试模式下,if中的代码未执行,但在发布模式下,p包含未定义的值,该值不太可能为0,因此代码执行时经常导致崩溃.
我会检查你的代码是否有未初始化的变量.这也适用于数组的内容.
到目前为止,没有答案试图对调试发布应用程序的可用技术进行认真的概述:
由于许多原因,发布和调试版本的行为有所不同. 这是一个很好的概述.这些差异中的每一个都可能导致Release构建中的一个错误,该错误在Debug构建中不存在.
调试器的存在也可能改变程序的行为,包括发布和调试版本.看到这个答案.简而言之,至少Visual Studio Debugger在附加到程序时会自动使用Debug Heap.您可以使用环境变量_NO_DEBUG_HEAP关闭调试堆.您可以在计算机属性中或在Visual Studio的"项目设置"中指定此项.这可能会使附加的调试器重现崩溃.
更多关于调试堆损坏的信息.
如果以前的解决方案不起作用,则需要捕获未处理的异常并附加事后调试器发生崩溃的实例.您可以使用例如WinDbg,有关可用的事后调试程序及其在MSDN上的安装的详细信息
您可以改进异常处理代码,如果这是生产应用程序,您应该:
一个.使用安装自定义终止处理程序std::set_terminate
如果要在本地调试此问题,可以在终止处理程序中运行无限循环,并将一些文本输出到控制台以通知您std::terminate
已被调用.然后附加调试器并检查调用堆栈. 或者按照本答案中的描述打印堆栈跟踪.
在生产应用程序中,您可能希望将错误报告发送回家,最好与小内存转储一起发送,以便您按照此处所述分析问题.
湾 使用Microsoft的结构化异常处理机制,允许您捕获硬件和软件异常.请参阅MSDN.您可以使用SEH保护代码的一部分,并使用与a)相同的方法来调试问题.SEH提供了有关从生产应用程序发送错误报告时可能使用的异常的更多信息.
需要注意的事项:
数组溢出 - visual studio调试器插入填充,可以阻止崩溃.
竞争条件 - 您是否涉及多个线程,如果这样,竞争条件很多只会在直接执行应用程序时出现.
链接 - 是您的发布版本,提供正确的库.
要尝试的事情:
Minidump - 非常容易使用(只需在msdn中查找)将为每个线程提供完整的崩溃转储.您只需将输出加载到visual studio中,就好像您在崩溃时进行调试一样.
您可以将WinDbg设置为您的事后调试程序.这将启动调试器并在崩溃发生时将其附加到进程.要安装WinDbg以进行事后调试,请使用/ I选项(注意它是大写的):
windbg /I
更多细节在这里.
至于原因,它很可能是其他答案所暗示的一个整体变量.
经过几个小时的调试,我终于找到问题的原因,这确实是由缓冲区溢出引起的,导致单字节差异:
char *end = static_cast(attr->data) + attr->dataSize;
这是一个fencepost错误(off-by-one错误)并修复了:
char *end = static_cast(attr->data) + attr->dataSize - 1;
奇怪的是,我在代码的各个部分周围调用了几次_CrtCheckMemory(),并且它们总是返回1.我能够通过放置"return false"来找到问题的根源.在测试案例中调用,然后最终通过反复试验确定故障的位置.
感谢大家的评论 - 我今天学到了很多关于windbg.exe的知识!:)
即使您已将exe作为发布版本构建,您仍然可以生成PDB(程序数据库)文件,这些文件将允许您堆叠跟踪,并执行有限数量的变量检查.在构建设置中,有一个创建PDB文件的选项.打开它并重新链接.然后尝试先从IDE运行,看看你是否遇到了崩溃.如果是这样,那就太好了 - 你们都准备好看待事物了.如果没有,那么当从命令行运行时,您可以执行以下两项操作之一:
运行EXE,然后在崩溃之前执行"附加到进程"(Visual Studio上的"工具"菜单).
崩溃后,选择启动调试器的选项.
当被要求指向PDB文件时,浏览以找到它们.如果将PDB放在与EXE或DLL相同的输出文件夹中,它们可能会被自动拾取.
PDB提供了一个到源的链接,其中包含足够的符号信息,可以查看堆栈跟踪,变量等.您可以正常检查这些值,但请注意,您可能会得到错误的读数,因为优化过程可能仅意味着事物出现在寄存器中,或者事情以不同于您期望的顺序发生.
注意:我在这里假设一个Windows/Visual Studio环境.