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


67

我正在尝试调试一些处理大文件的工作。该代码本身可以工作,但是.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)建议执行引擎错误-讨厌的小动物。一个相关的答案表明存在GC错误,并带有“修复”以禁用并发GC。但是,此“修复”并不能阻止该问题。

澄清:此低级错误不会触发该CurrentDomain.UnhandledException事件。

澄清:GC.Collect只能监视锯齿状内存,检查内存泄漏并保持可预测的状态;删除它并不会解决问题:只是使它在两次迭代之间保留更多的内存,并使dmp文件更大; p

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

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

有很多不同的场景。

我可以获得崩溃转储(dmp)文件;我如何进一步调查这件事,以查看系统出现如此严重的故障时正在做什么?


4
很好奇为什么要显式调用GC,因为在极少数情况下,可以将其视为良好实践。鉴于您的代表,我相信您有充分的理由并对它的含义感到好奇。
Eric J.

3
@EricJ这不是生产代码;GC收集只是为了使每次迭代都进入已知状态,而不是在中间随机进行。删除它并不能解决错误:它只会使锯齿更难观察; p整个代码块的存在纯粹是为了对其进行压力测试,以再现所报告的错误。
马克·格雷韦尔

16
不确定是否相关,但根据MSDN,垃圾收集器在重负载下会产生此错误:In some cases, an application that targets the .NET Framework may throw an ExecutionEngineException exception during garbage collection when an application or the system on which it is running is under a heavy load. As a workaround, you can disable concurrent garbage collection by modifying the application's configuration file. For more information, see How to: Disable Concurrent Garbage Collection.
2013年

4
您是否设法弄清楚是什么原因造成的?
Dan摆弄Firelight,2013年

2
@Nahum当我问这个问题时,它是通过给我的一个开源项目的支持电子邮件发送的,可以在本地复制。我们似乎不可能有完全相同的RAM故障。
马克·格雷夫

Answers:


22

如果您有内存转储,建议您使用WinDbg进行查看,前提是您尚未这样做。

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

0:000>!EEStack
---------------------------------------------
线程0
当前帧:ntdll!NtWaitForSingleObject + 0xa
子SP RetAddr呼叫者,被呼叫者
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命令。至少您可以确保堆是完整的(并且问题出在其他地方),或者发现您的问题实际上可能与GC或某些P / Invoke例程损坏了。

如果发现堆已损坏,我可能会尝试发现堆中有多少损坏,您可以通过来完成!HeapStat。但是,这可能仅表明整个堆已损坏。

很难建议任何其他方法来通过WinDbg对此进行分析,因为我对您的代码在做什么或如何构造没有真正的线索。

我想如果您发现它与堆有关,那么就意味着它可能是GC异常,我将在Windows事件跟踪中查看CLR GC事件


如果您获得的小型转储没有减少,并且您使用的是Windows 7 / 2008R2或更高版本,则可以在进程终止时无例外地使用全局标志(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。但是,我建议使用remote的原因是因为WerFault.exe,我相信这是读取注册表并启动监视进程的内容,它将在Session 0中启动调试器。

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


您如何使用x64dbg做到这一点
turmuka'4

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调用。


1
进行的工作涉及10 + GB的内存,并且每次迭代要花费一分钟以上的时间,并且可能不会持续很长时间,这可能是过多的日志记录。不过好主意。
Marc Gravell

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

2
我希望他已经尝试过了。他指出:“未命中异常处理程序。” 在这个问题上。
克里斯·弗

9
las,这是一个较低级别的“异常”-80131506是一个ExecutionEngineException; 在那之后,没有托管代码将运行。好主意,但不起作用。
马克·格雷韦尔

我假设他的异常处理程序是他在循环中编写的catch块。
Dhawalk 2013年

1
明确地说:是的,我尝试过;不,那也不会受到打击
Marc Gravell

2
从.NET 4.0开始,ExecutionEngineException导致进程立即终止,因此不幸的是,此操作将无济于事。
卡斯滕

3

我通常使用Valgrind和gdb研究与内存相关的问题。

如果您在Windows上运行您的东西,则有很多不错的替代方法,例如在这里建议的callgrind非常困倦:
是否有Windows的Valgrind替代品?

如果您确实要调试.NET运行时的内部错误,则会遇到以下问题:类库和VM都没有源。

由于您无法调试自己没有的东西,因此我建议(除了使用ILSpy反编译有问题的.NET框架库,并将它们添加到您的项目中,该项目仍然不涉及vm),您可以使用单声道运行时。
那里既有类库的源,也有VM的源。
也许您的程序可以在mono上正常工作,那么至少只要它是一次性处理任务,就可以解决您的问题。

如果没有,则有大量有关调试的常见问题解答,包括GDB支持
http://www.mono-project.com/Debugging

Miguel还发布了有关valgrind支持的帖子:http :
//tirania.org/blog/archive/2007/Jun-29.html

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

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

Mono的源代码可以在这里找到:http :
//download.mono-project.com/sources/

您可能对最新的Mono版本的资源感兴趣,
网址为http://download.mono-project.com/sources/mono/mono-3.0.3.tar.bz2

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

如果要更改源代码,请使用以下方法进行编译:http :
//ubuntuforums.org/showthread.php?t=1591370


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.