我的一个程序中有一个奇怪的竞争条件,它仅在发布模式和 OUTSIDE Visual Studio 环境中导致其崩溃。
如果我在 Visual Studio 中使用 F5(发布或调试)以发布模式启动此过程,它就可以工作。
如果我创建带有调试信息的发布副本,它不会崩溃。
我想知道如何调试这样的问题..为什么它没有在 Visual Studio 中崩溃?即使在启动它的发布版本时,Visual Studio 是否会减慢可执行文件的速度?
我的一个程序中有一个奇怪的竞争条件,它仅在发布模式和 OUTSIDE Visual Studio 环境中导致其崩溃。
如果我在 Visual Studio 中使用 F5(发布或调试)以发布模式启动此过程,它就可以工作。
如果我创建带有调试信息的发布副本,它不会崩溃。
我想知道如何调试这样的问题..为什么它没有在 Visual Studio 中崩溃?即使在启动它的发布版本时,Visual Studio 是否会减慢可执行文件的速度?
问题实际上是如何在不更改导致崩溃的运行时行为的情况下调试应用程序。答案是更好的验尸诊断
您可以改进您的异常处理代码,如果这是一个生产应用程序,您应该这样做。
使用安装自定义终止处理程序std::set_terminate
如果你想在本地调试这个问题,你可以在终止处理程序中运行一个无限循环,并将一些文本输出到控制台以通知你std::terminate
已被调用。然后附加调试器并检查调用堆栈。
在生产应用程序中,您可能希望将错误报告发送回家,理想情况下与允许您分析问题的小型内存转储一起发送。
Microsoft 有一个结构化的异常处理机制,允许您捕获硬件和软件异常。请参阅 MSDN。您可以使用 SEH 保护您的部分代码,并使用与 1) 中相同的方法来调试问题。SEH 提供了有关您在从生产应用程序发送错误报告时可以使用的异常的更多信息。
如果它真的是一个竞争条件,那么正确的时间是至关重要的,我猜,即使在发布模式下附加调试器确实会改变行为,从而改变时间
这回答了“有什么不同”,但可能不是为什么您的代码在发布模式下存在竞争条件的完整答案。
改变的一件事是运行时正在使用哪个堆,当您移到 VS 之外发布时。据我了解,即使在发布模式下,它也使用 VS 内部的调试堆。
由于根据定义必须锁定堆分配,因此使用调试堆(在将内存分配给客户端代码之前填充内存,并在释放内存时再次填充)将更频繁地阻塞竞争线程(导致更多的顺序执行),所以你可能会发现这是比赛发生的部分原因。
如果您_NO_DEBUG_HEAP=1
在调试环境中设置环境变量(配置->调试->环境变量...),那么您将在调试器中得到相同的结果。
不幸的是,这类事情很难调试。我发现有用的一件事是存储“我去过的地方”的值数组(数组越简单越好 - 所以整数值或小字符串或其他东西),而不是每次都打印一些东西. 如果您可以在调试器中停止或以某种方式检测到崩溃,则可以转储“跟踪”,并查看您如何到达现在的位置,以及涉及哪些线程。
Visual Studio 中的 F5 与单独运行程序之间的主要区别在于,当程序最初在调试器下运行时,Windows 在特殊的调试堆上运行程序。调试堆与普通堆不同,在某些情况下,这可能导致错误仅在普通堆上表现出来。
您可以运行该程序,然后附加调试器,然后它将使用普通堆,您的错误应该很高兴地重现。为了防止程序在附加调试器之前“走得太远”,您可以Sleep()
在入口点内插入对函数的调用。