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

时间:2013-01-09 15:28:16

标签: c# .net

我正在尝试调试一些处理大文件的工作。代码本身工作,但.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
}

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

对于非确定性迭代次数,循环将处理正常完全成功 - 没有任何问题;那么这个过程会突然终止。异常处理程序未被命中。测试确实涉及大量的内存使用,但它在每次迭代过程中看得很清楚(没有明显的内存泄漏,我有足够的空间 - 在最差时有14GB未使用的主存储器在锯齿中指出。这个过程是64位的。

Windows错误日志包含3个新条目,其中(通过退出代码80131506)提示执行引擎错误 - 一个讨厌的小动物。 related answer,建议GC错误,使用“修复”来禁用并发GC;但是这个“修复”并不能解决这个问题。

澄清:此低级别错误未触及CurrentDomain.UnhandledException事件。

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

通过添加更多控制台跟踪,我发现它在以下各项中都出错:

    反序列化期间
  • (大量分配等)
  • 在GC期间(GC“方法”与GC“完成”之间,使用GC通知API)
  • 验证期间
  • (仅foreach部分数据) - 在验证过程中 GC“完成”之后好奇地

有很多不同的场景。

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

5 个答案:

答案 0 :(得分: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 events


如果您获得的小型驱动器没有切割它您使用的是Windows 7 / 2008R2或更高版本,则可以使用Global Flags(gflags.exe)连接调试器如果您没有收到WER通知,则流程会毫无例外地终止。

Silent Process Exit标签中,输入可执行文件的名称,它的完整路径(即TestProgram.exe)。使用以下设置:

  • 选中启用静默流程退出监控
  • 检查启动监控进程
  • 对于监控流程,请使用{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互动并连接到窗口站,但我不记得是怎么做的。这也很不方便,因为如果您需要访问已打开的任何现有窗口,则必须在会话之间来回切换。

答案 1 :(得分:7)

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

+

Tools->IntelliTace-> IntelliTaceEbents And Call Information

+

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

并选择一个目录

应该允许您执行INTO .net代码并跟踪每个函数调用。 我在一个小样本项目上尝试了它并且它可以工作

在每次调试会话之后,它会假设创建调试会话的记录。它是设置目录 即使CLR死了,如果我没有弄错

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

答案 2 :(得分:3)

尝试编写通用异常处理程序,看看是否有未处理的异常导致您的应用程序被删除。

    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

static void MyExceptionHandler(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);

答案 3 :(得分:3)

我经常用Valgrind和gdb来调查与内存相关的问题。

如果你在Windows上运行你的东西,有很多很好的选择,比如这里建议的callgrind的verysleepy:
Is there a good Valgrind substitute for Windows?

如果你真的想调试.NET运行时的内部错误,你就会遇到类库和虚拟机都没有源的问题。

由于你无法调试你没有的东西,我建议(除了用ILSpy反编译.NET框架库,并将它们添加到你的项目中,这仍然不包括vm)你可以使用单声道运行时 在那里你既有类库的源代码,也有VM的源代码 也许你的程序可以用单声道工作,然后你的问题就会解决,至少只要它只是一次性处理任务。

如果没有,有一个关于调试的广泛常见问题解答,包括 GDB支持
http://www.mono-project.com/Debugging

Miguel也有关于valgrind支持的这篇文章:
http://tirania.org/blog/archive/2007/Jun-29.html

除此之外,如果你让它在Linux上运行,你也可以使用strace来查看系统调用中发生了什么。如果您没有广泛的winforms使用或WinAPI调用,.NET程序通常可以在Linux上正常工作(对于文件系统区分大小写的问题,您可以循环安装不区分大小写的文件系统和/或使用MONO_IOMAP

如果您是以Windows为中心的人,this post Windows最接近的是WinDbg的Logger.exe,但是ltrace信息并不广泛。

Mono源代码可在此处获得:
http://download.mono-project.com/sources/

您可能对最新单声道版本的来源感兴趣 http://download.mono-project.com/sources/mono/mono-3.0.3.tar.bz2

如果你需要框架4.5,你需要单声道3,你可以在这里找到预编译的包 https://www.meebey.net/posts/mono_3.0_preview_debian_ubuntu_packages/

如果您想对源代码进行更改,请按照以下方法进行编译:
http://ubuntuforums.org/showthread.php?t=1591370

答案 4 :(得分:1)

有一些无法捕获的.NET异常。退房:http://msdn.microsoft.com/en-us/magazine/dd419661.aspx