我在 JIT/CLR 中发现了一个错误 - 现在如何调试或重现它?



我有一个计算成本很高的多线程C#应用程序,运行30-90分钟后似乎一直会崩溃。它给出的错误是

运行时遇到致命错误。错误的地址位于线程0xbcc上的0xec37ebae。错误代码为0xc0000005。此错误可能是CLR或用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括COM互操作或PInvoke的用户封送处理错误,这些错误可能会损坏堆栈。

(0xc0000005是访问违规的错误代码)

我的应用程序不会调用任何本机代码,也不会使用任何不安全的块,甚至不会使用任何与CLS不兼容的类型(如uint)。事实上,调试器所说的导致崩溃的代码行是

overallLength += distanceTravelled;

其中两个值均为double类型


考虑到这一切,我认为崩溃一定是由于编译器、CLR或JIT中的错误造成的。我想弄清楚是什么原因造成的,或者至少写一个较小的复制品发送到微软,但我甚至不知道从哪里开始。我从未查看过CIL二进制文件、编译的JIT输出或本机堆栈(崩溃时没有托管堆栈),所以我不知道如何查看。我甚至不知道如何查看崩溃时所有变量的状态(不幸的是,VS在托管异常后不会像它那样告诉我,将它们输出到控制台/a文件会使应用程序慢1000倍,这显然不是一个选项)

那么,我该如何调试它呢


[Edit]在VS 2010 SP1下编译,运行最新版本的.Net 4.0客户端配置文件。显然是"。Net 4.0C/.Net 4.0E、.Net CLR 1.1.4322">

我想弄清楚它的原因,或者至少写一个更小的复制品发送到微软,但我甚至不知道从哪里开始。

"较小的复制"在这里听起来绝对是个好主意。。。即使"更小"并不意味着"繁殖更快"。

在开始之前,请尝试在另一台机器上重现错误。如果你不能在另一台机器上复制它,那就需要进行一系列完全不同的测试——硬件、安装等。

此外,请检查您是否使用了所有内容的最新版本。花几天时间调试它(恐怕很可能),然后得到一个"是的,我们知道这件事——这是.NET 4中的一个错误,在.NET 4.5中修复了"的响应,这会很烦人。如果你能在各种框架版本上复制它,那就更好了:)

接下来,删除程序中的所有内容:

  • 它有用户界面吗?如果可能的话,把它取下来
  • 它使用数据库吗?看看是否可以删除所有数据库访问:绝对是以后不使用的任何输出,最好也是输入。如果你能在应用程序中对输入进行硬编码,那将是理想的选择——但如果不能,文件的复制比数据库访问更简单
  • 它对数据敏感吗?同样,在不太了解该应用程序的情况下,很难知道这是否有用,但假设它处理了大量数据,你能使用二进制搜索找到相对少量的数据吗
  • 是多线程的吗?如果你能删除所有的线程,显然这可能需要更长的时间来重现问题——但这种情况还会发生吗
  • 尝试删除一些业务逻辑:如果你的应用程序被适当地组件化了,你可能会通过首先创建一个存根实现,然后简单地删除调用来伪造整个重要的组件

所有这些都将逐渐缩小应用程序的大小,直到它变得更易于管理。在每一步中,你都需要再次运行该应用程序,直到它崩溃或你确信它不会崩溃。如果你有很多可用的机器,那应该会有所帮助。。。

tl;dr确保编译到.Net 4.5


这听起来像是在这里发现的相同错误。来自MSDN页面:

垃圾回收器在释放和压缩内存时可能会遇到此错误。如果启用了并发垃圾回收,并且前台垃圾回收和后台垃圾回收发生了某种组合,则可能会发生此错误。当这种情况发生时,您将一次又一次地看到相同的调用堆栈。在堆上,你会看到一个空闲对象,在它结束之前,你会发现另一个空闲的对象正在破坏堆。

修复程序是编译到.Net 4.5。如果由于某些原因无法执行此操作,还可以通过禁用app.config文件中的gcConcurrent来禁用并发垃圾收集:

<configuration>
<runtime>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>

或者只编译到x86

WinDbg是您的朋友:

  • http://blogs.msdn.com/b/tess/archive/2006/02/09/net-crash-managed-heap-corruption-calling-unmanaged-code.aspx

  • http://www.codeproject.com/Articles/23589/Get-Started-Debugging-Memory-Related-Issues-in-Net

  • http://www.codeproject.com/Articles/22245/Quick-start-to-using-WinDbg

下载调试诊断工具v1.2

  1. 运行程序
  2. 添加规则"崩溃">
  3. 选择"特定流程">
  4. 高级配置页面上,如果您知道在哪个异常上失败,请设置您的异常,或者保持此页面不变
  5. 设置用户转储位置

现在等待进程崩溃,日志文件由DebugDiag创建。现在激活选项卡"高级分析",在顶部列表中选择"崩溃/挂起分析器",在下部列表中选择转储文件,然后点击"启动分析"。这将为您生成html报告。希望你能在报告中找到有用的信息。如果你在分析方面有问题,请将html报告上传到某个地方,并将url放在这里,这样我们就可以专注于它。

我的应用程序不调用任何本地代码,也不使用任何不安全的块,或者即使是任何不符合CLS的类型,如uint

你可能会这么想,但线程、通过信号量同步、互斥锁及其任何句柄都是本机的。net是操作系统之上的一层,.net本身不支持多线程应用程序的纯clr代码,这是因为操作系统已经这样做了。

这很可能是线程同步错误。可能有多个线程正在尝试访问clr边界之外的共享资源,如文件etc。

您可能认为您没有访问com等,但当您调用某些API(如get desktop folder path等)时,它是通过shell com API调用的。

你有以下两个选项,

  1. 发布您的代码,以便我们可以查看瓶颈
  2. 使用.net并行线程框架重新设计您的应用程序,该框架包括各种需要CPU密集型操作的算法

随着集合的增长,程序很可能在一段时间后失败,并且在其他线程干扰之前,操作无法执行。例如,生产者-消费者问题,你不会注意到任何问题,直到生产者变得更慢或在消费者介入之前无法完成其操作。

clr中的错误很少见,因为clr非常稳定。但写得不好的代码可能会导致clr中出现错误。Clr不能也永远不会检测到错误是在代码中还是在Clr本身中。

  • 你有没有为你的机器运行内存测试,因为有一次我出现了类似的症状,我的一个dimm出现了故障(Win7中包含了一个非常好的内存测试;http://www.tomstricks.com/how-to-test-your-ram-or-memory-with-windows-memory-diagnostic-tool-in-windows-7/)

  • 如果您的CPU在这段时间后变得过热,也可能是加热/节流问题。尽管那会发生得更快。

  • 应该有一个可以分析的转储文件。如果你从来没有这样做过,找一个这样做的人,或者把它发给微软

我建议您通过http://support.microsoft.com立即,因为支持人员可以向您展示如何收集必要的信息。

一般来说,就像@paulsm4和@psulek所说的那样,您可以使用WinDbg或Debug-Diag来捕获进程的崩溃转储,并且在其中嵌入所有必要的信息。然而,如果这是你第一次使用这些工具,你可能会感到困惑。Microsoft支持团队可以为您提供循序渐进的指导,他们甚至可以与您建立实时会议会话来捕获数据,因为程序经常崩溃。

一旦你熟悉了这些工具,将来你就可以更容易地进行类似的故障排除,

http://blogs.msdn.com/b/lexli/archive/2009/08/23/when-the-application-program-crashes-on-windows.aspx

顺便说一句,现在说"我发现了一个bug"还为时过早。尽管您在程序中显然找不到对本机代码的依赖,但它可能仍然对本机编码有依赖。在对这个问题进行进一步调试之前,我们不应该得出结论。

最新更新