如何在.NET运行时调试内部错误?

Mar*_*ell 67 .net c#

我正在尝试调试一些处理大文件的工作.代码本身有效,但.NET Runtime本身报告了偶发错误.对于上下文,这里的处理是1.5GB文件(仅加载到内存中一次)被处理并在循环中释放,故意试图重现这个不可预测的错误.

我的测试片段基本上是:

try {
    byte[] data =File.ReadAllBytes(path);
    for(int i = 0 ; i < 500 ; i++)
    {
        ProcessTheData(data); // deserialize and validate

        // force collection, for tidiness
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
    }
} catch(Exception ex) {
    Console.WriteLine(ex.Message);
    // some more logging; StackTrace, recursive InnerException, etc
}
Run Code Online (Sandbox Code Playgroud)

(带有一些时间和其他东西)

循环将完全成功地处理非确定性迭代次数- 没有任何问题; 那么这个过程会突然终止.异常处理程序未被命中.测试确实涉及大量的内存使用,但它在每次迭代过程中锯齿非常好(没有明显的内存泄漏,并且我有足够的空间 - 在锯齿的差点处有14GB未使用的主存储器) .该过程是64位.

Windows错误日志包含3个新条目,(通过退出代码80131506)建议执行引擎错误 - 一个讨厌的小动物.一个相关的答案,建议GC错误,用"修复"来禁用并发GC; 但是这个"修复"并不能解决这个问题.

澄清:这个低级错误没有触及CurrentDomain.UnhandledException事件.

澄清:GC.Collect只有监控锯齿状记忆,检查内存泄漏并保持可预测性; 删除它不会使问题消失:它只是使它在迭代之间保持更多内存,并使dmp文件更大; p

通过添加更多的控制台跟踪,我发现它在以下每个过程中出现故障:

  • 在反序列化期间(大量分配等)
  • GC期间(GC"方法"和GC"完成"之间,使用GC通知API)
  • 在验证期间(仅仅是foreach一些数据) - 奇怪的是在验证期间GC"完成"之后

所以有很多不同的场景.

我可以获得崩溃转储(dmp)文件; 我怎样才能进一步调查这个问题,看看当系统失败时系统正在做什么?

Chr*_*ens 22

如果你有内存转储,我建议使用WinDbg查看它们,假设你还没有这样做.

尝试运行注释!EEStack(混合本机和托管堆栈跟踪),并查看是否有任何可能在堆栈跟踪中跳出的内容.在我的测试程序中,我发现这一次是我的堆栈跟踪FEEE发生的地方(我故意破坏堆):

0:000> !EEStack
---------------------------------------------
Thread   0
Current frame: ntdll!NtWaitForSingleObject+0xa
Child-SP         RetAddr          Caller, Callee
00000089879bd3d0 000007fc586610ea KERNELBASE!WaitForSingleObjectEx+0x92, calling ntdll!NtWaitForSingleObject
00000089879bd400 000007fc5869811c KERNELBASE!RaiseException+0x68, calling ntdll!RtlRaiseException
[...]
00000089879bec80 000007fc49109cf6 clr!WKS::gc_heap::gc1+0x96, calling clr!WKS::gc_heap::mark_phase
00000089879becd0 000007fc49109c21 clr!WKS::gc_heap::garbage_collect+0x222, calling clr!WKS::gc_heap::gc1
00000089879bed10 000007fc491092f1 clr!WKS::GCHeap::RestartEE+0xa2, calling clr!Thread::ResumeRuntime
00000089879bed60 000007fc4910998d clr!WKS::GCHeap::GarbageCollectGeneration+0xdd, calling clr!WKS::gc_heap::garbage_collect
00000089879bedb0 000007fc4910df9c clr!WKS::GCHeap::Alloc+0x31b, calling clr!WKS::GCHeap::GarbageCollectGeneration
00000089879bee00 000007fc48ff82e1 clr!JIT_NewArr1+0x481

由于这可能与垃圾收集器的堆损坏有关,我会尝试该!VerifyHeap命令.至少你可以确保堆是完整的(并且你的问题出在其他地方)或者发现你的问题可能实际上是在GC或某些P/Invoke例程中破坏它.

如果您发现堆已损坏,我可能会尝试发现有多少堆已损坏,您可以通过它来执行此操作!HeapStat.但是,这可能只是表明整个堆在某一点上已经损坏了.

很难通过WinDbg建议任何其他方法来分析这个,因为我对你的代码正在做什么或它的结构没有真正的线索.

我想如果你发现它是堆的问题,因此意味着它可能是GC的怪异,我会看一下Windows事件跟踪中的CLR GC事件.


如果您获得的minidump没有削减它并且您正在使用Windows 7/2008R2或更高版本,那么您可以使用Global Flags(gflags.exe)在进程终止时附加调试器而没有例外,如果您是没有获得WER通知.

Silent Process Exit选项卡中,输入可执行文件的名称,而不是它的完整路径(即.TestProgram.exe).使用以下设置:

  • 选中启用静默进程退出监视
  • 检查启动监视器进程
  • 对于Monitor Process,请使用 {path to debugging tools}\cdb.exe -server tcp:port=5005 -g -G -p %e.

并应用设置.

当您的测试程序崩溃时,cdb将附加并等待您连接到它.启动WinDbg,键入Ctrl + R,然后使用连接字符串:tcp:port=5005,server=localhost.

您可以跳过使用远程调试而不是使用{path to debugging tools}\windbg.exe %e.但是,我建议使用远程的原因是因为WerFault.exe,我认为是读取注册表并启动监视器进程的内容,将在会话0中启动调试器.

您可以使会话0交互并连接到窗口站,但我不记得是怎么做的.这也很不方便,因为如果您需要访问已打开的任何现有窗口,则必须在会话之间来回切换.


Nah*_*hum 7

Tools->Debugging->General->Enable .Net Framework Debugging

+

Tools->IntelliTace-> IntelliTaceEbents And Call Information

+

Tools->IntelliTace-> Set StorIntelliTace Recordings in this directory

并选择一个目录

应该允许您步入INTO .net代码并跟踪每个函数调用.我在一个小样本项目上尝试了它,它的工作原理

在每个调试会话之后,它假定创建调试会话的记录.它是set目录,即使CLR死了,如果我没有弄错

这应该允许您在CLR崩溃之前进入extact调用.