我正在尝试调试一些处理大文件的工作。代码本身工作,但.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
通过添加更多控制台跟踪,我发现它在以下各项中都出错:
foreach
部分数据) - 在验证过程中 GC“完成”之后好奇地 有很多不同的场景。
我可以获取崩溃转储(dmp)文件;我怎样才能进一步研究这个问题,看看当它失败时系统正在做什么?
答案 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。