在第一次机会 OutOfMemoryException 上触发转储的问题
Posted
技术标签:
【中文标题】在第一次机会 OutOfMemoryException 上触发转储的问题【英文标题】:Problems triggering dump on first-chance OutOfMemoryException 【发布时间】:2013-06-17 04:58:45 【问题描述】:当第一次发生 OutOfMemoryException 时尝试使用 DebugDiag 进行转储时,我遇到了一个问题。所以我写了一个应用程序,我可以用它来创建内存不足的情况并按照以下说明操作:
http://blogs.msdn.com/b/kaushal/archive/2012/05/09/using-debugdiag-to-capture-a-dump-on-first-chance-exception.aspx
但我没有第一次机会转储,我只是获得第二次机会转储。当我查看来自 DebugDiag 的日志文件时,我得到以下信息:
[6/16/2013 9:54:04 PM] First chance exception - 0xe06d7363 caused by thread with System ID: 4628
[6/16/2013 9:54:04 PM] First chance exception - 0xe0434352 caused by thread with System ID: 4628
[6/16/2013 9:54:05 PM] Unable to determine CLR exception type
ExceptionObjHexAddr = 0x00000000`00000000
bInnerException = False
DumpObject Output = Invalid parameter 0x00000000`00000000
ChildEBP RetAddr Args to Child
002bdee4 6a44c93f e0434352 00000001 00000005 KERNELBASE!RaiseException+0x58
002bdf88 6a573b17 00000000 20b578f4 002be04c clr!RaiseTheExceptionInternalOnly+0x276
002bdfb8 6a5e5589 20b54734 002be090 00000000 clr!UnwindAndContinueRethrowHelperAfterCatch+0x83
002be058 003c0a3a 00000000 00000000 0233d174 clr!JIT_NewArr1+0x1af
... removed some rows, lots of data ...
OS Thread Id: 0x1214 (0)
Child SP IP Call Site
002bdfd4 7554c41f [Frame: 002bdfd4]
002be060 003c0a3a
... removed some rows, lots of data ...
Error requesting GC Heap data
Unable to determine bounds of gc heap
后来我明白了:
[6/16/2013 9:54:05 PM] CLR Exception Type - ''
[6/16/2013 9:54:05 PM] First chance exception - 0xe0434352 caused by thread with System ID: 4628
[6/16/2013 9:54:05 PM] Unable to determine CLR exception type
然后我终于明白了
[6/16/2013 9:54:05 PM] CLR Exception Type - ''
[6/16/2013 9:54:05 PM] C:\Windows\Microsoft.NET\Framework\v4.0.30319\diasymreader.dll loaded at 0x615c0000
[6/16/2013 9:54:13 PM] Second chance exception - 0xe0434352 caused by thread with System ID: 4628
貌似可以获取异常对象的地址,为0,所以脚本调用DumpObject时找不到异常信息。
我阅读这些日志条目的方式是我从 malloc 或其他东西中获得原生的第一次机会异常,然后跟进 OutOfMemoryException 的 CLR 异常。我试图弄清楚第二个第一次机会异常是什么,我的代码如下所示:
private void OnGrowMemoryCommand(int growMemorySize)
try
_heldMemoryChunks.Add(new byte[growMemorySize * 1024 * 1024]);
catch (Exception)
throw;
TotalMemorySize += growMemorySize;
此代码由 WPF 按钮上的命令触发。因此,源自此代码的任何异常都应导致 TargetInvocationException,我认为这是第一次机会异常中的第二个。然后最后来自 throw 块的是第二次机会异常,其类型为 TargetInvocationException。
所以我开始查看第二次机会转储文件。我将它加载到 windbg,然后发出这些命令:
.symfix C:\symcache
.loadby sos clr
.reload
!pe
Exception object: 023caf9c
Exception type: System.Reflection.TargetInvocationException
Message: Exception has been thrown by the target of an invocation.
InnerException: System.OutOfMemoryException, Use !PrintException 023c9928 to see more.
我可以看到,我的上述假设得到了第二次机会异常是 TargetInvocationException 的支持,但为什么 DebugDiag 不能获取 CLR 异常类型?对于健全性检查,我尝试进行实时调试会话。所以我启动应用程序并附加,然后发出这些命令。
.symfix C:\symcache
.loadby sos clr
.reload
!threads
Failed to request ThreadStore
!dumpheap
The garbage collector data structures are not in a valid state for traversal.
It is either in the "plan phase," where objects are being moved around, or
we are at the initialization or shutdown of the gc heap. Commands related to
displaying, finding or traversing objects as well as gc heap segments may not
work properly. !dumpheap and !verifyheap may incorrectly complain of heap
consistency errors.
Error requesting GC Heap data
Unable to build snapshot of the garbage collector state
它完全被冲洗掉了。于是我开始研究这个问题。
这个 url 表明它可能是多个 CLR 实例:
http://blogs.msdn.com/b/jjameson/archive/2011/01/11/issues-debugging-managed-code-in-windbg-with-sos-and-psscor2-e-g-quot-failed-to-request-threadstore-quot.aspx
所以我发出这些命令:
.cordll
CLR DLL status: Loaded DLL C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscordacwks.dll
这对我来说很奇怪,我认为从 4.0 开始 mscorwks 就被 clr 抛弃了。 mscordacwks 是 4.5 吗?
我发出了这个命令:
lmvm mscordacwks
但是 clr 已加载:
lmvm clr
start end module name
6a350000 6a9e2000 clr (pdb symbols) C:\symcache\clr.pdb\97FD69E1786F42F9A541C81D81AC96852\clr.pdb
Loaded symbol image file: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Image path: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Image name: clr.dll
Timestamp: Fri Mar 29 00:13:44 2013 (51553118)
CheckSum: 0069496E
ImageSize: 00692000
File version: 4.0.30319.18047
Product version: 4.0.30319.18047
File flags: 8 (Mask 3F) Private
File OS: 4 Unknown Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0409.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft® .NET Framework
InternalName: clr.dll
OriginalFilename: clr.dll
ProductVersion: 4.0.30319.18047
FileVersion: 4.0.30319.18047 built by: FX45RTMGDR
PrivateBuild: DDBLD316
FileDescription: Microsoft .NET Runtime Common Language Runtime - WorkStation
LegalCopyright: © Microsoft Corporation. All rights reserved.
Comments: Flavor=Retail
所以我认为我没有加载多个 CLR。
所以我假设导致我的实时调试问题的同一件事是导致我未能在第一次机会问题上触发。有什么想法吗?
【问题讨论】:
获取此类异常信息的正确方法是关注这篇文章,blogs.msdn.com/b/tom/archive/2008/05/19/… 你不应该尝试获取第一次机会异常转储。此外,如果您可以在内存使用率过高(>1 GB)时捕获挂起转储,您甚至不需要在异常时捕获转储。 KB2020006 记录了此类问题的常见原因,因此您可以从那里开始,support.microsoft.com/kb/2020006 看起来您将 DebugDiag 配置为捕获第一次机会 unmanaged 异常。 0xe06d7363 ('msc') 是一个 C++ 异常,可能是 std::bad_alloc。 0xe0434352 ('ccr') 是托管异常的底层 SEH 异常。在您使用托管调试工具之前,这不会生效,sos.dll 为您提供托管异常。 @Lex Li,有不止一种方法可以做到这一点,我认为你建议的方法不适合我。该方法需要更改注册表项,我不想在所有框上都这样做。另外,我不同意不需要捕获 OOM 异常的转储,我同意您可以在此之前看到问题,但是很高兴知道 OOM 发生时发生了什么。最后,我仍然无法使用 windbg 调试实时实例,这比转储更令人担忧。 @Hans Passant,我只有为 OOM 异常配置了过滤器的 CLR 异常,它为每个第一次机会异常转储一行到日志,因为它必须确定异常的类型看看它是否应该进行转储。 【参考方案1】:我在两台机器上遇到了同样的错误。两者都配备了 .NET 4.5,所以我认为这就是它不起作用的原因。
原来是 DebugDiag 脚本中的一个错误。它无法在其中一个脚本中检索 CLR 异常的名称。但是,这个问题是可以修复的,因为每个 DebugDiag 规则都会创建一个可以修改的 vbs 脚本。它使用完全相同的命令,您也可以在 WinDbg 中使用。以下是解决方法:
使用 GUI 创建您的 DebugDiag 规则,就像您已经做的那样。 对于每个规则,都会生成一个 vbs 脚本文件。在C:\Program Files\DebugDiag\Scripts\CrashRule_<rulename>.vbs
下的文本编辑器中打开它
找到函数GetCLRExceptionType
修改如下:
Function GetCLRExceptionType(ByVal ExceptionObjHexAddr, ByVal bInnerException)
Dim Output, Lines, i
If Debugger.IsClrExtensionMissing Then
WriteToLog "Unable to determine CLR exception type - extension dll could not be loaded."
Else
' Output = Debugger.Execute("!DumpObj " & ExceptionObjHexAddr) ' Does not work in .NET 4.5
Output = Debugger.Execute("!pe") ' FIX .NET45
Lines = Split(Output, Chr(10))
For i = 0 To UBound(Lines)
If bInnerException Then
If InStr(Lines(i), "_innerException") <> 0 Then
Tokens = Split(Lines(i), " ")
For j = 0 To UBound(Tokens)
If Len(Tokens(j)) = 8 Then
GetCLRExceptionType = GetCLRExceptionType(Tokens(j), False)
Exit For
End If
Next
End If
ElseIf Len(Lines(i)) >= 7 Then
If InStr(Lines(i), "Exception type:") = 1 Then ' FIX .NET45
GetCLRExceptionType = Trim(Mid(Lines(i), 16)) ' FIX .NET45
Exit For
End If
End If
Next
If GetCLRExceptionType = "" Then
If g_ClrExceptionTypeFailureLogCount < MAX_CLR_EXCEPTION_TYPE_FAILURE_LOG_ENTRIES Then
g_ClrExceptionTypeFailureLogCount = g_ClrExceptionTypeFailureLogCount + 1
WriteToLog "Unable to determine CLR exception type" & vbcrlf & _
"ExceptionObjHexAddr = " & ExceptionObjHexAddr & vbcrlf & _
"bInnerException = " & bInnerException & vbcrlf & _
"DumpObject Output = " & Output & vbcrlf & _
Debugger.Execute("kb100") & vbcrlf & _
Debugger.Execute("!clrstack") & vbcrlf & _
Debugger.Execute("!dso")
End If
End If
End If
End Function
默认情况下,!DumpObj
用于转储异常对象的内容。但是,在 .NET 4.5 中发生了一些变化,显然,无法再提取异常的类型。相反,将!pe
命令放在这里并从它的结果中解析异常类型。
为了使!pe
工作,你需要做.loadby sos clr
。我把它添加到 Sub Debugger_OnLoadModule
:
Sub Debugger_OnLoadModule(ByVal NewModule)
WriteToLog NewModule.ImageName & " loaded at " & Debugger.GetAs32BitHexString(NewModule.Base)
Select Case UCase(NewModule.ModuleName)
Case "MSCORWKS", "MSCORSVR", "CLR", "CORECLR"
UpdateDeferredManagedBreakpoints
End Select
Debugger.Execute(".loadby sos clr")
End Sub
进行更改后,您需要重新启动进程或停用/重新激活规则以应用它。
另外:请注意,如果您在 DebugDiag GUI 中修改规则,您所做的更改将被完全覆盖。
【讨论】:
很好的答案!为我工作。同样,我刚刚升级到 DebugDiag 2.0,在其中我没有遇到这个问题以上是关于在第一次机会 OutOfMemoryException 上触发转储的问题的主要内容,如果未能解决你的问题,请参考以下文章