From 6b66e69d455cc444f07a5bf2172719c143e569cd Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Fri, 31 Jan 2020 00:03:48 -0800 Subject: [PATCH] Various misc changes and SOS fixes (#789) Misc build changes Fix source/line number support Make sure the ip doesn't get adjusted before the start of the function. Allow `!SuppressJitOptimization on` to be used before the runtime is loaded and on xplat Add display il map option to !u --- eng/Build-Native.cmd | 16 +- eng/build.sh | 20 +- eng/common/msbuild.sh | 0 src/SOS/SOS.NETCore/SymbolReader.cs | 26 +- src/SOS/Strike/hostcoreclr.h | 11 +- src/SOS/Strike/sos_unixexports.src | 1 + src/SOS/Strike/sosdocs.txt | 1370 +++++++++++++-------------- src/SOS/Strike/sosdocsunix.txt | 1062 ++++++++++----------- src/SOS/Strike/strike.cpp | 114 ++- src/SOS/Strike/util.cpp | 32 +- 10 files changed, 1326 insertions(+), 1326 deletions(-) mode change 100644 => 100755 eng/common/msbuild.sh diff --git a/eng/Build-Native.cmd b/eng/Build-Native.cmd index bdd4b942a..8ab393194 100644 --- a/eng/Build-Native.cmd +++ b/eng/Build-Native.cmd @@ -176,14 +176,8 @@ if /i %__BuildCrossArch% EQU 1 ( if not exist "%__CrossCompIntermediatesDir%" md "%__CrossCompIntermediatesDir%" echo Generating Version Header - set __GenerateVersionRestoreLog="%__LogDir%\GenerateVersionRestore.binlog" - "%__DotNetCli%" msbuild "%__ProjectDir%\eng\CreateVersionFile.csproj" /bl:!__GenerateVersionRestoreLog! /t:Restore %__CommonBuildArgs% - if not !errorlevel! == 0 ( - echo Generate Version Restore FAILED - exit /b 1 - ) set __GenerateVersionLog="%__LogDir%\GenerateVersion.binlog" - "%__DotNetCli%" msbuild "%__ProjectDir%\eng\CreateVersionFile.csproj" /bl:!__GenerateVersionLog! /t:GenerateVersionFiles /p:FileVersionFile=%__RootBinDir%\bin\FileVersion.txt /p:GenerateVersionHeader=true /p:NativeVersionHeaderFile=%__CrossCompIntermediatesDir%\_version.h %__CommonBuildArgs% + powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__ProjectDir%\eng\common\msbuild.ps1" "%__ProjectDir%\eng\CreateVersionFile.csproj" /bl:!__GenerateVersionLog! /t:GenerateVersionFiles /restore /p:FileVersionFile=%__RootBinDir%\bin\FileVersion.txt /p:GenerateVersionHeader=true /p:NativeVersionHeaderFile=%__CrossCompIntermediatesDir%\_version.h %__CommonBuildArgs% if not !errorlevel! == 0 ( echo Generate Version Header FAILED exit /b 1 @@ -260,14 +254,8 @@ if %__Build% EQU 1 ( ) echo Generating Version Header - set __GenerateVersionRestoreLog="%__LogDir%\GenerateVersionRestore.binlog" - "%__DotNetCli%" msbuild "%__ProjectDir%\eng\CreateVersionFile.csproj" /bl:!__GenerateVersionRestoreLog! /t:Restore %__CommonBuildArgs% - if not !errorlevel! == 0 ( - echo Generate Version Restore FAILED - exit /b 1 - ) set __GenerateVersionLog="%__LogDir%\GenerateVersion.binlog" - "%__DotNetCli%" msbuild "%__ProjectDir%\eng\CreateVersionFile.csproj" /bl:!__GenerateVersionLog! /t:GenerateVersionFiles /p:FileVersionFile=%__RootBinDir%\bin\FileVersion.txt /p:GenerateVersionHeader=true /p:NativeVersionHeaderFile=%__IntermediatesDir%\_version.h %__CommonBuildArgs% + powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__ProjectDir%\eng\common\msbuild.ps1" "%__ProjectDir%\eng\CreateVersionFile.csproj" /bl:!__GenerateVersionLog! /t:GenerateVersionFiles /restore /p:FileVersionFile=%__RootBinDir%\bin\FileVersion.txt /p:GenerateVersionHeader=true /p:NativeVersionHeaderFile=%__IntermediatesDir%\_version.h %__CommonBuildArgs% if not !errorlevel! == 0 ( echo Generate Version Header FAILED exit /b 1 diff --git a/eng/build.sh b/eng/build.sh index c79122f93..7a928e436 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -441,20 +441,12 @@ if [ ! -e $__DotNetCli ]; then fi if [ $__NativeBuild == true ]; then - if [[ $__CI == true ]]; then - echo "Generating Version Source File" - __GenerateVersionRestoreLog="$__LogDir/GenerateVersionRestore.binlog" - $__DotNetCli msbuild $__ProjectRoot/eng/CreateVersionFile.csproj /v:$__Verbosity /bl:$__GenerateVersionRestoreLog /t:Restore /p:Configuration="$__BuildType" /p:Platform="$__BuildArch" $__UnprocessedBuildArgs - - __GenerateVersionLog="$__LogDir/GenerateVersion.binlog" - $__DotNetCli msbuild $__ProjectRoot/eng/CreateVersionFile.csproj /v:$__Verbosity /bl:$__GenerateVersionLog /t:GenerateVersionFiles /p:GenerateVersionSourceFile=true /p:NativeVersionSourceFile="$__IntermediatesDir/version.cpp" /p:Configuration="$__BuildType" /p:Platform="$__BuildArch" $__UnprocessedBuildArgs - if [ $? != 0 ]; then - echo "Generating Version Source File FAILED" - exit 1 - fi - else - echo "Generating Empty Version Source File" - echo "" > "$__IntermediatesDir/version.cpp" + echo "Generating Version Source File" + __GenerateVersionLog="$__LogDir/GenerateVersion.binlog" + "$__ProjectRoot/eng/common/msbuild.sh" $__ProjectRoot/eng/CreateVersionFile.csproj /v:$__Verbosity /bl:$__GenerateVersionLog /t:GenerateVersionFiles /restore /p:GenerateVersionSourceFile=true /p:NativeVersionSourceFile="$__IntermediatesDir/version.cpp" /p:Configuration="$__BuildType" /p:Platform="$__BuildArch" $__UnprocessedBuildArgs + if [ $? != 0 ]; then + echo "Generating Version Source File FAILED" + exit 1 fi build_native "$__BuildArch" "$__IntermediatesDir" "$__ExtraCmakeArgs" diff --git a/eng/common/msbuild.sh b/eng/common/msbuild.sh old mode 100644 new mode 100755 diff --git a/src/SOS/SOS.NETCore/SymbolReader.cs b/src/SOS/SOS.NETCore/SymbolReader.cs index 1f17a2284..2cd025443 100644 --- a/src/SOS/SOS.NETCore/SymbolReader.cs +++ b/src/SOS/SOS.NETCore/SymbolReader.cs @@ -529,25 +529,21 @@ namespace SOS MethodDebugInformation methodDebugInfo = reader.GetMethodDebugInformation(methodDebugHandle); SequencePointCollection sequencePoints = methodDebugInfo.GetSequencePoints(); - SequencePoint nearestPoint = sequencePoints.GetEnumerator().Current; + SequencePoint? nearestPoint = null; foreach (SequencePoint point in sequencePoints) { - if (point.Offset < ilOffset) - { - nearestPoint = point; - } - else - { - if (point.Offset == ilOffset) - nearestPoint = point; + if (point.Offset > ilOffset) + break; - if (nearestPoint.StartLine == 0 || nearestPoint.StartLine == SequencePoint.HiddenLine) - return false; + if (point.StartLine != 0 && !point.IsHidden) + nearestPoint = point; + } - lineNumber = nearestPoint.StartLine; - fileName = reader.GetString(reader.GetDocument(nearestPoint.Document).Name); - return true; - } + if (nearestPoint.HasValue) + { + lineNumber = nearestPoint.Value.StartLine; + fileName = reader.GetString(reader.GetDocument(nearestPoint.Value.Document).Name); + return true; } } catch diff --git a/src/SOS/Strike/hostcoreclr.h b/src/SOS/Strike/hostcoreclr.h index e1bc8d7f4..e5b703d1b 100644 --- a/src/SOS/Strike/hostcoreclr.h +++ b/src/SOS/Strike/hostcoreclr.h @@ -154,14 +154,15 @@ public: HRESULT LoadSymbols(___in IMetaDataImport* pMD, ___in IXCLRDataModule* pModule); HRESULT GetLineByILOffset(___in mdMethodDef MethodToken, ___in ULONG64 IlOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, ___in ULONG cchFileName); HRESULT GetNamedLocalVariable(___in ICorDebugFrame * pFrame, ___in ULONG localIndex, __out_ecount(paramNameLen) WCHAR* paramName, ___in ULONG paramNameLen, ___out ICorDebugValue** ppValue); - HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___out mdMethodDef* ___out pToken, ___out ULONG32* pIlOffset); + HRESULT ResolveSequencePoint(__in_z WCHAR* pFilename, ___in ULONG32 lineNumber, ___out mdMethodDef* pToken, ___out ULONG32* pIlOffset); }; HRESULT GetLineByOffset( - ___in ULONG64 IP, - ___out ULONG *pLinenum, - __out_ecount(cchFileName) WCHAR* pwszFileName, - ___in ULONG cchFileName); + ___in ULONG64 nativeOffset, + ___out ULONG* pLinenum, + __out_ecount(cchFileName) WCHAR* pwszFileName, + ___in ULONG cchFileName, + ___in BOOL bAdjustOffsetForLineNumber = FALSE); #endif // __hostcoreclr_h__ diff --git a/src/SOS/Strike/sos_unixexports.src b/src/SOS/Strike/sos_unixexports.src index a9afa599c..50009d200 100644 --- a/src/SOS/Strike/sos_unixexports.src +++ b/src/SOS/Strike/sos_unixexports.src @@ -50,6 +50,7 @@ SetHostRuntime SetSymbolServer SOSFlush SOSStatus +SuppressJitOptimization SyncBlk Threads ThreadState diff --git a/src/SOS/Strike/sosdocs.txt b/src/SOS/Strike/sosdocs.txt index fcd4cfad0..212ad00e3 100644 --- a/src/SOS/Strike/sosdocs.txt +++ b/src/SOS/Strike/sosdocs.txt @@ -34,7 +34,7 @@ GCRoot GCInfo ObjSize EHInfo FinalizeQueue BPMD (bpmd) PrintException (pe) COMState -TraverseHeap +TraverseHeap Examining CLR data structures Diagnostic Utilities ----------------------------- ----------------------------- @@ -56,7 +56,7 @@ DumpSig VMMap RCWCleanupList VMStat DumpIL MinidumpMode DumpRCW AnalyzeOOM (ao) -DumpCCW +DumpCCW SuppressJitOptimization Examining the GC history Other ----------------------------- ----------------------------- @@ -94,11 +94,11 @@ type: >> I got the following error message. Now what? - 0:000> .loadby sos coreclr - 0:000> !DumpStackObjects - Failed to find runtime DLL (coreclr.dll), 0x80004005 - Extension commands need coreclr.dll in order to have something to do. - 0:000> + 0:000> .loadby sos coreclr + 0:000> !DumpStackObjects + Failed to find runtime DLL (coreclr.dll), 0x80004005 + Extension commands need coreclr.dll in order to have something to do. + 0:000> This means that the coreclr is not loaded yet, or has been unloaded. You need to wait until your managed program is running in order to use these commands. If @@ -224,16 +224,16 @@ the size. You might find an object pointer by running !DumpStackObjects and choosing from the resultant list. Here is a simple object: - 0:000> !DumpObj a79d40 - Name: Customer - MethodTable: 009038ec - EEClass: 03ee1b84 - Size: 20(0x14) bytes - (C:\pub\unittest.exe) - Fields: - MT Field Offset Type VT Attr Value Name - 009038ec 4000008 4 Customer 0 instance 00a79ce4 name - 009038ec 4000009 8 Bank 0 instance 00a79d2c bank + 0:000> !DumpObj a79d40 + Name: Customer + MethodTable: 009038ec + EEClass: 03ee1b84 + Size: 20(0x14) bytes + (C:\pub\unittest.exe) + Fields: + MT Field Offset Type VT Attr Value Name + 009038ec 4000008 4 Customer 0 instance 00a79ce4 name + 009038ec 4000009 8 Bank 0 instance 00a79d2c bank Note that fields of type Customer and Bank are themselves objects, and you can run !DumpObj on them too. You could look at the field directly in memory using @@ -260,11 +260,11 @@ The arguments in detail: COMMAND: da. COMMAND: dumparray. !DumpArray - [-start ] - [-length ] - [-details] - [-nofields] - + [-start ] + [-length ] + [-details] + [-nofields] + This command allows you to examine elements of an array object. The arguments in detail: @@ -280,46 +280,46 @@ The arguments in detail: Example output: - 0:000> !dumparray -start 2 -length 3 -details 00ad28d0 - Name: Value[] - MethodTable: 03e41044 - EEClass: 03e40fc0 - Size: 132(0x84) bytes - Array: Rank 1, Number of elements 10, Type VALUETYPE - Element Type: Value - [2] 00ad28f0 - Name: Value - MethodTable 03e40f4c - EEClass: 03ef1698 - Size: 20(0x14) bytes - (C:\bugs\225271\arraytest.exe) - Fields: - MT Field Offset Type Attr Value Name - 5b9a628c 4000001 0 System.Int32 instance 2 x - 5b9a628c 4000002 4 System.Int32 instance 4 y - 5b9a628c 4000003 8 System.Int32 instance 6 z - [3] 00ad28fc - Name: Value - MethodTable 03e40f4c - EEClass: 03ef1698 - Size: 20(0x14) bytes - (C:\bugs\225271\arraytest.exe) - Fields: - MT Field Offset Type Attr Value Name - 5b9a628c 4000001 0 System.Int32 instance 3 x - 5b9a628c 4000002 4 System.Int32 instance 6 y - 5b9a628c 4000003 8 System.Int32 instance 9 z - [4] 00ad2908 - Name: Value - MethodTable 03e40f4c - EEClass: 03ef1698 - Size: 20(0x14) bytes - (C:\bugs\225271\arraytest.exe) - Fields: - MT Field Offset Type Attr Value Name - 5b9a628c 4000001 0 System.Int32 instance 4 x - 5b9a628c 4000002 4 System.Int32 instance 8 y - 5b9a628c 4000003 8 System.Int32 instance 12 z + 0:000> !dumparray -start 2 -length 3 -details 00ad28d0 + Name: Value[] + MethodTable: 03e41044 + EEClass: 03e40fc0 + Size: 132(0x84) bytes + Array: Rank 1, Number of elements 10, Type VALUETYPE + Element Type: Value + [2] 00ad28f0 + Name: Value + MethodTable 03e40f4c + EEClass: 03ef1698 + Size: 20(0x14) bytes + (C:\bugs\225271\arraytest.exe) + Fields: + MT Field Offset Type Attr Value Name + 5b9a628c 4000001 0 System.Int32 instance 2 x + 5b9a628c 4000002 4 System.Int32 instance 4 y + 5b9a628c 4000003 8 System.Int32 instance 6 z + [3] 00ad28fc + Name: Value + MethodTable 03e40f4c + EEClass: 03ef1698 + Size: 20(0x14) bytes + (C:\bugs\225271\arraytest.exe) + Fields: + MT Field Offset Type Attr Value Name + 5b9a628c 4000001 0 System.Int32 instance 3 x + 5b9a628c 4000002 4 System.Int32 instance 6 y + 5b9a628c 4000003 8 System.Int32 instance 9 z + [4] 00ad2908 + Name: Value + MethodTable 03e40f4c + EEClass: 03ef1698 + Size: 20(0x14) bytes + (C:\bugs\225271\arraytest.exe) + Fields: + MT Field Offset Type Attr Value Name + 5b9a628c 4000001 0 System.Int32 instance 4 x + 5b9a628c 4000002 4 System.Int32 instance 8 y + 5b9a628c 4000003 8 System.Int32 instance 12 z \\ @@ -364,7 +364,7 @@ COMMAND: dumpdelegate. For example: - 0:000> !dumpdelegate + 0:000> !dumpdelegate Target Method Name 000001461bacb0d8 00007ffc5c894b80 ConsoleApp16.Program.InstanceMethod() 000001461bacb098 00007ffc5c894b68 ConsoleApp16.Program.StaticMethod() @@ -393,26 +393,26 @@ fragmentation in the GC heap. When called without options, the output is first a list of objects in the heap, followed by a report listing all the types found, their size and number: - 0:000> !dumpheap - Address MT Size - 00a71000 0015cde8 12 Free - 00a7100c 0015cde8 12 Free - 00a71018 0015cde8 12 Free - 00a71024 5ba58328 68 - 00a71068 5ba58380 68 - 00a710ac 5ba58430 68 - 00a710f0 5ba5dba4 68 - ... - total 619 objects - Statistics: - MT Count TotalSize Class Name - 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource - 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag - 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer - ... - 0015cde8 6 10260 Free - 5ba57bf8 318 18136 System.String - ... + 0:000> !dumpheap + Address MT Size + 00a71000 0015cde8 12 Free + 00a7100c 0015cde8 12 Free + 00a71018 0015cde8 12 Free + 00a71024 5ba58328 68 + 00a71068 5ba58380 68 + 00a710ac 5ba58430 68 + 00a710f0 5ba5dba4 68 + ... + total 619 objects + Statistics: + MT Count TotalSize Class Name + 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource + 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag + 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer + ... + 0015cde8 6 10260 Free + 5ba57bf8 318 18136 System.String + ... "Free" objects are simply regions of space the garbage collector can use later. If 30%% or more of the heap contains "Free" objects, the process may suffer from @@ -420,12 +420,12 @@ heap fragmentation. This is usually caused by pinning objects for a long time combined with a high rate of allocation. Here is example output where !DumpHeap provides a warning about fragmentation: - - Fragmented blocks larger than 1MB: - Addr Size Followed by - 00a780c0 1.5MB 00bec800 System.Byte[] - 00da4e38 1.2MB 00ed2c00 System.Byte[] - 00f16df0 1.2MB 01044338 System.Byte[] + + Fragmented blocks larger than 1MB: + Addr Size Followed by + 00a780c0 1.5MB 00bec800 System.Byte[] + 00da4e38 1.2MB 00ed2c00 System.Byte[] + 00f16df0 1.2MB 01044338 System.Byte[] The arguments in detail: @@ -473,41 +473,41 @@ be returned. More generally, "-type []". The start/end parameters can be obtained from the output of !EEHeap -gc. For example, if you only want to list objects in the large heap segment: - 0:000> !eeheap -gc - Number of GC Heaps: 1 - generation 0 starts at 0x00c32754 - generation 1 starts at 0x00c32748 - generation 2 starts at 0x00a71000 - segment begin allocated size - 00a70000 00a71000 010443a8 005d33a8(6108072) - Large object heap starts at 0x01a71000 - segment begin allocated size - 01a70000 01a71000 01a75000 0x00004000(16384) - Total Size 0x5d73a8(6124456) - ------------------------------ - GC Heap Size 0x5d73a8(6124456) - - 0:000> !dumpheap 1a71000 1a75000 - Address MT Size - 01a71000 5ba88bd8 2064 - 01a71810 0019fe48 2032 Free - 01a72000 5ba88bd8 4096 - 01a73000 0019fe48 4096 Free - 01a74000 5ba88bd8 4096 - total 5 objects - Statistics: - MT Count TotalSize Class Name - 0019fe48 2 6128 Free - 5ba88bd8 3 10256 System.Object[] - Total 5 objects + 0:000> !eeheap -gc + Number of GC Heaps: 1 + generation 0 starts at 0x00c32754 + generation 1 starts at 0x00c32748 + generation 2 starts at 0x00a71000 + segment begin allocated size + 00a70000 00a71000 010443a8 005d33a8(6108072) + Large object heap starts at 0x01a71000 + segment begin allocated size + 01a70000 01a71000 01a75000 0x00004000(16384) + Total Size 0x5d73a8(6124456) + ------------------------------ + GC Heap Size 0x5d73a8(6124456) + + 0:000> !dumpheap 1a71000 1a75000 + Address MT Size + 01a71000 5ba88bd8 2064 + 01a71810 0019fe48 2032 Free + 01a72000 5ba88bd8 4096 + 01a73000 0019fe48 4096 Free + 01a74000 5ba88bd8 4096 + total 5 objects + Statistics: + MT Count TotalSize Class Name + 0019fe48 2 6128 Free + 5ba88bd8 3 10256 System.Object[] + Total 5 objects Finally, if GC heap corruption is present, you may see an error like this: - 0:000> !dumpheap -stat - object 00a73d24: does not have valid MT - curr_object : 00a73d24 - Last good object: 00a73d14 - ---------------- + 0:000> !dumpheap -stat + object 00a73d24: does not have valid MT + curr_object : 00a73d24 + Last good object: 00a73d14 + ---------------- That indicates a serious problem. See the help for !VerifyHeap for more information on diagnosing the cause. @@ -522,33 +522,33 @@ to know the MethodTable address to tell SOS how to interpret the fields, as a value class is not a first-class object with it's own MethodTable as the first field. For example: - 0:000> !DumpObj a79d98 - Name: Mainy - MethodTable: 009032d8 - EEClass: 03ee1424 - Size: 28(0x1c) bytes - (C:\pub\unittest.exe) - Fields: - MT Field Offset Type Attr Value Name - 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype - 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep + 0:000> !DumpObj a79d98 + Name: Mainy + MethodTable: 009032d8 + EEClass: 03ee1424 + Size: 28(0x1c) bytes + (C:\pub\unittest.exe) + Fields: + MT Field Offset Type Attr Value Name + 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype + 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep m_valuetype is a value type. The value in the MT column (0090320c) is the MethodTable for it, and the Value column provides the start address: - 0:000> !DumpVC 0090320c 00a79d9c - Name: Funny - MethodTable 0090320c - EEClass: 03ee14b8 - Size: 28(0x1c) bytes - (C:\pub\unittest.exe) - Fields: - MT Field Offset Type Attr Value Name - 0090320c 4000001 0 CLASS instance 00a743d8 signature - 0090320c 4000002 8 System.Int32 instance 2345 m1 - 0090320c 4000003 10 System.Boolean instance 1 b1 - 0090320c 4000004 c System.Int32 instance 1234 m2 - 0090320c 4000005 4 CLASS instance 00a79d98 backpointer + 0:000> !DumpVC 0090320c 00a79d9c + Name: Funny + MethodTable 0090320c + EEClass: 03ee14b8 + Size: 28(0x1c) bytes + (C:\pub\unittest.exe) + Fields: + MT Field Offset Type Attr Value Name + 0090320c 4000001 0 CLASS instance 00a743d8 signature + 0090320c 4000002 8 System.Int32 instance 2345 m1 + 0090320c 4000003 10 System.Boolean instance 1 b1 + 0090320c 4000004 c System.Int32 instance 1234 m2 + 0090320c 4000005 4 CLASS instance 00a79d98 backpointer !DumpVC is quite a specialized function. Some managed programs make heavy use of value classes, while others do not. @@ -588,22 +588,22 @@ of any objects pointed to by those handles. In calculating object size, For example, !DumpObj lists a size of 20 bytes for this Customer object: - 0:000> !do a79d40 - Name: Customer - MethodTable: 009038ec - EEClass: 03ee1b84 - Size: 20(0x14) bytes - (C:\pub\unittest.exe) - Fields: - MT Field Offset Type Attr Value Name - 009038ec 4000008 4 CLASS instance 00a79ce4 name - 009038ec 4000009 8 CLASS instance 00a79d2c bank - 009038ec 400000a c System.Boolean instance 1 valid + 0:000> !do a79d40 + Name: Customer + MethodTable: 009038ec + EEClass: 03ee1b84 + Size: 20(0x14) bytes + (C:\pub\unittest.exe) + Fields: + MT Field Offset Type Attr Value Name + 009038ec 4000008 4 CLASS instance 00a79ce4 name + 009038ec 4000009 8 CLASS instance 00a79d2c bank + 009038ec 400000a c System.Boolean instance 1 valid but !ObjSize lists 152 bytes: - 0:000> !ObjSize a79d40 - sizeof(00a79d40) = 152 ( 0x98) bytes (Customer) + 0:000> !ObjSize a79d40 + sizeof(00a79d40) = 152 ( 0x98) bytes (Customer) This is because a Customer points to a Bank, has a name, and the Bank points to an Address string. You can use !ObjSize to identify any particularly large @@ -627,20 +627,20 @@ COMMAND: finalizequeue. This command lists the objects registered for finalization. Here is output from a simple program: - 0:000> !finalizequeue - SyncBlocks to be cleaned up: 0 - MTA Interfaces to be released: 0 - STA Interfaces to be released: 1 - generation 0 has 4 finalizable objects (0015bc90->0015bca0) - generation 1 has 0 finalizable objects (0015bc90->0015bc90) - generation 2 has 0 finalizable objects (0015bc90->0015bc90) - Ready for finalization 0 objects (0015bca0->0015bca0) - Statistics: - MT Count TotalSize Class Name - 5ba6cf78 1 24 Microsoft.Win32.SafeHandles.SafeFileHandle - 5ba5db04 1 68 System.Threading.Thread - 5ba73e28 2 112 System.IO.StreamWriter - Total 4 objects + 0:000> !finalizequeue + SyncBlocks to be cleaned up: 0 + MTA Interfaces to be released: 0 + STA Interfaces to be released: 1 + generation 0 has 4 finalizable objects (0015bc90->0015bca0) + generation 1 has 0 finalizable objects (0015bc90->0015bc90) + generation 2 has 0 finalizable objects (0015bc90->0015bc90) + Ready for finalization 0 objects (0015bca0->0015bca0) + Statistics: + MT Count TotalSize Class Name + 5ba6cf78 1 24 Microsoft.Win32.SafeHandles.SafeFileHandle + 5ba5db04 1 68 System.Threading.Thread + 5ba73e28 2 112 System.IO.StreamWriter + Total 4 objects The GC heap is divided into generations, and objects are listed accordingly. We see that only generation 0 (the youngest generation) has any objects registered @@ -924,24 +924,24 @@ COMMAND: ip2md. Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc associated with it. For example, this output from K: - 0:000> K - ChildEBP RetAddr - 00a79c78 03ef02ab image00400000!Mainy.Top()+0xb - 00a79c78 03ef01a6 image00400000!Mainy.Level(Int32)+0xb - 00a79c78 5d3725a1 image00400000!Mainy.Main()+0xee - 0012ea04 5d512f59 clr!CallDescrWorkerInternal+0x30 - 0012ee34 5d7946aa clr!CallDescrWorker+0x109 - - 0:000> !IP2MD 03ef01a6 - MethodDesc: 00902f40 - Method Name: Mainy.Main() - Class: 03ee1424 - MethodTable: 009032d8 - mdToken: 0600000d - Module: 001caa38 - IsJitted: yes - CodeAddr: 03ef00b8 - Source file: c:\Code\prj.mini\exc.cs @ 39 + 0:000> K + ChildEBP RetAddr + 00a79c78 03ef02ab image00400000!Mainy.Top()+0xb + 00a79c78 03ef01a6 image00400000!Mainy.Level(Int32)+0xb + 00a79c78 5d3725a1 image00400000!Mainy.Main()+0xee + 0012ea04 5d512f59 clr!CallDescrWorkerInternal+0x30 + 0012ee34 5d7946aa clr!CallDescrWorker+0x109 + + 0:000> !IP2MD 03ef01a6 + MethodDesc: 00902f40 + Method Name: Mainy.Main() + Class: 03ee1424 + MethodTable: 009032d8 + mdToken: 0600000d + Module: 001caa38 + IsJitted: yes + CodeAddr: 03ef00b8 + Source file: c:\Code\prj.mini\exc.cs @ 39 We have taken a return address into Mainy.Main, and discovered information about that method. You could run !U, !DumpMT, !DumpClass, !DumpMD, or @@ -960,12 +960,12 @@ pointer for the method, or a code address within the method body. Unlike the debugger "U" function, the entire method from start to finish is printed, with annotations that convert metadata tokens to names. - - ... - 03ef015d b901000000 mov ecx,0x1 - 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713) - 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter) - 03ef016d 89442414 mov [esp+0x14],eax + + ... + 03ef015d b901000000 mov ecx,0x1 + 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713) + 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter) + 03ef016d 89442414 mov [esp+0x14],eax If you pass the -gcinfo flag, you'll get inline display of the GCInfo for the method. You can also obtain this information with the !GCInfo command. @@ -985,17 +985,17 @@ include the source file name and line number corresponding to the disassembly. The -n (No line numbers) flag can be specified to disable this behavior. - - ... - c:\Code\prj.mini\exc.cs @ 38: - 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type to continue: ") - 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b) - 001b00bb 90 nop - - c:\Code\prj.mini\exc.cs @ 39: - 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6) - >>> 001b00c1 90 nop - ... + + ... + c:\Code\prj.mini\exc.cs @ 38: + 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type to continue: ") + 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b) + 001b00bb 90 nop + + c:\Code\prj.mini\exc.cs @ 39: + 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6) + >>> 001b00c1 90 nop + ... \\ COMMAND: dumpstack. @@ -1045,27 +1045,27 @@ block and the handler block. For a TYPED handler, this would be the "try" and Sample output: - 0:000> !ehinfo 33bbd3a - MethodDesc: 03310f68 - Method Name: MainClass.Main() - Class: 03571358 - MethodTable: 0331121c - mdToken: 0600000b - Module: 001e2fd8 - IsJitted: yes - CodeAddr: 033bbca0 + 0:000> !ehinfo 33bbd3a + MethodDesc: 03310f68 + Method Name: MainClass.Main() + Class: 03571358 + MethodTable: 0331121c + mdToken: 0600000b + Module: 001e2fd8 + IsJitted: yes + CodeAddr: 033bbca0 - EHHandler 0: TYPED catch(System.IO.FileNotFoundException) - Clause: [033bbd2b, 033bbd3c] [8b, 9c] - Handler: [033bbd3c, 033bbd50] [9c, b0] + EHHandler 0: TYPED catch(System.IO.FileNotFoundException) + Clause: [033bbd2b, 033bbd3c] [8b, 9c] + Handler: [033bbd3c, 033bbd50] [9c, b0] - EHHandler 1: FINALLY - Clause: [033bbd83, 033bbda3] [e3, 103] - Handler: [033bbda3, 033bbdc5] [103, 125] + EHHandler 1: FINALLY + Clause: [033bbd83, 033bbda3] [e3, 103] + Handler: [033bbda3, 033bbdc5] [103, 125] - EHHandler 2: TYPED catch(System.Exception) - Clause: [033bbd7a, 033bbdc5] [da, 125] - Handler: [033bbdc5, 033bbdd6] [125, 136] + EHHandler 2: TYPED catch(System.Exception) + Clause: [033bbd7a, 033bbdc5] [da, 125] + Handler: [033bbdc5, 033bbdd6] [125, 136] \\ @@ -1084,53 +1084,53 @@ you would print this output out and read it alongside a disassembly of the method. For example, the notation "reg EDI becoming live" at offset 0x11 of the method might correspond to a "mov edi,ecx" statement. - 0:000> !gcinfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method) - entry point 5b68dbb8 - preJIT generated code - GC info 5b9f2f09 - Method info block: - method size = 0036 - prolog size = 19 - epilog size = 8 - epilog count = 1 - epilog end = yes - saved reg. mask = 000B - ebp frame = yes - fully interruptible=yes - double align = no - security check = no - exception handlers = no - local alloc = no - edit & continue = no - varargs = no - argument count = 4 - stack frame size = 1 - untracked count = 5 - var ptr tab count = 0 - epilog at 002E - 36 D4 8C C7 AA | - 93 F3 40 05 | - - Pointer table: - 14 | [EBP+14H] an untracked local - 10 | [EBP+10H] an untracked local - 0C | [EBP+0CH] an untracked local - 08 | [EBP+08H] an untracked local - 44 | [EBP-04H] an untracked local - F1 79 | 0011 reg EDI becoming live - 72 | 0013 reg ESI becoming live - 83 | 0016 push ptr 0 - 8B | 0019 push ptr 1 - 93 | 001C push ptr 2 - 9B | 001F push ptr 3 - 56 | 0025 reg EDX becoming live - 4A | 0027 reg ECX becoming live - 0E | 002D reg ECX becoming dead - 10 | 002D reg EDX becoming dead - E0 | 002D pop 4 ptrs - F0 31 | 0036 reg ESI becoming dead - 38 | 0036 reg EDI becoming dead - FF | + 0:000> !gcinfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method) + entry point 5b68dbb8 + preJIT generated code + GC info 5b9f2f09 + Method info block: + method size = 0036 + prolog size = 19 + epilog size = 8 + epilog count = 1 + epilog end = yes + saved reg. mask = 000B + ebp frame = yes + fully interruptible=yes + double align = no + security check = no + exception handlers = no + local alloc = no + edit & continue = no + varargs = no + argument count = 4 + stack frame size = 1 + untracked count = 5 + var ptr tab count = 0 + epilog at 002E + 36 D4 8C C7 AA | + 93 F3 40 05 | + + Pointer table: + 14 | [EBP+14H] an untracked local + 10 | [EBP+10H] an untracked local + 0C | [EBP+0CH] an untracked local + 08 | [EBP+08H] an untracked local + 44 | [EBP-04H] an untracked local + F1 79 | 0011 reg EDI becoming live + 72 | 0013 reg ESI becoming live + 83 | 0016 push ptr 0 + 8B | 0019 push ptr 1 + 93 | 001C push ptr 2 + 9B | 001F push ptr 3 + 56 | 0025 reg EDX becoming live + 4A | 0027 reg ECX becoming live + 0E | 002D reg ECX becoming dead + 10 | 002D reg EDX becoming dead + E0 | 002D pop 4 ptrs + F0 31 | 0036 reg ESI becoming dead + 38 | 0036 reg EDI becoming dead + FF | This function is important for CLR Devs, but very difficult for anyone else to make sense of it. You would usually come to use it if you suspect a gc heap @@ -1194,18 +1194,18 @@ To correctly specify explicitly implemented methods make sure to retrieve the method name from the metadata, or from the output of the "!dumpmt -md" command. For example: - public interface I1 - { - void M1(); - } - public class ExplicitItfImpl : I1 - { - ... - void I1.M1() // this method's name is 'I1.M1' - { ... } - } + public interface I1 + { + void M1(); + } + public class ExplicitItfImpl : I1 + { + ... + void I1.M1() // this method's name is 'I1.M1' + { ... } + } - !bpmd myapp.exe ExplicitItfImpl.I1.M1 + !bpmd myapp.exe ExplicitItfImpl.I1.M1 !bpmd works equally well with generic types. Adding a breakpoint on a generic @@ -1213,56 +1213,56 @@ type sets breakpoints on all already JIT-ted generic methods and sets a pending breakpoint for any instantiation that will be JIT-ted in the future. Example for generics: - Given the following two classes: + Given the following two classes: - class G3 - { - ... - public void F(T1 p1, T2 p2, T3 p3) - { ... } - } + class G3 + { + ... + public void F(T1 p1, T2 p2, T3 p3) + { ... } + } - public class G1 { - // static method - static public void G(W w) - { ... } - } + public class G1 { + // static method + static public void G(W w) + { ... } + } - One would issue the following commands to set breapoints on G3.F() and - G1.G(): + One would issue the following commands to set breapoints on G3.F() and + G1.G(): - !bpmd myapp.exe G3`3.F - !bpmd myapp.exe G1`1.G + !bpmd myapp.exe G3`3.F + !bpmd myapp.exe G1`1.G And for explicitly implemented methods on generic interfaces: - public interface IT1 - { - void M1(T t); - } + public interface IT1 + { + void M1(T t); + } - public class ExplicitItfImpl : IT1 - { - ... - void IT1.M1(U u) // this method's name is 'IT1.M1' - { ... } - } + public class ExplicitItfImpl : IT1 + { + ... + void IT1.M1(U u) // this method's name is 'IT1.M1' + { ... } + } - !bpmd bpmd.exe ExplicitItfImpl`1.IT1.M1 + !bpmd bpmd.exe ExplicitItfImpl`1.IT1.M1 Additional examples: - If IT1 and ExplicitItfImpl are types declared inside another class, - Outer, the bpmd command would become: + If IT1 and ExplicitItfImpl are types declared inside another class, + Outer, the bpmd command would become: - !bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1.M1 + !bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1.M1 - (note that the fully qualified type name for ExplicitItfImpl became - Outer+ExplicitItfImpl, using the '+' separator, while the method name - is Outer.IT1.M1, using a '.' as the separator) + (note that the fully qualified type name for ExplicitItfImpl became + Outer+ExplicitItfImpl, using the '+' separator, while the method name + is Outer.IT1.M1, using a '.' as the separator) - Furthermore, if the Outer class resides in a namespace, NS, the bpmd - command to use becomes: + Furthermore, if the Outer class resides in a namespace, NS, the bpmd + command to use becomes: - !bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1.M1 + !bpmd bpmd.exe NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1.M1 !bpmd does not accept offsets nor parameters in the method name. You can add an IL offset as an optional parameter seperate from the name. If there are overloaded @@ -1306,19 +1306,19 @@ have an object pointer, and can attempt to run "!DumpObj" on it. Here is output for a simple program: - 0:000> !eeheap -gc - Number of GC Heaps: 1 - generation 0 starts at 0x00a71018 - generation 1 starts at 0x00a7100c - generation 2 starts at 0x00a71000 - segment begin allocated size - 00a70000 00a71000 00a7e01c 0000d01c(53276) - Large object heap starts at 0x01a71000 - segment begin allocated size - 01a70000 01a71000 01a76000 0x00005000(20480) - Total Size 0x1201c(73756) - ------------------------------ - GC Heap Size 0x1201c(73756) + 0:000> !eeheap -gc + Number of GC Heaps: 1 + generation 0 starts at 0x00a71018 + generation 1 starts at 0x00a7100c + generation 2 starts at 0x00a71000 + segment begin allocated size + 00a70000 00a71000 00a7e01c 0000d01c(53276) + Large object heap starts at 0x01a71000 + segment begin allocated size + 01a70000 01a71000 01a76000 0x00005000(20480) + Total Size 0x1201c(73756) + ------------------------------ + GC Heap Size 0x1201c(73756) So the total size of the GC Heap is only 72K. On a large web server, with multiple processors, you can expect to see a GC Heap of 400MB or more. The @@ -1331,49 +1331,49 @@ The loader output lists various private heaps associated with AppDomains. It also lists heaps associated with the JIT compiler, and heaps associated with Modules. For example: - 0:000> !EEHeap -loader - Loader Heap: - -------------------------------------- - System Domain: 5e0662a0 - LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes. - HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes. - StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes. - Total size: 0x3000(12288)bytes - -------------------------------------- - Shared Domain: 5e066970 - LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes. - Wasted: 0x00001000 bytes. - HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes. - StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes. - Total size: 0x6000(24576)bytes - -------------------------------------- - Domain 1: 14f000 - LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes. - Wasted: 0x00001000 bytes. - HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes. - StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes. - Total size: 0x8000(32768)bytes - -------------------------------------- - Jit code heap: - Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes. - Total size: 0x2000(8192)bytes - -------------------------------------- - Module Thunk heaps: - Module 5ba22410: Size: 0x00000000 bytes. - Module 001c1320: Size: 0x00000000 bytes. - Module 001c03f0: Size: 0x00000000 bytes. - Module 001caa38: Size: 0x00000000 bytes. - Total size: 0x0(0)bytes - -------------------------------------- - Module Lookup Table heaps: - Module 5ba22410:Size: 0x00000000 bytes. - Module 001c1320:Size: 0x00000000 bytes. - Module 001c03f0:Size: 0x00000000 bytes. - Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes. - Total size: 0x2000(8192)bytes - -------------------------------------- - Total LoaderHeap size: 0x15000(86016)bytes - ======================================= + 0:000> !EEHeap -loader + Loader Heap: + -------------------------------------- + System Domain: 5e0662a0 + LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes. + HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes. + StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes. + Total size: 0x3000(12288)bytes + -------------------------------------- + Shared Domain: 5e066970 + LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes. + Wasted: 0x00001000 bytes. + HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes. + StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes. + Total size: 0x6000(24576)bytes + -------------------------------------- + Domain 1: 14f000 + LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes. + Wasted: 0x00001000 bytes. + HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes. + StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes. + Total size: 0x8000(32768)bytes + -------------------------------------- + Jit code heap: + Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes. + Total size: 0x2000(8192)bytes + -------------------------------------- + Module Thunk heaps: + Module 5ba22410: Size: 0x00000000 bytes. + Module 001c1320: Size: 0x00000000 bytes. + Module 001c03f0: Size: 0x00000000 bytes. + Module 001caa38: Size: 0x00000000 bytes. + Total size: 0x0(0)bytes + -------------------------------------- + Module Lookup Table heaps: + Module 5ba22410:Size: 0x00000000 bytes. + Module 001c1320:Size: 0x00000000 bytes. + Module 001c03f0:Size: 0x00000000 bytes. + Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes. + Total size: 0x2000(8192)bytes + -------------------------------------- + Total LoaderHeap size: 0x15000(86016)bytes + ======================================= By using !EEHeap to keep track of the growth of these private heaps, we are able to rule out or include them as a source of a memory leak. @@ -1386,21 +1386,21 @@ COMMAND: name2ee. This function allows you to turn a class name into a MethodTable and EEClass. It turns a method name into a MethodDesc. Here is an example for a method: - 0:000> !name2ee unittest.exe MainClass.Main - Module: 001caa38 - Token: 0x0600000d - MethodDesc: 00902f40 - Name: MainClass.Main() - JITTED Code Address: 03ef00b8 + 0:000> !name2ee unittest.exe MainClass.Main + Module: 001caa38 + Token: 0x0600000d + MethodDesc: 00902f40 + Name: MainClass.Main() + JITTED Code Address: 03ef00b8 and for a class: - 0:000> !name2ee unittest!MainClass - Module: 001caa38 - Token: 0x02000005 - MethodTable: 009032d8 - EEClass: 03ee1424 - Name: MainClass + 0:000> !name2ee unittest!MainClass + Module: 001caa38 + Token: 0x02000005 + MethodTable: 009032d8 + EEClass: 03ee1424 + Name: MainClass The module you are "browsing" with Name2EE needs to be loaded in the process. To get a type name exactly right, first browse the module with ILDASM. You @@ -1425,12 +1425,12 @@ price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null For this kind of module, simply use price as the module name: - 0:044> !name2ee price Price - Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null) - Token: 0x02000002 - MethodTable: 11a47ae0 - EEClass: 11a538c8 - Name: Price + 0:044> !name2ee price Price + Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null) + Token: 0x02000002 + MethodTable: 11a47ae0 + EEClass: 11a538c8 + Name: Price Where are we getting these module names from? Run !DumpDomain to see a list of all loaded modules in all domains. And remember that you can browse all the @@ -1478,30 +1478,30 @@ This is a deadlock situation, as Thread A could take r1, and Thread B r2, leaving both threads with no option but to wait forever in the second lock statement. !SyncBlk will detect this with the following output: - 0:003> !syncblk - Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner - 238 001e40ec 3 1 001e4e60 e04 3 00a7a194 Resource - 239 001e4124 3 1 001e5980 ab8 4 00a7a1a4 Resource + 0:003> !syncblk + Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner + 238 001e40ec 3 1 001e4e60 e04 3 00a7a194 Resource + 239 001e4124 3 1 001e5980 ab8 4 00a7a1a4 Resource It means that Thread e04 owns object 00a7a194, and Thread ab8 owns object 00a7a1a4. Combine that information with the call stacks of the deadlock: (threads 3 and 4 have similar output) - 0:003> k - ChildEBP RetAddr - 0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4 - 0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc - 0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c - 0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156 - 0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360 - 0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb - 0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d - 0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132 - 0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1 - 0404f09c 5d767880 clr!AwareLock::Contention+0x483 - 0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0 - 0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79 - ... + 0:003> k + ChildEBP RetAddr + 0404ea04 77f5c524 SharedUserData!SystemCallStub+0x4 + 0404ea08 77e75ee0 ntdll!NtWaitForMultipleObjects+0xc + 0404eaa4 5d9de9d6 KERNEL32!WaitForMultipleObjectsEx+0x12c + 0404eb38 5d9def80 clr!Thread::DoAppropriateAptStateWait+0x156 + 0404ecc4 5d9dd8bb clr!Thread::DoAppropriateWaitWorker+0x360 + 0404ed20 5da628dd clr!Thread::DoAppropriateWait+0xbb + 0404ede4 5da4e2e2 clr!CLREvent::Wait+0x29d + 0404ee70 5da4dd41 clr!AwareLock::EnterEpilog+0x132 + 0404ef34 5da4efa3 clr!AwareLock::Enter+0x2c1 + 0404f09c 5d767880 clr!AwareLock::Contention+0x483 + 0404f1c4 03f00229 clr!JITutil_MonContention+0x2c0 + 0404f1f4 5b6ef077 image00400000!Worker.Work()+0x79 + ... By looking at the code corresponding to Worker.Work()+0x79 (run "!u 03f00229"), you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which @@ -1541,14 +1541,14 @@ COMMAND: dumpmd. This command lists information about a MethodDesc. You can use !IP2MD to turn a code address in a managed function into a MethodDesc: - 0:000> !dumpmd 902f40 - Method Name: Mainy.Main() - Class: 03ee1424 - MethodTable: 009032d8 - mdToken: 0600000d - Module: 001caa78 - IsJitted: yes - CodeAddr: 03ef00b8 + 0:000> !dumpmd 902f40 + Method Name: Mainy.Main() + Class: 03ee1424 + MethodTable: 009032d8 + mdToken: 0600000d + Module: 001caa78 + IsJitted: yes + CodeAddr: 03ef00b8 If IsJitted is "yes," you can run !U on the CodeAddr pointer to see a disassembly of the JITTED code. You can also call !DumpClass, !DumpMT, @@ -1561,18 +1561,18 @@ COMMAND: token2ee. This function allows you to turn a metadata token into a MethodTable or MethodDesc. Here is an example showing class tokens being resolved: - 0:000> !token2ee unittest.exe 02000003 - Module: 001caa38 - Token: 0x02000003 - MethodTable: 0090375c - EEClass: 03ee1ae0 - Name: Bank - 0:000> !token2ee image00400000 02000004 - Module: 001caa38 - Token: 0x02000004 - MethodTable: 009038ec - EEClass: 03ee1b84 - Name: Customer + 0:000> !token2ee unittest.exe 02000003 + Module: 001caa38 + Token: 0x02000003 + MethodTable: 0090375c + EEClass: 03ee1ae0 + Name: Bank + 0:000> !token2ee image00400000 02000004 + Module: 001caa38 + Token: 0x02000004 + MethodTable: 009038ec + EEClass: 03ee1b84 + Name: Customer The module you are "browsing" with Token2EE needs to be loaded in the process. This function doesn't see much use, especially since a tool like ILDASM can @@ -1603,28 +1603,28 @@ COMMAND: dumpmodule. You can get a Module address from !DumpDomain, !DumpAssembly and other functions. Here is sample output: - 0:000> !DumpModule 1caa50 - Name: C:\pub\unittest.exe - Attributes: PEFile - Assembly: 001ca248 - LoaderHeap: 001cab3c - TypeDefToMethodTableMap: 03ec0010 - TypeRefToMethodTableMap: 03ec0024 - MethodDefToDescMap: 03ec0064 - FieldDefToDescMap: 03ec00a4 - MemberRefToDescMap: 03ec00e8 - FileReferencesMap: 03ec0128 - AssemblyReferencesMap: 03ec012c - MetaData start address: 00402230 (1888 bytes) + 0:000> !DumpModule 1caa50 + Name: C:\pub\unittest.exe + Attributes: PEFile + Assembly: 001ca248 + LoaderHeap: 001cab3c + TypeDefToMethodTableMap: 03ec0010 + TypeRefToMethodTableMap: 03ec0024 + MethodDefToDescMap: 03ec0064 + FieldDefToDescMap: 03ec00a4 + MemberRefToDescMap: 03ec00e8 + FileReferencesMap: 03ec0128 + AssemblyReferencesMap: 03ec012c + MetaData start address: 00402230 (1888 bytes) The Maps listed map metadata tokens to CLR data structures. Without going into too much detail, you can examine memory at those addresses to find the appropriate structures. For example, the TypeDefToMethodTableMap above can be examined: - 0:000> dd 3ec0010 - 03ec0010 00000000 00000000 0090320c 0090375c - 03ec0020 009038ec ... + 0:000> dd 3ec0010 + 03ec0010 00000000 00000000 0090320c 0090375c + 03ec0020 009038ec ... This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You can run !DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token @@ -1633,28 +1633,28 @@ and maps it to a MethodDesc, which can be passed to !DumpMD. There is a new option "-mt", which will display the types defined in a module, and the types referenced by the module. For example: - 0:000> !dumpmodule -mt 1aa580 - Name: C:\pub\unittest.exe - ...... - MetaData start address: 0040220c (1696 bytes) + 0:000> !dumpmodule -mt 1aa580 + Name: C:\pub\unittest.exe + ...... + MetaData start address: 0040220c (1696 bytes) - Types defined in this module + Types defined in this module - MT TypeDef Name - -------------------------------------------------------------------------- - 030d115c 0x02000002 Funny - 030d1228 0x02000003 Mainy + MT TypeDef Name + -------------------------------------------------------------------------- + 030d115c 0x02000002 Funny + 030d1228 0x02000003 Mainy - Types referenced in this module + Types referenced in this module - MT TypeRef Name - -------------------------------------------------------------------------- - 030b6420 0x01000001 System.ValueType - 030b5cb0 0x01000002 System.Object - 030fceb4 0x01000003 System.Exception - 0334e374 0x0100000c System.Console - 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle - 0336a048 0x0100000f System.GC + MT TypeRef Name + -------------------------------------------------------------------------- + 030b6420 0x01000001 System.ValueType + 030b5cb0 0x01000002 System.Object + 030fceb4 0x01000003 System.Exception + 0334e374 0x0100000c System.Console + 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle + 0336a048 0x0100000f System.GC \\ @@ -1671,12 +1671,12 @@ COMMAND: dumpassembly. Example output: - 0:000> !dumpassembly 1ca248 - Parent Domain: 0014f000 - Name: C:\pub\unittest.exe - ClassLoader: 001ca060 - Module Name - 001caa50 C:\pub\unittest.exe + 0:000> !dumpassembly 1ca248 + Parent Domain: 0014f000 + Name: C:\pub\unittest.exe + ClassLoader: 001ca060 + Module Name + 001caa50 C:\pub\unittest.exe An assembly can consist of multiple modules, and those will be listed. You can get an Assembly address from the output of !DumpDomain. @@ -1688,18 +1688,18 @@ COMMAND: dumpruntimetypes. !DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and prints the type name and MethodTable they refer too. Sample output: - Address Domain MT Type Name - ------------------------------------------------------------------------------ - a515f4 14a740 5baf8d28 System.TypedReference - a51608 14a740 5bb05764 System.Globalization.BaseInfoTable - a51958 14a740 5bb05b24 System.Globalization.CultureInfo - a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly - a51de0 14a740 5bb069c8 System.Globalization.TextInfo - a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource - a56bbc 14a740 5baf7248 System.Int32 - a56bd0 14a740 5baf3fdc System.String - a56cfc 14a740 5baf36a4 System.ValueType - ... + Address Domain MT Type Name + ------------------------------------------------------------------------------ + a515f4 14a740 5baf8d28 System.TypedReference + a51608 14a740 5bb05764 System.Globalization.BaseInfoTable + a51958 14a740 5bb05b24 System.Globalization.CultureInfo + a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly + a51de0 14a740 5bb069c8 System.Globalization.TextInfo + a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource + a56bbc 14a740 5baf7248 System.Int32 + a56bd0 14a740 5baf3fdc System.String + a56cfc 14a740 5baf36a4 System.ValueType + ... This command will print a "?" in the domain column if the type is loaded into multiple AppDomains. For example: @@ -1890,22 +1890,22 @@ signs of corruption. It walks objects one by one in a pattern like this: If an error is found, !VerifyHeap will report it. I'll take a perfectly good object and corrupt it: - 0:000> !DumpObj a79d40 - Name: Customer - MethodTable: 009038ec - EEClass: 03ee1b84 - Size: 20(0x14) bytes - (C:\pub\unittest.exe) - Fields: - MT Field Offset Type Attr Value Name - 009038ec 4000008 4 CLASS instance 00a79ce4 name - 009038ec 4000009 8 CLASS instance 00a79d2c bank - 009038ec 400000a c System.Boolean instance 1 valid - - 0:000> ed a79d40+4 01 (change the name field to the bogus pointer value 1) - 0:000> !VerifyHeap - object 01ee60dc: bad member 00000003 at 01EE6168 - Last good object: 01EE60C4. + 0:000> !DumpObj a79d40 + Name: Customer + MethodTable: 009038ec + EEClass: 03ee1b84 + Size: 20(0x14) bytes + (C:\pub\unittest.exe) + Fields: + MT Field Offset Type Attr Value Name + 009038ec 4000008 4 CLASS instance 00a79ce4 name + 009038ec 4000009 8 CLASS instance 00a79d2c bank + 009038ec 400000a c System.Boolean instance 1 valid + + 0:000> ed a79d40+4 01 (change the name field to the bogus pointer value 1) + 0:000> !VerifyHeap + object 01ee60dc: bad member 00000003 at 01EE6168 + Last good object: 01EE60C4. If this gc heap corruption exists, there is a serious bug in your own code or in the CLR. In user code, an error in constructing PInvoke calls can cause @@ -1921,11 +1921,11 @@ COMMAND: verifyobj. !VerifyObj is a diagnostic tool that checks the object that is passed as an argument for signs of corruption. - 0:002> !verifyobj 028000ec - object 0x28000ec does not have valid method table + 0:002> !verifyobj 028000ec + object 0x28000ec does not have valid method table - 0:002> !verifyobj 0680017c - object 0x680017c: bad member 00000001 at 06800184 + 0:002> !verifyobj 0680017c + object 0x680017c: bad member 00000001 at 06800184 \\ @@ -1952,18 +1952,18 @@ The process of answering the question would go something like this: 1. Find out the generation of the object of interest using the !GCWhere command, say it is gen 1: - !GCWhere + !GCWhere 2. Instruct the runtime to stop the next time it collects that generation using the !FindRoots command: - !FindRoots -gen 1 - g + !FindRoots -gen 1 + g 3. When the next GC starts, and has proceeded past the mark phase a CLR notification will cause a break in the debugger: - (fd0.ec4): CLR notification exception - code e0444143 (first chance) - CLR notification: GC - end of mark phase. - Condemned generation: 1. + (fd0.ec4): CLR notification exception - code e0444143 (first chance) + CLR notification: GC - end of mark phase. + Condemned generation: 1. 4. Now we can use the !FindRoots to find out the cross generational references to the object of interest. In other words, even if the @@ -1971,9 +1971,9 @@ object is not referenced by any "proper" root it may still be referenced by an older object (from an older generation), from a generation that has not yet been scheduled for collection. At this point !FindRoots will search those older generations too, and report those roots. - 0:002> !findroots 06808094 - older generations::Root: 068012f8(AAA.Test+a)-> - 06808094(AAA.Test+b) + 0:002> !findroots 06808094 + older generations::Root: 068012f8(AAA.Test+a)-> + 06808094(AAA.Test+b) \\ @@ -1988,32 +1988,32 @@ GC heap that are not rooted anymore. Sample output: - 0:002> !heapstat - Heap Gen0 Gen1 Gen2 LOH - Heap0 177904 12 306956 8784 - Heap1 159652 12 12 16 - Total 337556 24 306968 8800 - - Free space: Percentage - Heap0 28 12 12 64 SOH: 0%% LOH: 0%% - Heap1 104 12 12 16 SOH: 0%% LOH:100%% - Total 132 24 24 80 - - 0:002> !heapstat -inclUnrooted - Heap Gen0 Gen1 Gen2 LOH - Heap0 177904 12 306956 8784 - Heap1 159652 12 12 16 - Total 337556 24 306968 8800 - - Free space: Percentage - Heap0 28 12 12 64 SOH: 0%% LOH: 0%% - Heap1 104 12 12 16 SOH: 0%% LOH:100%% - Total 132 24 24 80 - - Unrooted objects: Percentage - Heap0 152212 0 306196 0 SOH: 94%% LOH: 0%% - Heap1 155704 0 0 0 SOH: 97%% LOH: 0%% - Total 307916 0 306196 0 + 0:002> !heapstat + Heap Gen0 Gen1 Gen2 LOH + Heap0 177904 12 306956 8784 + Heap1 159652 12 12 16 + Total 337556 24 306968 8800 + + Free space: Percentage + Heap0 28 12 12 64 SOH: 0%% LOH: 0%% + Heap1 104 12 12 16 SOH: 0%% LOH:100%% + Total 132 24 24 80 + + 0:002> !heapstat -inclUnrooted + Heap Gen0 Gen1 Gen2 LOH + Heap0 177904 12 306956 8784 + Heap1 159652 12 12 16 + Total 337556 24 306968 8800 + + Free space: Percentage + Heap0 28 12 12 64 SOH: 0%% LOH: 0%% + Heap1 104 12 12 16 SOH: 0%% LOH:100%% + Total 132 24 24 80 + + Unrooted objects: Percentage + Heap0 152212 0 306196 0 SOH: 94%% LOH: 0%% + Heap1 155704 0 0 0 SOH: 97%% LOH: 0%% + Total 307916 0 306196 0 The percentage column contains a breakout of free or unrooted bytes to total bytes. @@ -2083,16 +2083,16 @@ COMMAND: gcwhere. !GCWhere displays the location in the GC heap of the argument passed in. - 0:002> !GCWhere 02800038 - Address Gen Heap segment begin allocated size - 02800038 2 0 02800000 02800038 0282b740 12 + 0:002> !GCWhere 02800038 + Address Gen Heap segment begin allocated size + 02800038 2 0 02800000 02800038 0282b740 12 When the argument lies in the managed heap, but is not a valid *object* address the "size" is displayed as 0: - 0:002> !GCWhere 0280003c - Address Gen Heap segment begin allocated size - 0280003c 2 0 02800000 02800038 0282b740 0 + 0:002> !GCWhere 0280003c + Address Gen Heap segment begin allocated size + 0280003c 2 0 02800000 02800038 0282b740 0 \\ @@ -2107,27 +2107,27 @@ The command looks for the address in the GC heap that looks like a valid beginning of a managed object (based on a valid method table) and the object following the argument address. - 0:002> !ListNearObj 028000ec - Before: 0x28000a4 72 (0x48 ) System.StackOverflowException - After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException - Heap local consistency confirmed. + 0:002> !ListNearObj 028000ec + Before: 0x28000a4 72 (0x48 ) System.StackOverflowException + After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException + Heap local consistency confirmed. - 0:002> !ListNearObj 028000f0 - Before: 0x28000ec 72 (0x48 ) System.ExecutionEngineException - After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException - Heap local consistency confirmed. + 0:002> !ListNearObj 028000f0 + Before: 0x28000ec 72 (0x48 ) System.ExecutionEngineException + After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException + Heap local consistency confirmed. The command considers the heap as "locally consistent" if: - prev_obj_addr + prev_obj_size = arg_addr && arg_obj + arg_size = next_obj_addr + prev_obj_addr + prev_obj_size = arg_addr && arg_obj + arg_size = next_obj_addr OR - prev_obj_addr + prev_obj_size = next_obj_addr + prev_obj_addr + prev_obj_size = next_obj_addr When the condition is not satisfied: - 0:002> !lno 028000ec - Before: 0x28000a4 72 (0x48 ) System.StackOverflowException - After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException - Heap local consistency not confirmed. + 0:002> !lno 028000ec + Before: 0x28000a4 72 (0x48 ) System.StackOverflowException + After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException + Heap local consistency not confirmed. \\ @@ -2143,10 +2143,10 @@ in the current directory is created. The optional argument addr allows one to specify a stress log other then the default one. - 0:000> !DumpLog - Attempting to dump Stress log to file 'StressLog.txt' - ................. - SUCCESS: Stress log dumped + 0:000> !DumpLog + Attempting to dump Stress log to file 'StressLog.txt' + ................. + SUCCESS: Stress log dumped To turn on the stress log, set the following environment variables before starting the .NET Core app: @@ -2204,27 +2204,27 @@ The log facilities are defined as follows: Here is some sample output: - 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering. - Thread state = a030 + 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering. + Thread state = a030 - 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2, - collect_classes = 0) ==========={ + 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2, + collect_classes = 0) ==========={ - 3560 9.981125826 : `GC` Segment mem 00C61000 alloc - = 00D071F0 used 00D09254 committed 00D17000 + 3560 9.981125826 : `GC` Segment mem 00C61000 alloc + = 00D071F0 used 00D09254 committed 00D17000 - 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000 - ] cur = 00000000 + 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000 + ] cur = 00000000 - 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000 - ] cur = 00000000 + 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000 + ] cur = 00000000 - 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000 - ] cur = 00000000 + 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000 + ] cur = 00000000 - 3560 9.981124963 : `GC` GC Heap 00000000 + 3560 9.981124963 : `GC` GC Heap 00000000 - 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0) + 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0) The first column is the OS thread ID for the thread appending to the log, the second column is the timestamp, the third is the facility category for the @@ -2241,10 +2241,10 @@ COMMAND: findappdomain. !FindAppDomain will attempt to resolve the AppDomain of an object. For example, using an Object Pointer from the output of !DumpStackObjects: - 0:000> !findappdomain 00a79d98 - AppDomain: 0014f000 - Name: unittest.exe - ID: 1 + 0:000> !findappdomain 00a79d98 + AppDomain: 0014f000 + Name: unittest.exe + ID: 1 You can find out more about the AppDomain with the !DumpDomain command. Not every object has enough clues about it's origin to determine the AppDomain. @@ -2262,24 +2262,24 @@ binary to a file, so you can disassemble the code and browse types with ILDASM. The base address of an image can be found with the "LM" debugger command: - 0:000> lm - start end module name - 00400000 00408000 image00400000 (deferred) - 10200000 102ac000 MSVCR80D (deferred) - 5a000000 5a0b1000 mscoree (deferred) - 5a140000 5a29e000 clrjit (deferred) - 5b660000 5c440000 mscorlib_dll (deferred) - 5d1d0000 5e13c000 clr (deferred) - ... + 0:000> lm + start end module name + 00400000 00408000 image00400000 (deferred) + 10200000 102ac000 MSVCR80D (deferred) + 5a000000 5a0b1000 mscoree (deferred) + 5a140000 5a29e000 clrjit (deferred) + 5b660000 5c440000 mscorlib_dll (deferred) + 5d1d0000 5e13c000 clr (deferred) + ... If I wanted to save a copy of coreclr.dll, I could run: - 0:000> !SaveModule 5d1d0000 c:\pub\out.tmp - 4 sections in file - section 0 - VA=1000, VASize=e82da9, FileAddr=400, FileSize=e82e00 - section 1 - VA=e84000, VASize=24d24, FileAddr=e83200, FileSize=ec00 - section 2 - VA=ea9000, VASize=5a8, FileAddr=e91e00, FileSize=600 - section 3 - VA=eaa000, VASize=c183c, FileAddr=e92400, FileSize=c1a00 + 0:000> !SaveModule 5d1d0000 c:\pub\out.tmp + 4 sections in file + section 0 - VA=1000, VASize=e82da9, FileAddr=400, FileSize=e82e00 + section 1 - VA=e84000, VASize=24d24, FileAddr=e83200, FileSize=ec00 + section 2 - VA=ea9000, VASize=5a8, FileAddr=e91e00, FileSize=600 + section 3 - VA=eaa000, VASize=c183c, FileAddr=e92400, FileSize=c1a00 The diagnostic output indicates that the operation was successful. If c:\pub\out.tmp already exists, it will be overwritten. @@ -2394,15 +2394,15 @@ COMMAND: vmmap. !VMMap traverses the virtual address space and lists the type of protection applied to each region. Sample output: - 0:000> !VMMap - Start Stop Length AllocProtect Protect State Type - 00000000-0000ffff 00010000 NA Free - 00010000-00011fff 00002000 RdWr RdWr Commit Private - 00012000-0001ffff 0000e000 NA Free - 00020000-00020fff 00001000 RdWr RdWr Commit Private - 00021000-0002ffff 0000f000 NA Free - 00030000-00030fff 00001000 RdWr Reserve Private - ... + 0:000> !VMMap + Start Stop Length AllocProtect Protect State Type + 00000000-0000ffff 00010000 NA Free + 00010000-00011fff 00002000 RdWr RdWr Commit Private + 00012000-0001ffff 0000e000 NA Free + 00020000-00020fff 00001000 RdWr RdWr Commit Private + 00021000-0002ffff 0000f000 NA Free + 00030000-00030fff 00001000 RdWr Reserve Private + ... \\ COMMAND: vmstat. @@ -2412,20 +2412,20 @@ Provides a summary view of the virtual address space, ordered by each type of protection applied to that memory (free, reserved, committed, private, mapped, image). The TOTAL column is (AVERAGE * BLK COUNT). Sample output below: - 0:000> !VMStat - ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~ ~~~~~ - TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL - Free: - Small 4,096 65,536 48,393 27 1,306,611 - Medium 139,264 528,384 337,920 4 1,351,680 - Large 6,303,744 974,778,368 169,089,706 12 2,029,076,472 - Summary 4,096 974,778,368 47,249,646 43 2,031,734,778 + 0:000> !VMStat + ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~ ~~~~~ + TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL + Free: + Small 4,096 65,536 48,393 27 1,306,611 + Medium 139,264 528,384 337,920 4 1,351,680 + Large 6,303,744 974,778,368 169,089,706 12 2,029,076,472 + Summary 4,096 974,778,368 47,249,646 43 2,031,734,778 - Reserve: - Small 4,096 65,536 43,957 41 1,802,237 - Medium 249,856 1,019,904 521,557 6 3,129,342 - Large 2,461,696 16,703,488 11,956,224 3 35,868,672 - Summary 4,096 16,703,488 816,005 50 40,800,250 + Reserve: + Small 4,096 65,536 43,957 41 1,802,237 + Medium 249,856 1,019,904 521,557 6 3,129,342 + Large 2,461,696 16,703,488 11,956,224 3 35,868,672 + Summary 4,096 16,703,488 816,005 50 40,800,250 \\ @@ -2445,24 +2445,24 @@ HistInit command. Sample output: - 0:001> !HistInit - Attempting to read Stress log - STRESS LOG: - facilitiesToLog = 0xffffffff - levelToLog = 6 - MaxLogSizePerThread = 0x10000 (65536) - MaxTotalLogSize = 0x1000000 (16777216) - CurrentTotalLogChunk = 9 - ThreadsWithLogs = 3 - Clock frequency = 3.392 GHz - Start time 15:26:31 - Last message time 15:26:56 - Total elapsed time 25.077 sec - ..................................... - ---------------------------- 2407 total entries ----------------------------- + 0:001> !HistInit + Attempting to read Stress log + STRESS LOG: + facilitiesToLog = 0xffffffff + levelToLog = 6 + MaxLogSizePerThread = 0x10000 (65536) + MaxTotalLogSize = 0x1000000 (16777216) + CurrentTotalLogChunk = 9 + ThreadsWithLogs = 3 + Clock frequency = 3.392 GHz + Start time 15:26:31 + Last message time 15:26:56 + Total elapsed time 25.077 sec + ..................................... + ---------------------------- 2407 total entries ----------------------------- - SUCCESS: GCHist structures initialized + SUCCESS: GCHist structures initialized \\ @@ -2473,17 +2473,17 @@ To examine log entries related to an object whose present address is known one would use this command. The output of this command contains all entries that reference the object: - 0:003> !HistObjFind 028970d4 - GCCount Object Message - --------------------------------------------------------- - 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) - 2296 028970d4 Relocation NEWVALUE for root 00223fc4 - 2296 028970d4 Relocation NEWVALUE for root 01e411b8 - ... - 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) - 2295 028970d4 Relocation NEWVALUE for root 00223fc4 - 2295 028970d4 Relocation NEWVALUE for root 01e411b8 - ... + 0:003> !HistObjFind 028970d4 + GCCount Object Message + --------------------------------------------------------- + 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) + 2296 028970d4 Relocation NEWVALUE for root 00223fc4 + 2296 028970d4 Relocation NEWVALUE for root 01e411b8 + ... + 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) + 2295 028970d4 Relocation NEWVALUE for root 00223fc4 + 2295 028970d4 Relocation NEWVALUE for root 01e411b8 + ... \\ @@ -2496,23 +2496,23 @@ an object through the GCs. HistRoot provides information related to both promotions and relocations of the root specified as the argument. - 0:003> !HistRoot 01e411b8 - GCCount Value MT Promoted? Notes - --------------------------------------------------------- - 2296 028970d4 5b6c5cd8 yes - 2295 028970d4 5b6c5cd8 yes - 2294 028970d4 5b6c5cd8 yes - 2293 028970d4 5b6c5cd8 yes - 2292 028970d4 5b6c5cd8 yes - 2291 028970d4 5b6c5cd8 yes - 2290 028970d4 5b6c5cd8 yes - 2289 028970d4 5b6c5cd8 yes - 2288 028970d4 5b6c5cd8 yes - 2287 028970d4 5b6c5cd8 yes - 2286 028970d4 5b6c5cd8 yes - 2285 028970d4 5b6c5cd8 yes - 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs - ... + 0:003> !HistRoot 01e411b8 + GCCount Value MT Promoted? Notes + --------------------------------------------------------- + 2296 028970d4 5b6c5cd8 yes + 2295 028970d4 5b6c5cd8 yes + 2294 028970d4 5b6c5cd8 yes + 2293 028970d4 5b6c5cd8 yes + 2292 028970d4 5b6c5cd8 yes + 2291 028970d4 5b6c5cd8 yes + 2290 028970d4 5b6c5cd8 yes + 2289 028970d4 5b6c5cd8 yes + 2288 028970d4 5b6c5cd8 yes + 2287 028970d4 5b6c5cd8 yes + 2286 028970d4 5b6c5cd8 yes + 2285 028970d4 5b6c5cd8 yes + 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs + ... \\ @@ -2523,29 +2523,29 @@ This command examines all stress log relocation records and displays the chain of GC relocations that may have led to the address passed in as an argument. Conceptually the output is: - GenN obj_address root1, root2, root3, - GenN-1 prev_obj_addr root1, root2, - GenN-2 prev_prev_oa root1, root4, - ... + GenN obj_address root1, root2, root3, + GenN-1 prev_obj_addr root1, root2, + GenN-2 prev_prev_oa root1, root4, + ... Sample output: - 0:003> !HistObj 028970d4 - GCCount Object Roots - --------------------------------------------------------- - 2296 028970d4 00223fc4, 01e411b8, - 2295 028970d4 00223fc4, 01e411b8, - 2294 028970d4 00223fc4, 01e411b8, - 2293 028970d4 00223fc4, 01e411b8, - 2292 028970d4 00223fc4, 01e411b8, - 2291 028970d4 00223fc4, 01e411b8, - 2290 028970d4 00223fc4, 01e411b8, - 2289 028970d4 00223fc4, 01e411b8, - 2288 028970d4 00223fc4, 01e411b8, - 2287 028970d4 00223fc4, 01e411b8, - 2286 028970d4 00223fc4, 01e411b8, - 2285 028970d4 00223fc4, 01e411b8, - 322 028970d4 01e411b8, - 0 028970d4 + 0:003> !HistObj 028970d4 + GCCount Object Roots + --------------------------------------------------------- + 2296 028970d4 00223fc4, 01e411b8, + 2295 028970d4 00223fc4, 01e411b8, + 2294 028970d4 00223fc4, 01e411b8, + 2293 028970d4 00223fc4, 01e411b8, + 2292 028970d4 00223fc4, 01e411b8, + 2291 028970d4 00223fc4, 01e411b8, + 2290 028970d4 00223fc4, 01e411b8, + 2289 028970d4 00223fc4, 01e411b8, + 2288 028970d4 00223fc4, 01e411b8, + 2287 028970d4 00223fc4, 01e411b8, + 2286 028970d4 00223fc4, 01e411b8, + 2285 028970d4 00223fc4, 01e411b8, + 322 028970d4 01e411b8, + 0 028970d4 \\ @@ -2556,8 +2556,8 @@ This command releases any resources used by the Hist-family of commands. Generally there's no need to call this explicitly, as each HistInit will first cleanup the previous resources. - 0:003> !HistClear - Completed successfully. + 0:003> !HistClear + Completed successfully. \\ @@ -2633,7 +2633,7 @@ COMMAND: sosstatus. -desktop - switch to the desktop runtime if loaded. -netcore - switch to the .NET Core runtime if loaded. --reset - reset all the cached internal SOS state. +-reset - reset all the cached internal SOS state. Display internal SOS status, reset the internal cached state, or change between desktop or netcore runtimes when both are loaded in the process or dump. diff --git a/src/SOS/Strike/sosdocsunix.txt b/src/SOS/Strike/sosdocsunix.txt index e2200387d..196a45be2 100644 --- a/src/SOS/Strike/sosdocsunix.txt +++ b/src/SOS/Strike/sosdocsunix.txt @@ -39,7 +39,7 @@ Examining CLR data structures Diagnostic Utilities DumpDomain (dumpdomain) VerifyHeap EEHeap (eeheap) FindAppDomain Name2EE (name2ee) DumpLog (dumplog) -SyncBlk (syncblk) +SyncBlk (syncblk) SuppressJitOptimization DumpMT (dumpmt) DumpClass (dumpclass) DumpMD (dumpmd) @@ -84,10 +84,10 @@ you can now set a breakpoint on Main with "bpmd". (lldb) bpmd Foo.dll Program.Main >> I got the following error message. Now what? - - (lldb) sos DumpStackObjects - The coreclr module is not loaded yet in the target process - (lldb) + + (lldb) sos DumpStackObjects + The coreclr module is not loaded yet in the target process + (lldb) This means that the clr is not loaded yet, or has been unloaded. You need to wait until your managed program is running in order to use these commands. If @@ -110,16 +110,16 @@ the size. You might find an object pointer by running DumpStackObjects and choosing from the resultant list. Here is a simple object: - (lldb) dumpobj a79d40 - Name: Customer - MethodTable: 009038ec - EEClass: 03ee1b84 - Size: 20(0x14) bytes - (/home/user/pub/unittest) - Fields: - MT Field Offset Type VT Attr Value Name - 009038ec 4000008 4 Customer 0 instance 00a79ce4 name - 009038ec 4000009 8 Bank 0 instance 00a79d2c bank + (lldb) dumpobj a79d40 + Name: Customer + MethodTable: 009038ec + EEClass: 03ee1b84 + Size: 20(0x14) bytes + (/home/user/pub/unittest) + Fields: + MT Field Offset Type VT Attr Value Name + 009038ec 4000008 4 Customer 0 instance 00a79ce4 name + 009038ec 4000009 8 Bank 0 instance 00a79d2c bank Note that fields of type Customer and Bank are themselves objects, and you can run DumpObj on them too. You could look at the field directly in memory using @@ -142,11 +142,11 @@ The arguments in detail: COMMAND: dumparray. DumpArray - [-start ] - [-length ] - [-details] - [-nofields] - + [-start ] + [-length ] + [-details] + [-nofields] + This command allows you to examine elements of an array object. The arguments in detail: @@ -162,46 +162,46 @@ The arguments in detail: Example output: - (lldb) sos DumpArray -start 2 -length 3 -details 00ad28d0 - Name: Value[] - MethodTable: 03e41044 - EEClass: 03e40fc0 - Size: 132(0x84) bytes - Array: Rank 1, Number of elements 10, Type VALUETYPE - Element Type: Value - [2] 00ad28f0 - Name: Value - MethodTable 03e40f4c - EEClass: 03ef1698 - Size: 20(0x14) bytes - (/home/user/bugs/225271/arraytest) - Fields: - MT Field Offset Type Attr Value Name - 5b9a628c 4000001 0 System.Int32 instance 2 x - 5b9a628c 4000002 4 System.Int32 instance 4 y - 5b9a628c 4000003 8 System.Int32 instance 6 z - [3] 00ad28fc - Name: Value - MethodTable 03e40f4c - EEClass: 03ef1698 - Size: 20(0x14) bytes - (/home/user/bugs/225271/arraytest) - Fields: - MT Field Offset Type Attr Value Name - 5b9a628c 4000001 0 System.Int32 instance 3 x - 5b9a628c 4000002 4 System.Int32 instance 6 y - 5b9a628c 4000003 8 System.Int32 instance 9 z - [4] 00ad2908 - Name: Value - MethodTable 03e40f4c - EEClass: 03ef1698 - Size: 20(0x14) bytes - (/home/user/bugs/225271/arraytest.exe) - Fields: - MT Field Offset Type Attr Value Name - 5b9a628c 4000001 0 System.Int32 instance 4 x - 5b9a628c 4000002 4 System.Int32 instance 8 y - 5b9a628c 4000003 8 System.Int32 instance 12 z + (lldb) sos DumpArray -start 2 -length 3 -details 00ad28d0 + Name: Value[] + MethodTable: 03e41044 + EEClass: 03e40fc0 + Size: 132(0x84) bytes + Array: Rank 1, Number of elements 10, Type VALUETYPE + Element Type: Value + [2] 00ad28f0 + Name: Value + MethodTable 03e40f4c + EEClass: 03ef1698 + Size: 20(0x14) bytes + (/home/user/bugs/225271/arraytest) + Fields: + MT Field Offset Type Attr Value Name + 5b9a628c 4000001 0 System.Int32 instance 2 x + 5b9a628c 4000002 4 System.Int32 instance 4 y + 5b9a628c 4000003 8 System.Int32 instance 6 z + [3] 00ad28fc + Name: Value + MethodTable 03e40f4c + EEClass: 03ef1698 + Size: 20(0x14) bytes + (/home/user/bugs/225271/arraytest) + Fields: + MT Field Offset Type Attr Value Name + 5b9a628c 4000001 0 System.Int32 instance 3 x + 5b9a628c 4000002 4 System.Int32 instance 6 y + 5b9a628c 4000003 8 System.Int32 instance 9 z + [4] 00ad2908 + Name: Value + MethodTable 03e40f4c + EEClass: 03ef1698 + Size: 20(0x14) bytes + (/home/user/bugs/225271/arraytest.exe) + Fields: + MT Field Offset Type Attr Value Name + 5b9a628c 4000001 0 System.Int32 instance 4 x + 5b9a628c 4000002 4 System.Int32 instance 8 y + 5b9a628c 4000003 8 System.Int32 instance 12 z \\ COMMAND: dumpasync. @@ -274,26 +274,26 @@ fragmentation in the GC heap. When called without options, the output is first a list of objects in the heap, followed by a report listing all the types found, their size and number: - (lldb) dumpheap - Address MT Size - 00a71000 0015cde8 12 Free - 00a7100c 0015cde8 12 Free - 00a71018 0015cde8 12 Free - 00a71024 5ba58328 68 - 00a71068 5ba58380 68 - 00a710ac 5ba58430 68 - 00a710f0 5ba5dba4 68 - ... - total 619 objects - Statistics: - MT Count TotalSize Class Name - 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource - 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag - 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer - ... - 0015cde8 6 10260 Free - 5ba57bf8 318 18136 System.String - ... + (lldb) dumpheap + Address MT Size + 00a71000 0015cde8 12 Free + 00a7100c 0015cde8 12 Free + 00a71018 0015cde8 12 Free + 00a71024 5ba58328 68 + 00a71068 5ba58380 68 + 00a710ac 5ba58430 68 + 00a710f0 5ba5dba4 68 + ... + total 619 objects + Statistics: + MT Count TotalSize Class Name + 5ba7607c 1 12 System.Security.Permissions.HostProtectionResource + 5ba75d54 1 12 System.Security.Permissions.SecurityPermissionFlag + 5ba61f18 1 12 System.Collections.CaseInsensitiveComparer + ... + 0015cde8 6 10260 Free + 5ba57bf8 318 18136 System.String + ... "Free" objects are simply regions of space the garbage collector can use later. If 30% or more of the heap contains "Free" objects, the process may suffer from @@ -301,12 +301,12 @@ heap fragmentation. This is usually caused by pinning objects for a long time combined with a high rate of allocation. Here is example output where DumpHeap provides a warning about fragmentation: - - Fragmented blocks larger than 1MB: - Addr Size Followed by - 00a780c0 1.5MB 00bec800 System.Byte[] - 00da4e38 1.2MB 00ed2c00 System.Byte[] - 00f16df0 1.2MB 01044338 System.Byte[] + + Fragmented blocks larger than 1MB: + Addr Size Followed by + 00a780c0 1.5MB 00bec800 System.Byte[] + 00da4e38 1.2MB 00ed2c00 System.Byte[] + 00f16df0 1.2MB 01044338 System.Byte[] The arguments in detail: @@ -354,41 +354,41 @@ be returned. More generally, "-type []". The start/end parameters can be obtained from the output of eeheap -gc. For example, if you only want to list objects in the large heap segment: - (lldb) eeheap -gc - Number of GC Heaps: 1 - generation 0 starts at 0x00c32754 - generation 1 starts at 0x00c32748 - generation 2 starts at 0x00a71000 - segment begin allocated size - 00a70000 00a71000 010443a8 005d33a8(6108072) - Large object heap starts at 0x01a71000 - segment begin allocated size - 01a70000 01a71000 01a75000 0x00004000(16384) - Total Size 0x5d73a8(6124456) - ------------------------------ - GC Heap Size 0x5d73a8(6124456) - - (lldb) dumpheap 1a71000 1a75000 - Address MT Size - 01a71000 5ba88bd8 2064 - 01a71810 0019fe48 2032 Free - 01a72000 5ba88bd8 4096 - 01a73000 0019fe48 4096 Free - 01a74000 5ba88bd8 4096 - total 5 objects - Statistics: - MT Count TotalSize Class Name - 0019fe48 2 6128 Free - 5ba88bd8 3 10256 System.Object[] - Total 5 objects + (lldb) eeheap -gc + Number of GC Heaps: 1 + generation 0 starts at 0x00c32754 + generation 1 starts at 0x00c32748 + generation 2 starts at 0x00a71000 + segment begin allocated size + 00a70000 00a71000 010443a8 005d33a8(6108072) + Large object heap starts at 0x01a71000 + segment begin allocated size + 01a70000 01a71000 01a75000 0x00004000(16384) + Total Size 0x5d73a8(6124456) + ------------------------------ + GC Heap Size 0x5d73a8(6124456) + + (lldb) dumpheap 1a71000 1a75000 + Address MT Size + 01a71000 5ba88bd8 2064 + 01a71810 0019fe48 2032 Free + 01a72000 5ba88bd8 4096 + 01a73000 0019fe48 4096 Free + 01a74000 5ba88bd8 4096 + total 5 objects + Statistics: + MT Count TotalSize Class Name + 0019fe48 2 6128 Free + 5ba88bd8 3 10256 System.Object[] + Total 5 objects Finally, if GC heap corruption is present, you may see an error like this: - (lldb) dumpheap -stat - object 00a73d24: does not have valid MT - curr_object : 00a73d24 - Last good object: 00a73d14 - ---------------- + (lldb) dumpheap -stat + object 00a73d24: does not have valid MT + curr_object : 00a73d24 + Last good object: 00a73d14 + ---------------- That indicates a serious problem. See the help for VerifyHeap for more information on diagnosing the cause. @@ -403,33 +403,33 @@ to know the MethodTable address to tell SOS how to interpret the fields, as a value class is not a first-class object with it's own MethodTable as the first field. For example: - (lldb) sos DumpObj a79d98 - Name: Mainy - MethodTable: 009032d8 - EEClass: 03ee1424 - Size: 28(0x1c) bytes - (/home/user/pub/unittest) - Fields: - MT Field Offset Type Attr Value Name - 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype - 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep + (lldb) sos DumpObj a79d98 + Name: Mainy + MethodTable: 009032d8 + EEClass: 03ee1424 + Size: 28(0x1c) bytes + (/home/user/pub/unittest) + Fields: + MT Field Offset Type Attr Value Name + 0090320c 4000010 4 VALUETYPE instance 00a79d9c m_valuetype + 009032d8 400000f 4 CLASS static 00a79d54 m_sExcep m_valuetype is a value type. The value in the MT column (0090320c) is the MethodTable for it, and the Value column provides the start address: - (lldb) sos DumpVC 0090320c 00a79d9c - Name: Funny - MethodTable 0090320c - EEClass: 03ee14b8 - Size: 28(0x1c) bytes - (/home/user/pub/unittest) - Fields: - MT Field Offset Type Attr Value Name - 0090320c 4000001 0 CLASS instance 00a743d8 signature - 0090320c 4000002 8 System.Int32 instance 2345 m1 - 0090320c 4000003 10 System.Boolean instance 1 b1 - 0090320c 4000004 c System.Int32 instance 1234 m2 - 0090320c 4000005 4 CLASS instance 00a79d98 backpointer + (lldb) sos DumpVC 0090320c 00a79d9c + Name: Funny + MethodTable 0090320c + EEClass: 03ee14b8 + Size: 28(0x1c) bytes + (/home/user/pub/unittest) + Fields: + MT Field Offset Type Attr Value Name + 0090320c 4000001 0 CLASS instance 00a743d8 signature + 0090320c 4000002 8 System.Int32 instance 2345 m1 + 0090320c 4000003 10 System.Boolean instance 1 b1 + 0090320c 4000004 c System.Int32 instance 1234 m2 + 0090320c 4000005 4 CLASS instance 00a79d98 backpointer DumpVC is quite a specialized function. Some managed programs make heavy use of value classes, while others do not. @@ -691,7 +691,7 @@ IP2MD Given an address in managed JITTED code, IP2MD attempts to find the MethodDesc associated with it. For example, this output from K: - (lldb) bt + (lldb) bt ... frame #9: 0x00007fffffffbf60 0x00007ffff61c6d89 libcoreclr.so`MethodDesc::DoPrestub(this=0x00007ffff041f870, pDispatchingMT=0x0000000000000000) + 3001 at prestub.cpp:1490 frame #10: 0x00007fffffffc140 0x00007ffff61c5f17 libcoreclr.so`::PreStubWorker(pTransitionBlock=0x00007fffffffc9a8, pMD=0x00007ffff041f870) + 1399 at prestub.cpp:1037 @@ -704,7 +704,7 @@ associated with it. For example, this output from K: frame #23: 0x00007fffffffccb0 0x00007ffff5d6d6dc libcoreclr.so`CallDescrWorkerWithHandler(pCallDescrData=0x00007fffffffce80, fCriticalCall=0) + 476 at callhelpers.cpp:88 frame #24: 0x00007fffffffcd00 0x00007ffff5d6eb38 libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=0x00007fffffffd0c8, pArguments=0x00007fffffffd048) + 2504 at callhelpers.cpp:633 - (lldb) ip2md 0x00007ffff049773c + (lldb) ip2md 0x00007ffff049773c MethodDesc: 00007ffff7f71920 Method Name: Microsoft.Win32.SafeHandles.SafeFileHandle.Open(System.Func`1) Class: 00007ffff0494bf8 @@ -732,12 +732,12 @@ pointer for the method, or a code address within the method body. Unlike the debugger "U" function, the entire method from start to finish is printed, with annotations that convert metadata tokens to names. - - ... - 03ef015d b901000000 mov ecx,0x1 - 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713) - 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter) - 03ef016d 89442414 mov [esp+0x14],eax + + ... + 03ef015d b901000000 mov ecx,0x1 + 03ef0162 ff156477a25b call dword ptr [mscorlib_dll+0x3c7764 (5ba27764)] (System.Console.InitializeStdOutError(Boolean), mdToken: 06000713) + 03ef0168 a17c20a701 mov eax,[01a7207c] (Object: SyncTextWriter) + 03ef016d 89442414 mov [esp+0x14],eax If you pass the -gcinfo flag, you'll get inline display of the GCInfo for the method. You can also obtain this information with the GCInfo command. @@ -757,17 +757,17 @@ include the source file name and line number corresponding to the disassembly. The -n (No line numbers) flag can be specified to disable this behavior. - - ... - c:\Code\prj.mini\exc.cs @ 38: - 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type to continue: ") - 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b) - 001b00bb 90 nop - - c:\Code\prj.mini\exc.cs @ 39: - 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6) - >>> 001b00c1 90 nop - ... + + ... + c:\Code\prj.mini\exc.cs @ 38: + 001b00b0 8b0d3020ab03 mov ecx,dword ptr ds:[3AB2030h] ("Break in debugger. When done type to continue: ") + 001b00b6 e8d5355951 call mscorlib_ni+0x8b3690 (51743690) (System.Console.Write(System.String), mdToken: 0600091b) + 001b00bb 90 nop + + c:\Code\prj.mini\exc.cs @ 39: + 001b00bc e863cdc651 call mscorlib_ni+0xf8ce24 (51e1ce24) (System.Console.ReadLine(), mdToken: 060008f6) + >>> 001b00c1 90 nop + ... \\ COMMAND: dumpstack. @@ -815,27 +815,27 @@ block and the handler block. For a TYPED handler, this would be the "try" and Sample output: - (lldb) sos EHInfo 33bbd3a - MethodDesc: 03310f68 - Method Name: MainClass.Main() - Class: 03571358 - MethodTable: 0331121c - mdToken: 0600000b - Module: 001e2fd8 - IsJitted: yes - CodeAddr: 033bbca0 + (lldb) sos EHInfo 33bbd3a + MethodDesc: 03310f68 + Method Name: MainClass.Main() + Class: 03571358 + MethodTable: 0331121c + mdToken: 0600000b + Module: 001e2fd8 + IsJitted: yes + CodeAddr: 033bbca0 - EHHandler 0: TYPED catch(System.IO.FileNotFoundException) - Clause: [033bbd2b, 033bbd3c] [8b, 9c] - Handler: [033bbd3c, 033bbd50] [9c, b0] + EHHandler 0: TYPED catch(System.IO.FileNotFoundException) + Clause: [033bbd2b, 033bbd3c] [8b, 9c] + Handler: [033bbd3c, 033bbd50] [9c, b0] - EHHandler 1: FINALLY - Clause: [033bbd83, 033bbda3] [e3, 103] - Handler: [033bbda3, 033bbdc5] [103, 125] + EHHandler 1: FINALLY + Clause: [033bbd83, 033bbda3] [e3, 103] + Handler: [033bbda3, 033bbdc5] [103, 125] - EHHandler 2: TYPED catch(System.Exception) - Clause: [033bbd7a, 033bbdc5] [da, 125] - Handler: [033bbdc5, 033bbdd6] [125, 136] + EHHandler 2: TYPED catch(System.Exception) + Clause: [033bbd7a, 033bbdc5] [da, 125] + Handler: [033bbdc5, 033bbdd6] [125, 136] \\ @@ -854,53 +854,53 @@ you would print this output out and read it alongside a disassembly of the method. For example, the notation "reg EDI becoming live" at offset 0x11 of the method might correspond to a "mov edi,ecx" statement. - (lldb) sos GCInfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method) - entry point 5b68dbb8 - preJIT generated code - GC info 5b9f2f09 - Method info block: - method size = 0036 - prolog size = 19 - epilog size = 8 - epilog count = 1 - epilog end = yes - saved reg. mask = 000B - ebp frame = yes - fully interruptible=yes - double align = no - security check = no - exception handlers = no - local alloc = no - edit & continue = no - varargs = no - argument count = 4 - stack frame size = 1 - untracked count = 5 - var ptr tab count = 0 - epilog at 002E - 36 D4 8C C7 AA | - 93 F3 40 05 | - - Pointer table: - 14 | [EBP+14H] an untracked local - 10 | [EBP+10H] an untracked local - 0C | [EBP+0CH] an untracked local - 08 | [EBP+08H] an untracked local - 44 | [EBP-04H] an untracked local - F1 79 | 0011 reg EDI becoming live - 72 | 0013 reg ESI becoming live - 83 | 0016 push ptr 0 - 8B | 0019 push ptr 1 - 93 | 001C push ptr 2 - 9B | 001F push ptr 3 - 56 | 0025 reg EDX becoming live - 4A | 0027 reg ECX becoming live - 0E | 002D reg ECX becoming dead - 10 | 002D reg EDX becoming dead - E0 | 002D pop 4 ptrs - F0 31 | 0036 reg ESI becoming dead - 38 | 0036 reg EDI becoming dead - FF | + (lldb) sos GCInfo 5b68dbb8 (5b68dbb8 is the start of a JITTED method) + entry point 5b68dbb8 + preJIT generated code + GC info 5b9f2f09 + Method info block: + method size = 0036 + prolog size = 19 + epilog size = 8 + epilog count = 1 + epilog end = yes + saved reg. mask = 000B + ebp frame = yes + fully interruptible=yes + double align = no + security check = no + exception handlers = no + local alloc = no + edit & continue = no + varargs = no + argument count = 4 + stack frame size = 1 + untracked count = 5 + var ptr tab count = 0 + epilog at 002E + 36 D4 8C C7 AA | + 93 F3 40 05 | + + Pointer table: + 14 | [EBP+14H] an untracked local + 10 | [EBP+10H] an untracked local + 0C | [EBP+0CH] an untracked local + 08 | [EBP+08H] an untracked local + 44 | [EBP-04H] an untracked local + F1 79 | 0011 reg EDI becoming live + 72 | 0013 reg ESI becoming live + 83 | 0016 push ptr 0 + 8B | 0019 push ptr 1 + 93 | 001C push ptr 2 + 9B | 001F push ptr 3 + 56 | 0025 reg EDX becoming live + 4A | 0027 reg ECX becoming live + 0E | 002D reg ECX becoming dead + 10 | 002D reg EDX becoming dead + E0 | 002D pop 4 ptrs + F0 31 | 0036 reg ESI becoming dead + 38 | 0036 reg EDI becoming dead + FF | This function is important for CLR Devs, but very difficult for anyone else to make sense of it. You would usually come to use it if you suspect a gc heap @@ -949,18 +949,18 @@ To correctly specify explicitly implemented methods make sure to retrieve the method name from the metadata, or from the output of the "dumpmt -md" command. For example: - public interface I1 - { - void M1(); - } - public class ExplicitItfImpl : I1 - { - ... - void I1.M1() // this method's name is 'I1.M1' - { ... } - } + public interface I1 + { + void M1(); + } + public class ExplicitItfImpl : I1 + { + ... + void I1.M1() // this method's name is 'I1.M1' + { ... } + } - bpmd myapp.dll ExplicitItfImpl.I1.M1 + bpmd myapp.dll ExplicitItfImpl.I1.M1 bpmd works equally well with generic types. Adding a breakpoint on a generic @@ -968,56 +968,56 @@ type sets breakpoints on all already JIT-ted generic methods and sets a pending breakpoint for any instantiation that will be JIT-ted in the future. Example for generics: - Given the following two classes: + Given the following two classes: - class G3 - { - ... - public void F(T1 p1, T2 p2, T3 p3) - { ... } - } + class G3 + { + ... + public void F(T1 p1, T2 p2, T3 p3) + { ... } + } - public class G1 { - // static method - static public void G(W w) - { ... } - } + public class G1 { + // static method + static public void G(W w) + { ... } + } - One would issue the following commands to set breakpoints on G3.F() and - G1.G(): + One would issue the following commands to set breakpoints on G3.F() and + G1.G(): - bpmd myapp.dll G3`3.F - bpmd myapp.dll G1`1.G + bpmd myapp.dll G3`3.F + bpmd myapp.dll G1`1.G And for explicitly implemented methods on generic interfaces: - public interface IT1 - { - void M1(T t); - } + public interface IT1 + { + void M1(T t); + } - public class ExplicitItfImpl : IT1 - { - ... - void IT1.M1(U u) // this method's name is 'IT1.M1' - { ... } - } + public class ExplicitItfImpl : IT1 + { + ... + void IT1.M1(U u) // this method's name is 'IT1.M1' + { ... } + } - bpmd bpmd.dll ExplicitItfImpl`1.IT1.M1 + bpmd bpmd.dll ExplicitItfImpl`1.IT1.M1 Additional examples: - If IT1 and ExplicitItfImpl are types declared inside another class, - Outer, the bpmd command would become: + If IT1 and ExplicitItfImpl are types declared inside another class, + Outer, the bpmd command would become: - bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1.M1 + bpmd bpmd.exe Outer+ExplicitItfImpl`1.Outer.IT1.M1 - (note that the fully qualified type name for ExplicitItfImpl became - Outer+ExplicitItfImpl, using the '+' separator, while the method name - is Outer.IT1.M1, using a '.' as the separator) + (note that the fully qualified type name for ExplicitItfImpl became + Outer+ExplicitItfImpl, using the '+' separator, while the method name + is Outer.IT1.M1, using a '.' as the separator) - Furthermore, if the Outer class resides in a namespace, NS, the bpmd - command to use becomes: + Furthermore, if the Outer class resides in a namespace, NS, the bpmd + command to use becomes: - bpmd bpmd.dll NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1.M1 + bpmd bpmd.dll NS.Outer+ExplicitItfImpl`1.NS.Outer.IT1.M1 bpmd does not accept offsets nor parameters in the method name. You can add an IL offset as an optional parameter separate from the name. If there are overloaded @@ -1061,19 +1061,19 @@ have an object pointer, and can attempt to run "dumpobj" on it. Here is output for a simple program: - (lldb) eeheap -gc - Number of GC Heaps: 1 - generation 0 starts at 0x00a71018 - generation 1 starts at 0x00a7100c - generation 2 starts at 0x00a71000 - segment begin allocated size - 00a70000 00a71000 00a7e01c 0000d01c(53276) - Large object heap starts at 0x01a71000 - segment begin allocated size - 01a70000 01a71000 01a76000 0x00005000(20480) - Total Size 0x1201c(73756) - ------------------------------ - GC Heap Size 0x1201c(73756) + (lldb) eeheap -gc + Number of GC Heaps: 1 + generation 0 starts at 0x00a71018 + generation 1 starts at 0x00a7100c + generation 2 starts at 0x00a71000 + segment begin allocated size + 00a70000 00a71000 00a7e01c 0000d01c(53276) + Large object heap starts at 0x01a71000 + segment begin allocated size + 01a70000 01a71000 01a76000 0x00005000(20480) + Total Size 0x1201c(73756) + ------------------------------ + GC Heap Size 0x1201c(73756) So the total size of the GC Heap is only 72K. On a large web server, with multiple processors, you can expect to see a GC Heap of 400MB or more. The @@ -1086,49 +1086,49 @@ The loader output lists various private heaps associated with AppDomains. It also lists heaps associated with the JIT compiler, and heaps associated with Modules. For example: - (lldb) eeheap -loader - Loader Heap: - -------------------------------------- - System Domain: 5e0662a0 - LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes. - HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes. - StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes. - Total size: 0x3000(12288)bytes - -------------------------------------- - Shared Domain: 5e066970 - LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes. - Wasted: 0x00001000 bytes. - HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes. - StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes. - Total size: 0x6000(24576)bytes - -------------------------------------- - Domain 1: 14f000 - LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes. - Wasted: 0x00001000 bytes. - HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes. - StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes. - Total size: 0x8000(32768)bytes - -------------------------------------- - Jit code heap: - Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes. - Total size: 0x2000(8192)bytes - -------------------------------------- - Module Thunk heaps: - Module 5ba22410: Size: 0x00000000 bytes. - Module 001c1320: Size: 0x00000000 bytes. - Module 001c03f0: Size: 0x00000000 bytes. - Module 001caa38: Size: 0x00000000 bytes. - Total size: 0x0(0)bytes - -------------------------------------- - Module Lookup Table heaps: - Module 5ba22410:Size: 0x00000000 bytes. - Module 001c1320:Size: 0x00000000 bytes. - Module 001c03f0:Size: 0x00000000 bytes. - Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes. - Total size: 0x2000(8192)bytes - -------------------------------------- - Total LoaderHeap size: 0x15000(86016)bytes - ======================================= + (lldb) eeheap -loader + Loader Heap: + -------------------------------------- + System Domain: 5e0662a0 + LowFrequencyHeap:008f0000(00002000:00001000) Size: 0x00001000 bytes. + HighFrequencyHeap:008f2000(00008000:00001000) Size: 0x00001000 bytes. + StubHeap:008fa000(00002000:00001000) Size: 0x00001000 bytes. + Total size: 0x3000(12288)bytes + -------------------------------------- + Shared Domain: 5e066970 + LowFrequencyHeap:00920000(00002000:00001000) 03e30000(00010000:00003000) Size: 0x00004000 bytes. + Wasted: 0x00001000 bytes. + HighFrequencyHeap:00922000(00008000:00001000) Size: 0x00001000 bytes. + StubHeap:0092a000(00002000:00001000) Size: 0x00001000 bytes. + Total size: 0x6000(24576)bytes + -------------------------------------- + Domain 1: 14f000 + LowFrequencyHeap:00900000(00002000:00001000) 03ee0000(00010000:00003000) Size: 0x00004000 bytes. + Wasted: 0x00001000 bytes. + HighFrequencyHeap:00902000(00008000:00003000) Size: 0x00003000 bytes. + StubHeap:0090a000(00002000:00001000) Size: 0x00001000 bytes. + Total size: 0x8000(32768)bytes + -------------------------------------- + Jit code heap: + Normal JIT:03ef0000(00010000:00002000) Size: 0x00002000 bytes. + Total size: 0x2000(8192)bytes + -------------------------------------- + Module Thunk heaps: + Module 5ba22410: Size: 0x00000000 bytes. + Module 001c1320: Size: 0x00000000 bytes. + Module 001c03f0: Size: 0x00000000 bytes. + Module 001caa38: Size: 0x00000000 bytes. + Total size: 0x0(0)bytes + -------------------------------------- + Module Lookup Table heaps: + Module 5ba22410:Size: 0x00000000 bytes. + Module 001c1320:Size: 0x00000000 bytes. + Module 001c03f0:Size: 0x00000000 bytes. + Module 001caa38:03ec0000(00010000:00002000) Size: 0x00002000 bytes. + Total size: 0x2000(8192)bytes + -------------------------------------- + Total LoaderHeap size: 0x15000(86016)bytes + ======================================= By using eeheap to keep track of the growth of these private heaps, we are able to rule out or include them as a source of a memory leak. @@ -1141,21 +1141,21 @@ Name2EE ! This function allows you to turn a class name into a MethodTable and EEClass. It turns a method name into a MethodDesc. Here is an example for a method: - (lldb) name2ee unittest.exe MainClass.Main - Module: 001caa38 - Token: 0x0600000d - MethodDesc: 00902f40 - Name: MainClass.Main() - JITTED Code Address: 03ef00b8 + (lldb) name2ee unittest.exe MainClass.Main + Module: 001caa38 + Token: 0x0600000d + MethodDesc: 00902f40 + Name: MainClass.Main() + JITTED Code Address: 03ef00b8 and for a class: - (lldb) name2ee unittest!MainClass - Module: 001caa38 - Token: 0x02000005 - MethodTable: 009032d8 - EEClass: 03ee1424 - Name: MainClass + (lldb) name2ee unittest!MainClass + Module: 001caa38 + Token: 0x02000005 + MethodTable: 009032d8 + EEClass: 03ee1424 + Name: MainClass The module you are "browsing" with Name2EE needs to be loaded in the process. To get a type name exactly right, first browse the module with ILDASM. You @@ -1179,12 +1179,12 @@ price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null For this kind of module, simply use price as the module name: - 0:044> name2ee price Price - Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null) - Token: 0x02000002 - MethodTable: 11a47ae0 - EEClass: 11a538c8 - Name: Price + 0:044> name2ee price Price + Module: 10f028b0 (price, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null) + Token: 0x02000002 + MethodTable: 11a47ae0 + EEClass: 11a538c8 + Name: Price Where are we getting these module names from? Run DumpDomain to see a list of all loaded modules in all domains. And remember that you can browse all the @@ -1295,14 +1295,14 @@ DumpMD This command lists information about a MethodDesc. You can use ip2md to turn a code address in a managed function into a MethodDesc: - (lldb) dumpmd 902f40 - Method Name: Mainy.Main() - Class: 03ee1424 - MethodTable: 009032d8 - mdToken: 0600000d - Module: 001caa78 - IsJitted: yes - CodeAddr: 03ef00b8 + (lldb) dumpmd 902f40 + Method Name: Mainy.Main() + Class: 03ee1424 + MethodTable: 009032d8 + mdToken: 0600000d + Module: 001caa78 + IsJitted: yes + CodeAddr: 03ef00b8 If IsJitted is "yes," you can run U on the CodeAddr pointer to see a disassembly of the JITTED code. You can call also DumpClass, DumpMT, @@ -1315,18 +1315,18 @@ Token2EE This function allows you to turn a metadata token into a MethodTable or MethodDesc. Here is an example showing class tokens being resolved: - (lldb) sos Token2EE unittest.exe 02000003 - Module: 001caa38 - Token: 0x02000003 - MethodTable: 0090375c - EEClass: 03ee1ae0 - Name: Bank - (lldb) sos Token2EE image00400000 02000004 - Module: 001caa38 - Token: 0x02000004 - MethodTable: 009038ec - EEClass: 03ee1b84 - Name: Customer + (lldb) sos Token2EE unittest.exe 02000003 + Module: 001caa38 + Token: 0x02000003 + MethodTable: 0090375c + EEClass: 03ee1ae0 + Name: Bank + (lldb) sos Token2EE image00400000 02000004 + Module: 001caa38 + Token: 0x02000004 + MethodTable: 009038ec + EEClass: 03ee1b84 + Name: Customer The module you are "browsing" with Token2EE needs to be loaded in the process. This function doesn't see much use, especially since a tool like ILDASM can @@ -1344,28 +1344,28 @@ DumpModule [-mt] You can get a Module address from DumpDomain, DumpAssembly and other functions. Here is sample output: - (lldb) sos DumpModule 1caa50 - Name: /home/user/pub/unittest - Attributes: PEFile - Assembly: 001ca248 - LoaderHeap: 001cab3c - TypeDefToMethodTableMap: 03ec0010 - TypeRefToMethodTableMap: 03ec0024 - MethodDefToDescMap: 03ec0064 - FieldDefToDescMap: 03ec00a4 - MemberRefToDescMap: 03ec00e8 - FileReferencesMap: 03ec0128 - AssemblyReferencesMap: 03ec012c - MetaData start address: 00402230 (1888 bytes) + (lldb) sos DumpModule 1caa50 + Name: /home/user/pub/unittest + Attributes: PEFile + Assembly: 001ca248 + LoaderHeap: 001cab3c + TypeDefToMethodTableMap: 03ec0010 + TypeRefToMethodTableMap: 03ec0024 + MethodDefToDescMap: 03ec0064 + FieldDefToDescMap: 03ec00a4 + MemberRefToDescMap: 03ec00e8 + FileReferencesMap: 03ec0128 + AssemblyReferencesMap: 03ec012c + MetaData start address: 00402230 (1888 bytes) The Maps listed map metadata tokens to CLR data structures. Without going into too much detail, you can examine memory at those addresses to find the appropriate structures. For example, the TypeDefToMethodTableMap above can be examined: - (lldb) dd 3ec0010 - 03ec0010 00000000 00000000 0090320c 0090375c - 03ec0020 009038ec ... + (lldb) dd 3ec0010 + 03ec0010 00000000 00000000 0090320c 0090375c + 03ec0020 009038ec ... This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You can run DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token @@ -1374,28 +1374,28 @@ and maps it to a MethodDesc, which can be passed to dumpmd. There is a new option "-mt", which will display the types defined in a module, and the types referenced by the module. For example: - (lldb) sos DumpModule -mt 1aa580 - Name: /home/user/pub/unittest - ...... - MetaData start address: 0040220c (1696 bytes) + (lldb) sos DumpModule -mt 1aa580 + Name: /home/user/pub/unittest + ...... + MetaData start address: 0040220c (1696 bytes) - Types defined in this module + Types defined in this module - MT TypeDef Name - -------------------------------------------------------------------------- - 030d115c 0x02000002 Funny - 030d1228 0x02000003 Mainy + MT TypeDef Name + -------------------------------------------------------------------------- + 030d115c 0x02000002 Funny + 030d1228 0x02000003 Mainy - Types referenced in this module + Types referenced in this module - MT TypeRef Name - -------------------------------------------------------------------------- - 030b6420 0x01000001 System.ValueType - 030b5cb0 0x01000002 System.Object - 030fceb4 0x01000003 System.Exception - 0334e374 0x0100000c System.Console - 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle - 0336a048 0x0100000f System.GC + MT TypeRef Name + -------------------------------------------------------------------------- + 030b6420 0x01000001 System.ValueType + 030b5cb0 0x01000002 System.Object + 030fceb4 0x01000003 System.Exception + 0334e374 0x0100000c System.Console + 03167a50 0x0100000e System.Runtime.InteropServices.GCHandle + 0336a048 0x0100000f System.GC \\ @@ -1404,12 +1404,12 @@ DumpAssembly Example output: - (lldb) sos DumpAssembly 1ca248 - Parent Domain: 0014f000 - Name: /home/user/pub/unittest - ClassLoader: 001ca060 - Module Name - 001caa50 /home/user/pub/unittest + (lldb) sos DumpAssembly 1ca248 + Parent Domain: 0014f000 + Name: /home/user/pub/unittest + ClassLoader: 001ca060 + Module Name + 001caa50 /home/user/pub/unittest An assembly can consist of multiple modules, and those will be listed. You can get an Assembly address from the output of DumpDomain. @@ -1421,18 +1421,18 @@ DumpRuntimeTypes DumpRuntimeTypes finds all System.RuntimeType objects in the gc heap and prints the type name and MethodTable they refer too. Sample output: - Address Domain MT Type Name - ------------------------------------------------------------------------------ - a515f4 14a740 5baf8d28 System.TypedReference - a51608 14a740 5bb05764 System.Globalization.BaseInfoTable - a51958 14a740 5bb05b24 System.Globalization.CultureInfo - a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly - a51de0 14a740 5bb069c8 System.Globalization.TextInfo - a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource - a56bbc 14a740 5baf7248 System.Int32 - a56bd0 14a740 5baf3fdc System.String - a56cfc 14a740 5baf36a4 System.ValueType - ... + Address Domain MT Type Name + ------------------------------------------------------------------------------ + a515f4 14a740 5baf8d28 System.TypedReference + a51608 14a740 5bb05764 System.Globalization.BaseInfoTable + a51958 14a740 5bb05b24 System.Globalization.CultureInfo + a51a44 14a740 5bb06298 System.Globalization.GlobalizationAssembly + a51de0 14a740 5bb069c8 System.Globalization.TextInfo + a56b98 14a740 5bb12d28 System.Security.Permissions.HostProtectionResource + a56bbc 14a740 5baf7248 System.Int32 + a56bd0 14a740 5baf3fdc System.String + a56cfc 14a740 5baf36a4 System.ValueType + ... This command will print a "?" in the domain column if the type is loaded into multiple AppDomains. For example: @@ -1587,22 +1587,22 @@ signs of corruption. It walks objects one by one in a pattern like this: If an error is found, VerifyHeap will report it. I'll take a perfectly good object and corrupt it: - (lldb) dumpobj a79d40 - Name: Customer - MethodTable: 009038ec - EEClass: 03ee1b84 - Size: 20(0x14) bytes - (/home/user/pub/unittest) - Fields: - MT Field Offset Type Attr Value Name - 009038ec 4000008 4 CLASS instance 00a79ce4 name - 009038ec 4000009 8 CLASS instance 00a79d2c bank - 009038ec 400000a c System.Boolean instance 1 valid - - (lldb) ed a79d40+4 01 (change the name field to the bogus pointer value 1) - (lldb) sos VerifyHeap - object 01ee60dc: bad member 00000003 at 01EE6168 - Last good object: 01EE60C4. + (lldb) dumpobj a79d40 + Name: Customer + MethodTable: 009038ec + EEClass: 03ee1b84 + Size: 20(0x14) bytes + (/home/user/pub/unittest) + Fields: + MT Field Offset Type Attr Value Name + 009038ec 4000008 4 CLASS instance 00a79ce4 name + 009038ec 4000009 8 CLASS instance 00a79d2c bank + 009038ec 400000a c System.Boolean instance 1 valid + + (lldb) ed a79d40+4 01 (change the name field to the bogus pointer value 1) + (lldb) sos VerifyHeap + object 01ee60dc: bad member 00000003 at 01EE6168 + Last good object: 01EE60C4. If this gc heap corruption exists, there is a serious bug in your own code or in the CLR. In user code, an error in constructing PInvoke calls can cause @@ -1616,16 +1616,16 @@ GCWhere !GCWhere displays the location in the GC heap of the argument passed in. - 0:002> !GCWhere 02800038 - Address Gen Heap segment begin allocated size - 02800038 2 0 02800000 02800038 0282b740 12 + 0:002> !GCWhere 02800038 + Address Gen Heap segment begin allocated size + 02800038 2 0 02800000 02800038 0282b740 12 When the argument lies in the managed heap, but is not a valid *object* address the "size" is displayed as 0: - 0:002> !GCWhere 0280003c - Address Gen Heap segment begin allocated size - 0280003c 2 0 02800000 02800038 0282b740 0 + 0:002> !GCWhere 0280003c + Address Gen Heap segment begin allocated size + 0280003c 2 0 02800000 02800038 0282b740 0 \\ COMMAND: dumplog. @@ -1640,10 +1640,10 @@ in the current directory is created. The optional argument addr allows one to specify a stress log other then the default one. - (lldb) dumplog - Attempting to dump Stress log to file 'StressLog.txt' - ................. - SUCCESS: Stress log dumped + (lldb) dumplog + Attempting to dump Stress log to file 'StressLog.txt' + ................. + SUCCESS: Stress log dumped To turn on the stress log, set the following environment variables before starting the .NET Core app: @@ -1701,27 +1701,27 @@ The log facilities are defined as follows: Here is some sample output: - 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering. - Thread state = a030 + 3560 9.981137099 : `SYNC` RareEnablePremptiveGC: entering. + Thread state = a030 - 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2, - collect_classes = 0) ==========={ + 3560 9.981135033 : `GC`GCALLOC`GCROOTS` ========== ENDGC 4194 (gen = 2, + collect_classes = 0) ==========={ - 3560 9.981125826 : `GC` Segment mem 00C61000 alloc - = 00D071F0 used 00D09254 committed 00D17000 + 3560 9.981125826 : `GC` Segment mem 00C61000 alloc + = 00D071F0 used 00D09254 committed 00D17000 - 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000 - ] cur = 00000000 + 3560 9.981125726 : `GC` Generation 0 [00CED07C, 00000000 + ] cur = 00000000 - 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000 - ] cur = 00000000 + 3560 9.981125529 : `GC` Generation 1 [00CED070, 00000000 + ] cur = 00000000 - 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000 - ] cur = 00000000 + 3560 9.981125103 : `GC` Generation 2 [00C61000, 00000000 + ] cur = 00000000 - 3560 9.981124963 : `GC` GC Heap 00000000 + 3560 9.981124963 : `GC` GC Heap 00000000 - 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0) + 3560 9.980618994 : `GC`GCROOTS` GcScanHandles (Promotion Phase = 0) The first column is the OS thread ID for the thread appending to the log, the second column is the timestamp, the third is the facility category for the @@ -1738,10 +1738,10 @@ FindAppDomain FindAppDomain will attempt to resolve the AppDomain of an object. For example, using an Object Pointer from the output of DumpStackObjects: - (lldb) sos FindAppDomain 00a79d98 - AppDomain: 0014f000 - Name: unittest.exe - ID: 1 + (lldb) sos FindAppDomain 00a79d98 + AppDomain: 0014f000 + Name: unittest.exe + ID: 1 You can find out more about the AppDomain with the DumpDomain command. Not every object has enough clues about it's origin to determine the AppDomain. @@ -1758,24 +1758,24 @@ HistInit command. Sample output: - (lldb) histinit - Attempting to read Stress log - STRESS LOG: - facilitiesToLog = 0xffffffff - levelToLog = 6 - MaxLogSizePerThread = 0x10000 (65536) - MaxTotalLogSize = 0x1000000 (16777216) - CurrentTotalLogChunk = 9 - ThreadsWithLogs = 3 - Clock frequency = 3.392 GHz - Start time 15:26:31 - Last message time 15:26:56 - Total elapsed time 25.077 sec - ..................................... - ---------------------------- 2407 total entries ----------------------------- - - - SUCCESS: GCHist structures initialized + (lldb) histinit + Attempting to read Stress log + STRESS LOG: + facilitiesToLog = 0xffffffff + levelToLog = 6 + MaxLogSizePerThread = 0x10000 (65536) + MaxTotalLogSize = 0x1000000 (16777216) + CurrentTotalLogChunk = 9 + ThreadsWithLogs = 3 + Clock frequency = 3.392 GHz + Start time 15:26:31 + Last message time 15:26:56 + Total elapsed time 25.077 sec + ..................................... + ---------------------------- 2407 total entries ----------------------------- + + + SUCCESS: GCHist structures initialized \\ @@ -1786,17 +1786,17 @@ To examine log entries related to an object whose present address is known one would use this command. The output of this command contains all entries that reference the object: - (lldb) histobjfind 028970d4 - GCCount Object Message - --------------------------------------------------------- - 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) - 2296 028970d4 Relocation NEWVALUE for root 00223fc4 - 2296 028970d4 Relocation NEWVALUE for root 01e411b8 - ... - 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) - 2295 028970d4 Relocation NEWVALUE for root 00223fc4 - 2295 028970d4 Relocation NEWVALUE for root 01e411b8 - ... + (lldb) histobjfind 028970d4 + GCCount Object Message + --------------------------------------------------------- + 2296 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) + 2296 028970d4 Relocation NEWVALUE for root 00223fc4 + 2296 028970d4 Relocation NEWVALUE for root 01e411b8 + ... + 2295 028970d4 Promotion for root 01e411b8 (MT = 5b6c5cd8) + 2295 028970d4 Relocation NEWVALUE for root 00223fc4 + 2295 028970d4 Relocation NEWVALUE for root 01e411b8 + ... \\ @@ -1809,23 +1809,23 @@ an object through the GCs. HistRoot provides information related to both promotions and relocations of the root specified as the argument. - (lldb) histroot 01e411b8 - GCCount Value MT Promoted? Notes - --------------------------------------------------------- - 2296 028970d4 5b6c5cd8 yes - 2295 028970d4 5b6c5cd8 yes - 2294 028970d4 5b6c5cd8 yes - 2293 028970d4 5b6c5cd8 yes - 2292 028970d4 5b6c5cd8 yes - 2291 028970d4 5b6c5cd8 yes - 2290 028970d4 5b6c5cd8 yes - 2289 028970d4 5b6c5cd8 yes - 2288 028970d4 5b6c5cd8 yes - 2287 028970d4 5b6c5cd8 yes - 2286 028970d4 5b6c5cd8 yes - 2285 028970d4 5b6c5cd8 yes - 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs - ... + (lldb) histroot 01e411b8 + GCCount Value MT Promoted? Notes + --------------------------------------------------------- + 2296 028970d4 5b6c5cd8 yes + 2295 028970d4 5b6c5cd8 yes + 2294 028970d4 5b6c5cd8 yes + 2293 028970d4 5b6c5cd8 yes + 2292 028970d4 5b6c5cd8 yes + 2291 028970d4 5b6c5cd8 yes + 2290 028970d4 5b6c5cd8 yes + 2289 028970d4 5b6c5cd8 yes + 2288 028970d4 5b6c5cd8 yes + 2287 028970d4 5b6c5cd8 yes + 2286 028970d4 5b6c5cd8 yes + 2285 028970d4 5b6c5cd8 yes + 322 028970e8 5b6c5cd8 yes Duplicate promote/relocs + ... \\ @@ -1836,29 +1836,29 @@ This command examines all stress log relocation records and displays the chain of GC relocations that may have led to the address passed in as an argument. Conceptually the output is: - GenN obj_address root1, root2, root3, - GenN-1 prev_obj_addr root1, root2, - GenN-2 prev_prev_oa root1, root4, - ... + GenN obj_address root1, root2, root3, + GenN-1 prev_obj_addr root1, root2, + GenN-2 prev_prev_oa root1, root4, + ... Sample output: - (lldb) histobj 028970d4 - GCCount Object Roots - --------------------------------------------------------- - 2296 028970d4 00223fc4, 01e411b8, - 2295 028970d4 00223fc4, 01e411b8, - 2294 028970d4 00223fc4, 01e411b8, - 2293 028970d4 00223fc4, 01e411b8, - 2292 028970d4 00223fc4, 01e411b8, - 2291 028970d4 00223fc4, 01e411b8, - 2290 028970d4 00223fc4, 01e411b8, - 2289 028970d4 00223fc4, 01e411b8, - 2288 028970d4 00223fc4, 01e411b8, - 2287 028970d4 00223fc4, 01e411b8, - 2286 028970d4 00223fc4, 01e411b8, - 2285 028970d4 00223fc4, 01e411b8, - 322 028970d4 01e411b8, - 0 028970d4 + (lldb) histobj 028970d4 + GCCount Object Roots + --------------------------------------------------------- + 2296 028970d4 00223fc4, 01e411b8, + 2295 028970d4 00223fc4, 01e411b8, + 2294 028970d4 00223fc4, 01e411b8, + 2293 028970d4 00223fc4, 01e411b8, + 2292 028970d4 00223fc4, 01e411b8, + 2291 028970d4 00223fc4, 01e411b8, + 2290 028970d4 00223fc4, 01e411b8, + 2289 028970d4 00223fc4, 01e411b8, + 2288 028970d4 00223fc4, 01e411b8, + 2287 028970d4 00223fc4, 01e411b8, + 2286 028970d4 00223fc4, 01e411b8, + 2285 028970d4 00223fc4, 01e411b8, + 322 028970d4 01e411b8, + 0 028970d4 \\ @@ -1869,8 +1869,8 @@ This command releases any resources used by the Hist-family of commands. Generally there's no need to call this explicitly, as each HistInit will first cleanup the previous resources. - (lldb) histclear - Completed successfully. + (lldb) histclear + Completed successfully. \\ diff --git a/src/SOS/Strike/strike.cpp b/src/SOS/Strike/strike.cpp index 94f857b22..48eda6df2 100644 --- a/src/SOS/Strike/strike.cpp +++ b/src/SOS/Strike/strike.cpp @@ -2242,15 +2242,23 @@ size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size return _wcslen(wszHeader); } +enum StackTraceElementFlags +{ + // Set if this element represents the last frame of the foreign exception stack trace + STEF_LAST_FRAME_FROM_FOREIGN_STACK_TRACE = 0x0001, + + // Set if the "ip" field has already been adjusted (decremented) + STEF_IP_ADJUSTED = 0x0002, +}; + +// This struct needs to match the definition in the runtime. +// See: https://github.com/dotnet/runtime/blob/master/src/coreclr/src/vm/clrex.h struct StackTraceElement { UINT_PTR ip; UINT_PTR sp; DWORD_PTR pFunc; // MethodDesc - // TRUE if this element represents the last frame of the foreign - // exception stack trace. - BOOL fIsLastFrameFromForeignStackTrace; - + INT flags; // This is StackTraceElementFlags but it needs to always be "int" sized for backward compatibility. }; #include "sos_stacktrace.h" @@ -2487,7 +2495,7 @@ size_t FormatGeneratedException (DWORD_PTR dataPtr, // The unmodified IP is displayed (above by DumpMDInfoBuffer) which points after the exception in most // cases. This means that the printed IP and the printed line number often will not map to one another // and this is intentional. - SUCCEEDED(GetLineByOffset(TO_CDADDR(bAsync && i == 0 ? ste.ip : ste.ip - g_targetMachine->StackWalkIPAdjustOffset()), &linenum, filename, _countof(filename)))) + SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename), !bAsync || i > 0))) { swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W(" %s [%s @ %d]\n"), so.String(), filename, linenum); } @@ -2665,6 +2673,8 @@ HRESULT FormatException(CLRDATA_ADDRESS taObj, BOOL bLineNumbers = FALSE) if (arrayLen != 0 && hr == S_OK) { + // This code is accessing the StackTraceInfo class in the runtime. + // See: https://github.com/dotnet/runtime/blob/master/src/coreclr/src/vm/clrex.h #ifdef _TARGET_WIN64_ DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD); #else @@ -9328,6 +9338,7 @@ DECLARE_API(u) BOOL fWithEHInfo = FALSE; BOOL bSuppressLines = FALSE; BOOL bDisplayOffsets = FALSE; + BOOL bDisplayILMap = FALSE; BOOL bIL = FALSE; BOOL dml = FALSE; size_t nArg; @@ -9339,6 +9350,7 @@ DECLARE_API(u) {"-n", &bSuppressLines, COBOOL, FALSE}, {"-o", &bDisplayOffsets, COBOOL, FALSE}, {"-il", &bIL, COBOOL, FALSE}, + {"-map", &bDisplayILMap, COBOOL, FALSE}, #ifndef FEATURE_PAL {"/d", &dml, COBOOL, FALSE}, #endif @@ -9394,7 +9406,7 @@ DECLARE_API(u) DacpCodeHeaderData& codeHeaderData = std::get<1>(p); std::unique_ptr map(nullptr); ULONG32 mapCount = 0; - Status = GetIntermediateLangMap(bIL, codeHeaderData, map /*out*/, mapCount /* out */, false); + Status = GetIntermediateLangMap(bIL, codeHeaderData, map /*out*/, mapCount /* out */, bDisplayILMap); if (Status != S_OK) { return Status; @@ -9831,7 +9843,7 @@ HRESULT GetIntermediateLangMap(BOOL bIL, const DacpCodeHeaderData& codeHeaderDat { // TODO: These information should be interleaved with the disassembly // Decoded IL can be obtained through refactoring DumpIL code. - ExtOut("%04x %p %p\n", map[i].ilOffset, map[i].startAddress, map[i].endAddress); + ExtOut("%08x %p %p\n", map[i].ilOffset, map[i].startAddress, map[i].endAddress); } } } @@ -15207,6 +15219,8 @@ HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj, if (arrayLen) { + // This code is accessing the StackTraceInfo class in the runtime. + // See: https://github.com/dotnet/runtime/blob/master/src/coreclr/src/vm/clrex.h #ifdef _TARGET_WIN64_ DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD); #else @@ -15518,10 +15532,49 @@ DECLARE_API(VerifyStackTrace) return Status; } -// This is an internal-only Apollo extension to de-optimize the code +// This is an internal-only Apollo extension to save breakpoint/watch state +DECLARE_API(SaveState) +{ + INIT_API_NOEE(); + MINIDUMP_NOT_SUPPORTED(); + + StringHolder filePath; + CMDValue arg[] = + { // vptr, type + {&filePath.data, COSTRING}, + }; + size_t nArg; + if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) + { + return E_FAIL; + } + + if(nArg == 0) + { + ExtOut("Usage: !SaveState \n"); + } + + FILE* pFile; + errno_t error = fopen_s(&pFile, filePath.data, "w"); + if(error != 0) + { + ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error); + return E_FAIL; + } + + g_bpoints.SaveBreakpoints(pFile); + g_watchCmd.SaveListToFile(pFile); + + fclose(pFile); + ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data); + return S_OK; +} + +#endif // FEATURE_PAL + DECLARE_API(SuppressJitOptimization) { - INIT_API_NODAC(); + INIT_API_NOEE(); MINIDUMP_NOT_SUPPORTED(); StringHolder onOff; @@ -15553,8 +15606,6 @@ DECLARE_API(SuppressJitOptimization) g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0); ExtOut("JIT optimization will be suppressed\n"); } - - } else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0)) { @@ -15655,47 +15706,6 @@ HRESULT SetNGENCompilerFlags(DWORD flags) return hr; } - -// This is an internal-only Apollo extension to save breakpoint/watch state -DECLARE_API(SaveState) -{ - INIT_API_NOEE(); - MINIDUMP_NOT_SUPPORTED(); - - StringHolder filePath; - CMDValue arg[] = - { // vptr, type - {&filePath.data, COSTRING}, - }; - size_t nArg; - if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) - { - return E_FAIL; - } - - if(nArg == 0) - { - ExtOut("Usage: !SaveState \n"); - } - - FILE* pFile; - errno_t error = fopen_s(&pFile, filePath.data, "w"); - if(error != 0) - { - ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error); - return E_FAIL; - } - - g_bpoints.SaveBreakpoints(pFile); - g_watchCmd.SaveListToFile(pFile); - - fclose(pFile); - ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data); - return S_OK; -} - -#endif // FEATURE_PAL - DECLARE_API(StopOnCatch) { INIT_API(); diff --git a/src/SOS/Strike/util.cpp b/src/SOS/Strike/util.cpp index 805f2f34b..f368161fd 100644 --- a/src/SOS/Strike/util.cpp +++ b/src/SOS/Strike/util.cpp @@ -4822,7 +4822,8 @@ GetLastMethodIlOffset( // represent an "IL offset". HRESULT ConvertNativeToIlOffset( - ___in ULONG64 native, + ___in ULONG64 nativeOffset, + ___in BOOL bAdjustOffsetForLineNumber, ___out IXCLRDataModule** ppModule, ___out mdMethodDef* methodToken, ___out PULONG32 methodOffs) @@ -4830,12 +4831,24 @@ ConvertNativeToIlOffset( ToRelease pMethodInst(NULL); HRESULT Status; - if ((Status = GetClrMethodInstance(native, &pMethodInst)) != S_OK) + if ((Status = GetClrMethodInstance(nativeOffset, &pMethodInst)) != S_OK) { return Status; } - if ((Status = pMethodInst->GetILOffsetsByAddress(native, 1, NULL, methodOffs)) != S_OK) + if (bAdjustOffsetForLineNumber) + { + CLRDATA_ADDRESS startAddr; + if (pMethodInst->GetRepresentativeEntryAddress(&startAddr) == S_OK) + { + if (nativeOffset >= (startAddr + g_targetMachine->StackWalkIPAdjustOffset())) + { + nativeOffset -= g_targetMachine->StackWalkIPAdjustOffset(); + } + } + } + + if ((Status = pMethodInst->GetILOffsetsByAddress(nativeOffset, 1, NULL, methodOffs)) != S_OK) { *methodOffs = 0; } @@ -4870,18 +4883,19 @@ ConvertNativeToIlOffset( // identifies the corresponding source file name and line number. HRESULT GetLineByOffset( - ___in ULONG64 offset, + ___in ULONG64 nativeOffset, ___out ULONG *pLinenum, __out_ecount(cchFileName) WCHAR* pwszFileName, - ___in ULONG cchFileName) + ___in ULONG cchFileName, + ___in BOOL bAdjustOffsetForLineNumber /* = FALSE */) { HRESULT Status = S_OK; ULONG32 methodToken; ULONG32 methodOffs; - // Find the image, method token and IL offset that correspond to "offset" + // Find the image, method token and IL offset that correspond to "nativeOffset" ToRelease pModule(NULL); - IfFailRet(ConvertNativeToIlOffset(offset, &pModule, &methodToken, &methodOffs)); + IfFailRet(ConvertNativeToIlOffset(nativeOffset, bAdjustOffsetForLineNumber, &pModule, &methodToken, &methodOffs)); ToRelease pMDImport(NULL); pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport); @@ -5281,9 +5295,7 @@ WString MethodNameFromIP(CLRDATA_ADDRESS ip, BOOL bSuppressLines, BOOL bAssembly ArrayHolder wszFileName = new WCHAR[MAX_LONGPATH]; if (!bSuppressLines && - // If the IP needs to be adjusted, it is a lot simpler to decrement IP instead of trying to figure out - // the beginning of the instruction. It is enough for GetLineByOffset to return the correct line number. - SUCCEEDED(GetLineByOffset(TO_CDADDR(bAdjustIPForLineNumber ? ip - g_targetMachine->StackWalkIPAdjustOffset() : ip), &linenum, wszFileName, MAX_LONGPATH))) + SUCCEEDED(GetLineByOffset(TO_CDADDR(ip), &linenum, wszFileName, MAX_LONGPATH, bAdjustIPForLineNumber))) { methodOutput += WString(W(" [")) + wszFileName + W(" @ ") + Decimal(linenum) + W("]"); } -- 2.34.1