有一个.NET应用程序似乎在非托管堆中存在内存泄漏。我发现了一个很有前途的博客,它解释了如何调试非托管堆,并跟踪堆帧直到导致内存分配的托管函数(https://www.deleaker.com/blog/2021/03/19/unmanaged-memory-leaks-in-dotnet/)。由于我是第一次使用windbg,我决定重复博客中的例子。我复制粘贴了代码,下载了调试工具包,并按照建议使用了windbg。以下是我陷入困境的原因:
Microsoft (R) Windows Debugger Version 10.0.22000.194 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
*** wait with pending attach
************* Path validation summary **************
Response Time (ms) Location
Deferred srv*
Symbol search path is: srv*
Executable search path is:
ModLoad: 00000000`00120000 00000000`00128000 C:<MyPath>WinDbgTest.exe
ModLoad: 00007ffe`c4c10000 00007ffe`c4e05000 C:WINDOWSSYSTEM32ntdll.dll
ModLoad: 00000000`77800000 00000000`779a3000 ntdll.dll
ModLoad: 00007ffe`c3180000 00007ffe`c31d9000 C:WINDOWSSystem32wow64.dll
ModLoad: 00007ffe`c30d0000 00007ffe`c3153000 C:WINDOWSSystem32wow64win.dll
ModLoad: 00000000`777f0000 00000000`777fa000 C:WINDOWSSystem32wow64cpu.dll
ModLoad: 00000000`74890000 00000000`748e2000 mscoree.dll
ModLoad: 00000000`75e50000 00000000`75f40000 KERNEL32.dll
ModLoad: 00000000`76f40000 00000000`77154000 KERNELBASE.dll
ModLoad: 00000000`756b0000 00000000`7572a000 ADVAPI32.dll
ModLoad: 00000000`75730000 00000000`757ef000 msvcrt.dll
ModLoad: 00000000`75be0000 00000000`75c55000 SECHOST.dll
ModLoad: 00000000`76610000 00000000`766d0000 RPCRT4.dll
ModLoad: 00000000`74760000 00000000`747ed000 mscoreei.dll
ModLoad: 00000000`766d0000 00000000`76715000 SHLWAPI.dll
ModLoad: 00000000`74750000 00000000`7475f000 AppCore.dll
ModLoad: 00000000`74e30000 00000000`74e38000 VERSION.dll
ModLoad: 00000000`707a0000 00000000`70f50000 clr.dll
ModLoad: 00000000`75890000 00000000`75a26000 USER32.dll
ModLoad: 00000000`774e0000 00000000`774f8000 win32u.dll
ModLoad: 00000000`705b0000 00000000`7065b000 ucrtbase_clr0400.dll
ModLoad: 00000000`70660000 00000000`70674000 VCRUNTIME140_CLR0400.dll
ModLoad: 00000000`76ec0000 00000000`76ee3000 GDI32.dll
ModLoad: 00000000`75c60000 00000000`75d3c000 gdi32full.dll
ModLoad: 00000000`76810000 00000000`7688b000 msvcp_win.dll
ModLoad: 00000000`75a30000 00000000`75b50000 ucrtbase.dll
ModLoad: 00000000`75f50000 00000000`75f75000 IMM32.dll
ModLoad: 00000000`6f180000 00000000`7058e000 mscorlib.ni.dll
ModLoad: 00000000`76060000 00000000`76143000 ole32.dll
ModLoad: 00000000`77560000 00000000`777e2000 combase.dll
ModLoad: 00000000`76e50000 00000000`76ead000 bcryptPrimitives.dll
ModLoad: 00000000`6f0f0000 00000000`6f17a000 clrjit.dll
ModLoad: 00000000`757f0000 00000000`75886000 OLEAUT32.dll
(745c.6ac0): Break instruction exception - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00007ffe`c4cb0810 cc int 3
0:005> !heap -s
************************************************************************************************************************
NT HEAP STATS BELOW
************************************************************************************************************************
NtGlobalFlag enables following debugging aids for new heaps:
stack back traces
LFH Key : 0xca6fcf0f30c2eb6b
Termination on corruption : ENABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-------------------------------------------------------------------------------------
0000000001db0000 08000002 60 32 60 11 5 1 0 0
0000000000150000 08008000 64 4 64 2 1 1 0 0
-------------------------------------------------------------------------------------
0:005> !heap -stat -h 0000000001db0000
heap @ 0000000001db0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2094 1 - 2094 (48.69)
838 1 - 838 (12.28)
800 1 - 800 (11.96)
120 5 - 5a0 (8.41)
1d8 2 - 3b0 (5.51)
100 3 - 300 (4.48)
238 1 - 238 (3.32)
50 4 - 140 (1.87)
42 3 - c6 (1.16)
3c 2 - 78 (0.70)
62 1 - 62 (0.57)
48 1 - 48 (0.42)
30 1 - 30 (0.28)
28 1 - 28 (0.23)
10 1 - 10 (0.09)
4 1 - 4 (0.02)
0:005> !heap -flt s 2094
_HEAP @ 1db0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0000000001db10c0 020c 0000 [00] 0000000001db10f0 02094 - (busy)
unknown!printable
_HEAP @ 150000
0:005> !heap -p -a 0000000001db10f0
address 0000000001db10f0 found in
_HEAP @ 1db0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0000000001db10c0 020c 0000 [00] 0000000001db10f0 02094 - (busy)
unknown!printable
7ffec4c3b49d ntdll!RtlpAllocateHeapInternal+0x0000000000000a7d
7ffec4c5dce1 ntdll!RtlpInitEnvironmentBlock+0x0000000000000049
7ffec4ce27c1 ntdll!LdrpInitializeProcess+0x0000000000000ba1
7ffec4c84ceb ntdll!LdrpInitialize+0x000000000000015f
7ffec4c84b73 ntdll!LdrpInitialize+0x000000000000003b
7ffec4c84b1e ntdll!LdrInitializeThunk+0x000000000000000e
0:005> .load C:<DllPath>WinDbgDllssos.dll
0:005> !ip2md 7ffec4c84b1e
Failed to load data access DLL, 0x80004005
Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
2) the file mscordacwks.dll that matches your version of clr.dll is
in the version directory or on the symbol path
3) or, if you are debugging a dump file, verify that the file
mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
4) you are debugging on supported cross platform architecture as
the dump file. For example, an ARM dump file must be debugged
on an X86 or an ARM machine; an AMD64 dump file must be
debugged on an AMD64 machine.
You can also run the debugger command .cordll to control the debugger's
load of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.
If that succeeds, the SOS command should work on retry.
If you are debugging a minidump, you need to make sure that your executable
path is pointing to clr.dll as well.
0:005> .cordll -ve -u -l
CLR DLL status: No load attempts
在我打电话之前一切似乎都正常!ip2md,这里来了一个";加载数据访问DLL失败";。根据一些谷歌搜索结果,我将clr.dll、SOS.dll和mscordacwks.dll放在一个文件夹中,并确保它们都具有相同的位和相同的版本。
如何继续?
让.NET决定加载哪个版本,而不是加载位于PC上某个位置的特定版本的SOS。
更换
.load C:<DllPath>WinDbgDllssos.dll
通过
.loadby sos clr
这个命令告诉WinDbg从加载CLR的任何位置加载SOS扩展。这将确保版本匹配,DAC也匹配。
.loadby
可能取决于.NET版本
.loadby sos mscorwks ; *** .NET 2
.loadby sos clr ; *** .NET 4
.loadby sos coreclr ; *** Silverlight and .NET Core