6

背景

  • 我有一个带有Poof-Crash [ 1 ] 的应用程序。我相当肯定这是由于堆栈爆炸造成的。
  • 该应用程序是多线程的。
  • 我正在用“ Enable C++ Exceptions: Yes With SEH Exceptions (/EHa)”编译。
  • 我写了一个 SE Translator 函数并_set_se_translator()用它调用。
  • 我已经为 和 setupset_terminate()set_unexpected().
  • 要获得 Stack Overflow,我必须在重负载下以发布模式运行几天。在调试器下运行不是一种选择,因为应用程序的执行速度不够快,无法达到查看问题所需的运行时间。
  • 我可以通过在执行其中一个函数时添加无限递归来模拟问题,从而测试EXCEPTION_STACK_OVERFLOW异常的捕获。
  • 我将 WinDBG 设置为故障转储程序,并获得有关所有其他崩溃问题的好信息,但不是这个。故障转储将仅包含一个线程,即“Sleep()”。所有其他线程都已退出。

问题

我尝试过的所有事情都没有导致出现EXCEPTION_STACK_OVERFLOW异常。

有谁知道如何保证在发布模式下运行时有机会遇到这个异常?

定义

  1. Poof-Crash:应用程序因“噗”而崩溃并消失得无影无踪。

(考虑到这个网站的名称,我有点惊讶这个问题还没有出现在这里!)

笔记

  1. 简要发布了一个关于调整堆栈大小以可能更快地强制问题并允许使用调试器捕获它的答案。这是一个聪明的想法,但不幸的是,我不相信它会有所帮助。该问题可能是由导致无限递归的极端情况引起的。缩短堆栈不会很快暴露问题,并且可能会导致有效深度代码中的无关崩溃。好主意,感谢您发布它,即使您确实删除了它。
4

5 回答 5

5

windows xp 之前的所有东西都不会(或者更难)通常能够捕获堆栈溢出。随着 xp 的出现,您可以设置向量异常处理程序,该处理程序在任何基于堆栈(结构化异常)处理程序之前有机会发生堆栈溢出(这就是原因 - 结构化异常处理程序是基于堆栈的)。

但是,即使您能够捕获这样的异常,您也确实无能为力。

他的博客中,cbrumme(对不起,没有他/她的真名)讨论了一个与保护页面(产生堆栈溢出的页面)相邻的堆栈页面,该页面可能被用于回退。如果您可以压缩退出代码以仅使用一个堆栈页面 - 您可以尽可能多地释放您的逻辑。否则,应用程序在遇到堆栈溢出时几乎死机。捕获它后,唯一合理的做法是编写转储文件以供以后调试。

希望能帮助到你。

于 2009-01-13T12:57:55.553 回答
4

我不相信您在将其诊断为堆栈溢出方面是正确的。

但无论如何,你得到一个便便的事实!,加上您在 WinDbg 中看到的内容

故障转储将仅包含一个线程,即“Sleep()”。所有其他线程都已退出。

向我建议有人调用了 C RTL exit() 函数,或者可能直接调用了 Windows API TerminateProcess()。这可能与您的中断处理程序有关。也许异常处理逻辑中的某些内容具有重新进入检查,并且在重新进入时任意决定 exit() 。

我的建议是修补您的可执行文件,以便在退出()的入口点放置一个 INT 3 调试,如果它是静态链接的,或者如果它是动态链接的,则修补导入并修补 kernel32::TerminateProcess 的任何导入到改为抛出 DebugBreak() 。

当然,exit() 和/或 TerminateProcess() 也可以在正常关闭时调用,因此您必须过滤掉误报,但如果您可以获得调用堆栈以应对即将发生的情况去证明,你应该有你需要的。

编辑添加:只需编写自己的 exit() 版本并将其链接而不是 CRTL 版本就可以了。

于 2009-01-14T00:03:34.617 回答
1

我记得以前工作场所的代码听起来相似,对堆栈指针进行显式边界检查并手动抛出异常。

不过,自从我接触 C++ 以来已经有一段时间了,即使我接触了它,我也不知道自己在做什么,所以请注意实施者关于上述建议的可移植性/可靠性。

于 2009-01-12T20:27:10.203 回答
1

您是否考虑过 Windows 调试工具中的ADPlus ?

ADPlus 将 CDB 调试器附加到处于“崩溃”模式的进程,并将为进程生成的大多数异常生成崩溃转储。基本上,您运行“ADPlus -crash -p yourPIDhere”,它会执行侵入式附加并开始记录。

鉴于您上面关于在调试器下运行的评论,我只想补充一点,CDB 在体面的(双核,2GB RAM)机器上在 -crash 模式下增加了几乎为零的开销,所以不要让它阻碍你尝试它.

于 2009-01-12T23:30:05.683 回答
0

您可以在不禁用优化的情况下生成调试符号。事实上,无论如何你都应该这样做。它只会使调试变得更加困难。

并且文档_set_se_translator说每个线程都有自己的 SE 翻译器。您是否为每个线程设置一个?

set_unexpected至少根据 VS 2005 文档,可能是无操作的。每个线程也有自己的terminate处理程序,因此您也应该为每个线程安装它。

我也强烈建议不要使用 SE 翻译。它需要您不应该忽略的硬件异常(即,您应该真正记录错误并终止)并将它们变成您可以忽略的东西(C++ 异常)。如果您想捕获此类错误,请使用__try/__except处理程序。

于 2009-01-12T20:34:22.480 回答