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

我正在尝试debugging一些处理大文件的工作。 代码本身工作 ,但从.NET运行时本身报告零星的错误。 对于上下文来说,这里的处理是一个1.5GB的文件(只加载到内存中一次)被循环处理和释放,故意尝试重现这个不可预知的错误。

我的testing片段基本上是:

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 } 

(有些时间和其他东西扔进去)

循环将处理罚款非确定性的迭代次数完全成功 – 没有任何问题; 那么这个过程将会突然终止。 exception处理程序没有命中。 testing的确涉及到大量的内存使用,但是在每次迭代过程中锯齿都非常好(没有明显的内存泄漏,而且我有足够的空间 – 在锯齿处最差的地方有14GB未使用的主内存) 。 这个过程是64位的。

Windows错误日志包含3个新条目,其中(通过退出代码80131506)build议执行引擎错误 – 一个讨厌的小动物。 一个相关的答案 ,build议GC错误,用“修复”来禁用并发GC; 然而这个“修复”并不能阻止这个问题。

澄清:这个低级错误不会触及CurrentDomain.UnhandledException事件。

说明: GC.Collect只用于监视锯齿存储器,检查内存泄漏并保持事情可预测; 删除它不会使问题消失:它只是使它在迭代之间保留更多的内存,并使dmp文件更大; p

通过添加更多的控制台跟踪,我已经观察到在每个过程中的错误:

  • 在反序列化(大量的分配等)
  • 在GC期间(使用GC通知API在GC“方法”和GC“完成”之间)
  • 在validation过程中(仅仅是对某些数据的foreach ) – 在validation过程中,在GC“完成” 之后好奇地

所以很多不同的场景。

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

如果你有内存转储,我build议使用WinDbg来看看他们,假设你没有这样做。

尝试运行注释!EEStack (混合本地和托pipe堆栈跟踪),并查看是否有任何可能跳出堆栈跟踪。 在我的testing程序中,我发现这个时代是我发现FEEE发生的堆栈跟踪(我有意破坏堆栈):

 0:000>!EEStack
 ---------------------------------------------
线程0
当前帧:ntdll!NtWaitForSingleObject + 0xa
子SP SPAddAddr调用者,被调用者
 00000089879bd3d0 000007fc586610ea KERNELBASE!WaitForSingleObjectEx + 0x92,调用ntdll!NtWaitForSingleObject
 00000089879bd400 000007fc5869811c KERNELBASE!RaiseException + 0x68,调用ntdll!RtlRaiseException
 [...]
 00000089879bec80 000007fc49109cf6 clr!WKS :: gc_heap :: gc1 + 0x96,调用clr!WKS :: gc_heap :: mark_phase
 00000089879becd0 000007fc49109c21 clr!WKS :: gc_heap :: garbage_collect + 0x222,调用clr!WKS :: gc_heap :: gc1
 00000089879bed10 000007fc491092f1 clr!WKS :: GCHeap :: RestartEE + 0xa2,调用clr!Thread :: ResumeRuntime
 00000089879bed60 000007fc4910998d clr!WKS :: GCHeap :: GarbageCollectGeneration + 0xdd,调用clr!WKS :: gc_heap :: garbage_collect
 00000089879bedb0 000007fc4910df9c clr!WKS :: GCHeap :: Alloc + 0x31b,调用clr!WKS :: GCHeap :: GarbageCollectGeneration
 00000089879bee00 000007fc48ff82e1 clr!JIT_NewArr1 + 0x481

由于这可能与垃圾收集器的堆损坏有关,所以我会尝试使用!VerifyHeap命令。 至less你可以确保这个堆是完整的(而你的问题在别处),或者发现你的问题可能实际上是由GC或一些P / Invoke例程造成的。

如果你发现这个堆已经损坏了,我可能会试着去发现这个堆已经损坏了多less,你可以通过!HeapStat来完成。 尽pipe如此,这可能只是显示了整个堆的腐败。

我们很难提出任何其他方法来通过WinDbg来分析这个方法,因为我不知道你的代码在做什么或者它的结构如何。

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


如果您正在获取的小型转储文件没有被剪切, 而且您正在使用Windows 7 / 2008R2或更高版本,那么您可以使用Global Flags(gflags.exe)在进程终止而无exception时附加debugging器没有得到一个WER通知。

在“ Silent Process Exit选项卡中,input可执行文件的名称, 而不是完整path(即TestProgram.exe )。 使用以下设置:

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

并应用设置。

当你的testing程序崩溃时,cdb会连接并等待你连接到它。 启动WinDbg,键入Ctrl + R,然后使用连接string: tcp:port=5005,server=localhost

您可能可以跳过使用远程debugging,而是使用{path to debugging tools}\windbg.exe %e 。 然而,我build议远程的原因,是因为WerFault.exe ,我相信是什么读取registry,启动监视器进程,将启动debugging器在会话0。

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

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

+

Tools->IntelliTace-> IntelliTaceEbents And Call Information

+

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

并select一个目录

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

在每个debugging会话之后,它假设创build一个debugging会话的logging。 它即使CLR死亡,如果我没有错误的设置目录

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

尝试编写一个通用的exception处理程序,看看是否有一个未处理的exception,以杀死你的应用程序。

  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); 

我通常用Valgrind和gdb来解决与内存有关的问题。

如果你在Windows上运行你的东西,那么有很多很好的select,例如callgrind的verysleepy,如下所示:
有没有一个很好的Valgrind替代Windows?

如果你真的想debugging.NET运行时的内部错误,那么你就有这样一个问题:类库和VM都没有源码。

由于你不能debugging你没有的东西,所以我build议(除了用ILSpy反编译.NET Framework库,并将它们添加到你的项目中,但仍然不包括这个项目),你可以使用单声道运行。
在那里你既有类库的来源也有虚拟机的来源。
也许你的程序在单声道工作正常,那么你的问题就可以解决了,至less只要一次处理任务就行了。

如果没有,那么有一个广泛的debugging常见问题,包括GDB支持
http://www.mono-project.com/Debugging

米格尔也有关于valgrind支持的这个post:
http://tirania.org/blog/archive/2007/Jun-29.html

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

如果你是以Windows为中心的人, 这篇文章说Windows最接近的是WinDbg的Logger.exe,但是ltrace的信息并不是那么广泛。

单声道源代码可在这里find:
http://download.mono-project.com/sources/

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

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

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

有.NET例外,不能被捕获。 签出: http : //msdn.microsoft.com/en-us/magazine/dd419661.aspx 。

对于非确定性和非预测性的错误,windbg崩溃转储分析是最重要的分析机制之一,请仔细检查以下链接,以便了解windbgdebugging的细节:

http://www.debuginfo.com/articles/easywindbg.html

http://www.debuginfo.com/articles/easywindbg2.html

看看这些与windbgdebugging有关的信息性幻灯片:

http://www.slideshare.net/ShanmugaSundaram12/crash-dump-analysisshanmugasundaram

正如你从上面详细了解的正确崩溃转储分析,你会得到使用adplus崩溃开关最重要的方面是正确的符号或pdb文件,因为他们将有助于映射到当前函数调用hex堆栈和将提供关于在生成崩溃/ AV之前执行的方法的关键信息。 符号从_NT_SYMBOL_PATH环境variables中提取。 在Windbg中,您不需要命令提示符工具,可视化界面足以提供错误期间堆栈跟踪的所有线程详细信息。

我的理解是你已经尝试在VS中启用exception,包括最好在exception对话框中启用所有的exception,因为这总是第一个debugging点,如果与特定的exception中断,可以产生关键信息,所以第一个线索。 Windbg总是跟随它,对问题有更深入的了解,这是最出名的Windows工具。

然而,我的观点会在这个问题上有所不同,因为我看到程序涉及在运行时映射到一个巨大的工作集的字节stream,所以为了避免这个问题,你可以尝试以下方法:

– 创build较小的内存块并对其进行处理,这样可以确保如果由于突然的内存压力和大型工作集的映射而导致出现错误,那么GC将获得更大的范围来收集内存并降低整体内存压力。 如果1.5GB可以分成3-5个小块(500MB-300MB)。 在这种情况下,您可以通过在读取字节stream之后或者在读取字节stream之后通过读取文件部分,您可以分成较小的字节[]来反序列化并汇总最终结果。 我几乎已经看到这个照顾许多类似的问题。

  • 正如你已经build议的那样,GC调用并没有在生产中,但是你一定会意识到,即使GC调用并不能确保任何确定性的行为,GC仍然会被自己调用,只是它会确保执行等待让GC在程序迭代之后完成它的工作,然而在这里,因为目前处理的数据似乎已经足够高以至于偶尔重现了这个问题。

如果你可能想看看你的进程的内存使用模式,因为有时不断增加的进程工作集/虚拟字节可能是问题的根源,那么我有一个在这里发布debugging内存不足例外:

当我使用Socket.IO,为什么我得到一个错误未处理的exceptiontypes'System.OutOfMemoryException'

在这种情况下,它可能是OOM之前的一个转折点,但是如果你通过初步的任务pipe理器分析发现,记忆力正在不断增加,那么你可能需要进一步研究这个问题。

另外,虽然我不知道系统configuration,但是您可能想要在/ 3GB / USERVA等窗口中使用启动configuration开关,以将用户进程内存调整为更高的值,这足以避免此类问题需要手动分析才能明确记忆压力的某一点,当它肯定会导致错误