代码之家  ›  专栏  ›  技术社区  ›  BlueRaja - Danny Pflughoeft

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

  •  25
  • BlueRaja - Danny Pflughoeft  · 技术社区  · 13 年前

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

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

    (0xc0000005是的错误代码 Access Violation )

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

    overallLength += distanceTravelled;
    

    其中两个值都属于类型 double


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

    所以 我该如何调试它?


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

    7 回复  |  直到 6 年前
        1
  •  23
  •   Jon Skeet    13 年前

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

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

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

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

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

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

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

        2
  •  10
  •   Community Mohan Dere    9 年前

    tl;博士 确保编译到.Net 4.5


    这听起来可疑地与发现的错误相同 here 。来自 MSDN page 以下为:

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

    修复方法是编译到.Net 4.5。如果由于某种原因你不能这样做,你也可以 disable concurrent garbage collection 通过禁用 gcConcurrent app.config 文件:

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

    或者只是编译到 x86

        4
  •  6
  •   psulek    13 年前

    下载 Debug Diagnostic Tool v1.2

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

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

        5
  •  4
  •   Akash Kava    13 年前

    我的应用程序不会调用任何本机代码,也不会使用任何不安全的块,或者 甚至任何不符合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本身中。

        6
  •  1
  •   IvoTops    13 年前
        7
  •  0
  •   Lex Li    13 年前

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

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

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

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

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