在 Windows 中调试堆栈溢出?
Posted
技术标签:
【中文标题】在 Windows 中调试堆栈溢出?【英文标题】:debug stack overflow in windows? 【发布时间】:2009-04-02 13:50:11 【问题描述】:所以我正在尝试调试这个奇怪的问题,即进程结束而不调用一些析构函数...
在 VS (2005) 调试器中,我点击“Break all”,然后在神秘消失进程的线程的调用堆栈中查看,当我看到以下内容时:
smells like SO http://img6.imageshack.us/img6/7628/95434880.jpg
这绝对看起来像是一个正在制作中的 SO,这可以解释为什么这个过程会在没有先打包手提箱的情况下运行到它快乐的地方。
问题是,VS 调试器的调用堆栈只显示您在图像中看到的内容。
所以我的问题是:如何找到无限递归调用的开始位置?
我读到 somewhere 在 Linux 中您可以将回调附加到 SIGSEGV 处理程序并获取有关正在发生的事情的更多信息。
Windows上有类似的东西吗?
【问题讨论】:
【参考方案1】:若要控制 Windows 在访问冲突时的操作(SIGSEGV
-equivalent),请调用 SetErrorMode
(传递参数 0 以在出现错误时强制弹出,允许您使用调试器附加到它。)
但是,根据您已经获得的堆栈跟踪,在故障时附加调试器可能不会产生任何其他信息。您的堆栈已损坏,或者递归深度已超过 VS 可显示的最大帧数。在后一种情况下,您可能希望减少进程的默认堆栈大小(使用项目属性中的/F
开关或等效选项)以使问题很快就会显现出来,并确保 VS 将显示所有帧。或者,您可能希望在 std::basic_filebuf::flush() 中放置一个断点并遍历它直到销毁阶段(或在销毁阶段之前禁用它。)
【讨论】:
【参考方案2】:嗯,您知道问题出在哪条线索上——这可能很简单,从一开始就追踪它,看看它从哪里进入杂草。
另一种选择是使用 Debugging Tools for Windows 包中的一个调试器——它们可能比 VS 调试器显示更多(也许),即使它们通常更复杂且难以使用(实际上可能是因为那个)。
【讨论】:
它们将显示整个堆栈。类似的东西:k5000 应该不错。【参考方案3】:乍一看确实像无限递归,您可以尝试在终止进程的行之前放置一个断点。它可以到达那里吗?如果是这样,您有两种相当简单的方法。
你要么向前一步,看看哪些析构函数被调用,什么时候被赶上。或者你可以在每个相关的对象析构函数中放置一个 printf/OutputDebugString(只有全局对象才需要这个)。如果消息是析构函数做的第一件事,那么你看到的最后一条消息来自析构函数,它把事情挂了。
另一方面,如果它没有到达我最初提到的那个断点,那么可以做类似的事情,但它会更烦人,因为程序仍在“做事”。
【讨论】:
"我的问题是:如何找到无限递归调用的开始位置?" -> 我不知道哪个调用导致进程结束 - 堆栈视图没有显示我的任何项目代码 - 请阅读我对@Dan Breslau 答案的评论。 好吧,您没有提到该过程意外结束。你让它听起来像是进程退出(就像返回到 main)并陷入了析构函数。 我想我的下一个问题是,程序的本质是什么(功能相当线性,长期运行的服务)?挂起是在生命周期的开始还是结束时发生?还给出了调用堆栈中的函数,您是否使用自定义日志记录机制? 如果是这样,在禁用日志记录的情况下会发生这种情况吗?您可以通过某种方式调用自身的日志记录进行无限递归。【参考方案4】:我不排除在 Windows 中有这样的处理程序,但我从未听说过。
我认为您显示的回溯可能是虚假的。如果您在某种损坏已经发生后闯入该过程,那么回溯不一定有效。但是,如果幸运的话,堆栈跟踪的底部仍然有一些关于正在发生的事情的线索。
尝试将Sleep()
调用放入源中可能涉及递归的选定函数中。这应该让您有更好的机会在堆栈完全溢出之前进入进程。
【讨论】:
查看图像中的滚动条 - 它是堆栈跟踪的底部 :(【参考方案5】:我同意丹·布雷斯劳的观点。你的堆栈是假的。不过,可能仅仅是因为您没有正确的符号。 如果一个程序在没有启动 WER 处理的情况下就消失了,这通常是内存不足的情况。你去调查过这种可能性吗?
【讨论】:
以上是关于在 Windows 中调试堆栈溢出?的主要内容,如果未能解决你的问题,请参考以下文章