5

即使听起来很奇怪,我相信每个人在处理具有大量自定义组件的大型应用程序时都会遇到这种问题。在某处生成了 AV,但应用程序继续执行,稍后会引发错误。我不是在谈论多线程应用程序。只是关于通用单线程应用程序。

我正在努力寻找一个随机引发的错误,甚至我正在使用 MadExcept/AQT、调试 dcus 和断点,但我无法找到引发它的确切步骤,以及它从哪里引发。错误正在传播,它有时会在 TWinControl 破坏(Delphi 标准 TPageControl)上引发,有时在打开数据集时(之前使用相同的 SQL 打开和关闭多次)。所以堆栈被破坏了,在这种情况下我不能使用它。

我知道这个问题过于本地化,但我想问你还有哪些其他替代方法可以跟踪此类错误。

4

5 回答 5

7

我将从使用其他一些内存管理器开始,看看您是否可以强制应用程序的使用模式更快地中断。

FastMM可以使用 FullDebugMode 运行(确保将相应的 DLL 与您的应用程序 EXE 一起放置)。它可以做一些重要的事情:

  • 用模式填充记忆(如Arioch 的建议)
  • 检查块被释放后的修改
  • 在应用程序终止时记录内存泄漏
  • 回调某些事件,以便您可以记录内容

过去我写过 2 篇关于 FastMM 的博客文章,我知道我应该写更多关于它和其他内存管理器的内容。

编辑: SO用户Eric Grange关于其他内存管理器内存管理器调查的好文章。

出于文档目的,其他一些内存管理器(其中大多数不适用于您的案例,但我还没有找到包含所有这些的帖子):

在多线程环境中,这些可能很好:

在 EXE/DLL 设置中,您可能想尝试FastShareMem

在他的ScaleMM 存储库中,André Mussche 确实测试了很多其他内存管理器

注意:+1;好问题,我应该找点时间写一篇关于 Delphi 内存管理器的后续博客文章(:

于 2013-06-18T13:12:37.687 回答
3

记录到文件可以改变结果,所以我推荐使用 GExperts 调试器,它可以将消息实时写入输出窗口。编写一个包装函数,以便您可以根据需要转储到文件,控制记录的内容(日志级别过滤器)等。

将日志记录放在所有单元的初始化/完成中。我发现在最终确定过程中经常发生“出乎意料”的错误。例如:您关闭了一个数据库连接,但该对象已被其他东西释放。哦!

还将日志记录在任何组件的事件处理程序中,以及这些组件的析构函数中。这将显示您是否在组件被销毁后触发事件。

作为最后的手段,您可能必须“删除”程序,以“块”的形式删除您编写的所有代码。将代码保存在某处,删除 form1 上的事件处理程序,编译和测试。如果错误消失,放回事件处理程序并删除其中一半,重新测试。继续,直到你充分缩小范围,并且应该通过消除过程来确定罪魁祸首。

于 2013-06-18T13:02:39.037 回答
2

我相信这比德尔福问题更普遍。但一些有用的技术包括:

  • 过多地记录到文件中,以便您以后可以研究它 - 包含所有值
  • 到处都是断言。您可以禁用它们,这样它们在发布时不会增加成本
  • 禁用应用程序的某些部分,直到错误停止出现,或者
  • 禁用除一些基本 shell 之外的所有内容,然后逐个启用组件,直到 AV 返回。
  • 使用 跟踪内存泄漏ReportMemoryLeaksOnShutdown := true

当您找到似乎触发错误的组件时,进入它并重复该过程,禁用其中的所有内容并逐步启用部分。

于 2013-06-18T11:43:09.387 回答
2

有两件事成为我最好的朋友,尤其是在开发非可视化的东西时,而不仅仅是在 Delphi 中:

  • 单元测试和日志记录

我使用 (DUnit) 进行单元测试,但也用于执行系统和压力测试。我使用 AQT 来评估我的单元测试和内存泄漏检测的覆盖率。这个想法是我不需要预先为每个方法编写测试,而是尝试编写一个尽可能多的测试。当出现特定问题时,我会编写一个测试来重现该问题,然后我知道我的修复何时有效。特别是对于大型应用程序的另一个优势是,我可以在进行更改后运行测试并验证一切是否仍按预期工作。

我使用的另一个工具是 SmartInspect,因为它记录进入和离开方法等,并且有一个向导来检测现有代码(添加进入离开日志记录)和一个控制台来过滤日志。

于 2013-06-19T16:03:23.877 回答
2

奇怪的是,没有人提到调试名为 SafeMM 的内存管理器。它可以准确地捕获 AV 发生的位置。

于 2013-06-20T08:42:40.307 回答