【问题标题】:C++: __try...__except; hides crash in release mode?C++: __try...__except;在发布模式下隐藏崩溃?
【发布时间】:2012-05-21 21:20:02
【问题描述】:

我有一个在嵌入式 Windows XP 机器上运行的 DX9 应用程序。当让它在一夜之间自动进行浸泡测试时,它会在大约六到八小时后崩溃。在我们的开发中。机器(Win 7)我们似乎无法重现此问题。我也相当确定这不是内存泄漏。

  • 如果我们在嵌入式机器上的 Debug 中运行相同的应用程序,它不会崩溃。
  • 如果我们在嵌入式机器上的主循环更新周围放置 __try/__except,它不会崩溃。

我知道在 Debug 中,本地堆栈周围有一些额外的字节填充,这可能会“隐藏”超出范围的本地数组访问,或者某种未初始化的变量正在潜行。

所以我有两个问题:

  1. __try/__except 的行为是否类似于调试,即使在发行版中运行?
  2. 如果我们在 Release 模式下发生崩溃,但在 Debug 模式下没有崩溃,我应该扫描哪些代码?

【问题讨论】:

  • “如果我们在发布模式下发生崩溃,但在调试模式下没有崩溃,我应该扫描哪些代码?” - 那是@987654323 @ 或其他有副作用的调试代码
  • 首先,没有try/except,有try/catch。其次,您在调试中没有观察到崩溃的原因可能有很多,其中之一是您可能需要运行更长时间。
  • 不,我正在使用 __try __except... 它们很相似,但存在潜在差异。但是我不太确定细节。
  • 什么样的程序员不确定自己使用的语言特性的细节? (一个坏的)
  • @shoosh:我们在这里讨论的是 C++。大多数程序员并不了解每种语言功能的每一个细节。好人已经学会了如何安全地使用这些功能,这仍然不需要记住每个角落的情况。

标签: c++ crash try-catch buildconfiguration


【解决方案1】:

如果您使用的是__try{ } __except(),则不应该这样做。
那些和 C++ 代码不能很好地混合。 (例如,您不能在用这些对象包装的函数的堆栈上包含 C++ 对象。如果您使用 catch(...)(带省略号),则应该使用 C++ try {} catch() {},它与 __except() 的作用基本相同

try.. catch__try .. __except 在调试和发布中的行为相同。

如果您怀疑您的问题是意外异常,您应该阅读以下所有内容:

SetUnhandledExceptionFilter()
_set_se_translator()

_CrtSetReportMode()
_RTC_SetErrorFunc()
_set_abort_behavior()
_set_error_mode()
_set_new_handler()
_set_new_mode()
_set_purecall_handler()
set_terminate()
set_unexpected()
_set_invalid_parameter_handler()
_controlfp()

使用前两个中的一个可能会让您很快查明您的问题。如果您希望对过程中可能出现的所有错误情况进行绝对控制,其余的都在那里。

具体来说,使用SetUnhandledExceptionFilter(),您可以设置一个函数过滤器,该过滤器记录导致异常的代码的地址。然后,您可以使用调试器来确定该代码。使用 DbgHelp 库和提供给过滤器函数的信息,您可以编写一些代码,打印出崩溃的完整堆栈跟踪,包括符号和行号。

确保您将构建配置设置为也为发布构建发出调试符号。他们只能帮助并且不会做任何事情来减慢您的应用程序(但可能会使其变得更大)

【讨论】:

  • IIRC catch(...) 在某些最新版本的 VC++ 中默认禁用捕获 SEH 异常(参见例如 cmets here)。
  • @Matteo:这取决于/EHs/EHa。但是使用_set_se_translator() 也应该有所帮助。
【解决方案2】:

如果我们在嵌入式机器上的主循环更新周围放置__try/__except,它不会崩溃。

那就这样做吧。

推荐的方法是围绕整个程序(以及每个工作线程的入口点)使用单个 __try 块,它可以让您在退出之前写出故障转储并进行错误报告。使用 SEH 无法进行太多恢复,因为异常只是没有携带足够的信息来有效地区分不同的故障。不过,存储整个程序状态并将其拉入调试器非常有用。

注意:某些视频驱动程序会导致它们也捕获的 SEH 异常,也许某些逻辑期望安装多个 SEH 范围,这是您的 __try 块提供的。

【讨论】:

  • 您应该注意不要阻止保护页面等使用的 SEH 异常(参见例如here)。我认为SetUnhandledExceptionFilter 解决方案不会带来这些不便(但我不确定,我不喜欢使用 SEH,因为有很多我可能会忘记的细节)。
  • @Matteo:操作系统异常处理代码在开始检查用户异常过滤器之前应该捕获这些异常处理代码。只有在实际堆栈溢出的情况下(操作系统试图扩大堆栈,但失败),您才需要担心保护页面会导致访问冲突。
  • 你确定吗?据我从 Raymond Chen 的文章中可以看出,预计保护页面异常会遍历整个堆栈,并且操作系统代码会在声明它们未被捕获之前在堆栈顶部捕获它们。因此,未处理的异常过滤器应该没问题,而顶级(但仍然不是堆栈的真正顶部)__try 可能会出现问题。
  • @Matteo:阅读 cmets...操作系统会在检查任何用户模式异常过滤器之前拦截堆栈增长情况。困难在于是否触发了其他线程的保护页面。但是,如果发生这种情况,发出故障转储并退出是最好的方法,因为它使您有机会将状态带回开发人员计算机并查看发生了什么(可能会或可能不会揭示原因)。请注意,我不建议吞下异常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-17
  • 1970-01-01
  • 2018-12-31
  • 1970-01-01
  • 1970-01-01
  • 2016-02-05
  • 2020-03-01
相关资源
最近更新 更多