如何调试仅在应用程序关闭时发生的崩溃? (德尔福)

Posted

技术标签:

【中文标题】如何调试仅在应用程序关闭时发生的崩溃? (德尔福)【英文标题】:How to debug a crash that only occurs on application shutdown? (Delphi) 【发布时间】:2011-07-08 01:22:20 【问题描述】:

因此,经过最近的一些更改,我们发现我们最古老的应用程序之一有时会在关机时崩溃。这表现为“运行时错误 216”消息或来自 Windows 错误报告的应用程序已停止工作的消息。该应用程序已经在每次发送OutputDebugString-messages 并且 AFAICT 我们自己的所有代码都会正确执行到完成。所有的析构函数都像所有终结部分和类析构函数一样被调用,它们都没有引发任何异常。

此外,madExcept 和 FastMM4 的完全调试模式似乎都没有什么可抱怨的(尽管这可能是一个错误的结论,因为崩溃可能在这些组件自己的最终代码运行之前发生)。

那么,你会怎么做?你会从哪里开始?


这个问题应该更多地是关于这类问题的一般方法,而不是关于我目前面临的具体实例,所以我故意省略了细节。随意询问您是否认为它们可能与调试方法的选择有关,我稍后会添加它们。

【问题讨论】:

存在一个 VCL 错误,在关闭时表现为 AV。它与Owner 破坏表单有关。我通过在Application.Run 之后显式关闭 .dpr 文件中的表单来解决此问题。如果您让Application 为您关闭,那么过时的引用可能会出现问题。 @David:听起来很有趣。你有那个 QC 号码吗?但是,除了主表单之外,我将在代码中创建所有表单,并将相应的父表单作为所有者。您所说的问题是否也适用于这种情况? 如果您依靠破坏应用程序来降低其他所有内容,那么我正在谈论的错误可能适用。尝试在 .dpr 文件中添加 MainForm.Free。我去看看 QC 号码。 @大卫:谢谢!我去看看。 【参考方案1】:

运行时错误 216 表示您有 Av(访问冲突)并且 SysUtils 已经停止将这些错误转换为异常。

第一次尝试:使用调试 DCU 构建并查看引发错误的单位系统,在那里设置断点。希望您可以在调试器中捕获它并从那里开始工作。

您可能有内存错误(悬空指针、空引用等在已完成的单元中使用 s 字符串常量),最好的技巧是在 sysutils 完成后检查完成情况。您可以通过构建 WITH 调试 dcu、在 sysutils 中将断点设置为最终确定并开始逐步执​​行代码直到发生错误来做到这一点。

【讨论】:

【参考方案2】:

您是否使用运行时包?我以前见过类似的问题。如果您要跨包边界共享全局变量或接口,则必须确保在卸载该包之前清除对属于某个包的类的所有引用;否则他们会尝试对不再有效的内存进行虚拟调用。

【讨论】:

哦,是的,不仅是运行时包,还有依赖于这些包的 COM-DLL(即插件)(主机 EXE 也是如此)。不要问。我今天不会再这样做了,但它已经完美运行了 10 多年了……不过,我会仔细检查对某些全球单例的任何引用。这听起来确实很有希望...... “完美运行 10 多年了” 发生了什么变化?这可能就是我解决这个问题的方式。 触摸! ;) 对于一个公认的答案来说,这几乎已经足够好了。 :-P【参考方案3】:

Runtime error 216 是内存问题 (Access violation),您似乎引用了某个当时不存在的对象。

Emarcadero 写道:

使用 SysUtils 类的应用程序将大多数运行时错误映射到异常,这允许您的应用程序在不终止的情况下解决错误

您是否尝试在 Sysutils 的终结部分设置断点?

我会尝试使用分配/内存分析器,它不确定您是否会找到错误代码行,但这可能会向您显示发生内存问题的代码的某些部分。

【讨论】:

【参考方案4】:

您可能遇到了指针问题。某些事件或方法试图在不再存在的对象上运行。

【讨论】:

是的,这就是“运行时错误 216”或多或少的含义,但问题是您将如何追踪该问题? 如果您可以可靠地重现它,那么您可以使用调试器(您需要调试 DCU)来确定它发生的位置。不幸的是,我认为你很可能在它引发的时候有一个非常绝望的堆栈跟踪。 @David:这个问题的“可靠再现”仍然是一个问题:我会说它发生在大约 60% 的时间。而且我还无法确定工作会话和非工作会话之间的区别可能是什么。我实际上已经认为我已经解决了几次问题,只是在几次会议后再次出现......【参考方案5】:

“运行时错误 216”来自 Windows 本身,而不是 Delphi 异常处理程序。我发现它是由在单元的初始化和终结部分中运行的代码引起的,这些代码在 Delphi 异常处理程序启动之前执行。特别是。在 Delphi 应用程序终止后运行的终结代码需要卸载的 COM 对象将导致此错误和类似错误。所以检查那些东西。

MNG

【讨论】:

不完全:见单位制:函数 MapToRunError 将 NT 错误代码 STATUS_ACCESS_VIOLATION 映射到 ErrCode := 216。 感谢您的评论——无论如何,这发生在默认的 Delphi 应用程序异常处理程序初始化之前或之后。单元在 Delphi 应用程序启动之前基于 uses 子句加载,在应用程序终止后卸载 - 初始化和终结部分在单元加载和卸载时运行,因此在其中运行的代码不会由 Delphi 应用程序异常处理程序处理。这就是为什么你会得到一个神秘的“运行时错误 216”或 217 并且没有异常消息“Eaccess 违规..@”等。 这不是真的; 216 来自 Delphi RTL,在异常处理机制被取消挂钩之后。这是告诉有 AV 的德尔福方式。 216 不是窗体。 请参见上面的解释。【参考方案6】:

就像其他人所说:216 表示 SysUtils 关闭后的 AV。通常,在 SysUtils 之后关闭(并有机会提高 AV)的唯一东西是系统单元。具体来说:内存管理器。

因此,关闭时出现运行时错误 216 通常意味着您的应用程序中存在内存损坏错误。

这很容易解决 - 只需在内存管理器中启用完全调试模式或使用调试内存管理器。然而,有时它可能很难找到。但是你可以先从MM的debug模式开始。

见this article。

【讨论】:

以上是关于如何调试仅在应用程序关闭时发生的崩溃? (德尔福)的主要内容,如果未能解决你的问题,请参考以下文章

当系统清除内存并关闭 UIViewController 时,iOS 崩溃

本地化应用程序仅在非基本语言上崩溃,并且仅在未使用Xcode运行时崩溃

分段错误仅在发布配置下发生

如何调试从后台返回时发生的崩溃

如何调试 iPhone 应用程序崩溃日志。仅在 App Store 购买的版本中崩溃,而不是在开发中

程序仅在调试器外的发布模式下崩溃