Skip to content

Commit

Permalink
Enable more SOS commands for Linux/MacOS
Browse files Browse the repository at this point in the history
Allows more SOS on Linux/MacOS and Linux core dumps on Windows under Windbg.

Issue: dotnet#3169
  • Loading branch information
mikem8361 committed Jul 19, 2022
1 parent 02e1b22 commit 7d07627
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 31 deletions.
15 changes: 9 additions & 6 deletions src/SOS/SOS.Hosting/Commands/SOSCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace SOS.Hosting
{
[Command(Name = "analyzeoom", DefaultOptions = "AnalyzeOOM", Help = "Displays the info of the last OOM occurred on an allocation request to the GC heap.")]
[Command(Name = "clrstack", DefaultOptions = "ClrStack", Help = "Provides a stack trace of managed code only.")]
[Command(Name = "clrthreads", DefaultOptions = "Threads", Help = "List the managed threads running.")]
[Command(Name = "dbgout", DefaultOptions = "dbgout", Help = "Enable/disable (-off) internal SOS logging.")]
Expand All @@ -26,11 +27,15 @@ namespace SOS.Hosting
[Command(Name = "dumpmodule", DefaultOptions = "DumpModule", Help = "Displays information about a EE module structure at the specified address.")]
[Command(Name = "dumpmt", DefaultOptions = "DumpMT", Help = "Displays information about a method table at the specified address.")]
[Command(Name = "dumpobj", DefaultOptions = "DumpObj", Aliases = new string[] { "do" }, Help = "Displays info about an object at the specified address.")]
[Command(Name = "dumpvc", DefaultOptions = "DumpVC", Help = "Displays info about the fields of a value class.")]
[Command(Name = "dumpruntimetypes", DefaultOptions = "DumpRuntimeTypes", Help = "Finds all System.RuntimeType objects in the gc heap and prints the type name and MethodTable they refer too.")]
[Command(Name = "dumpstackobjects", DefaultOptions = "DumpStackObjects", Aliases = new string[] { "dso" }, Help = "Displays all managed objects found within the bounds of the current stack.")]
[Command(Name = "dumpsig", DefaultOptions = "DumpSig", Help = "This command dumps the signature of a method or field given by <sigaddr> <moduleaddr>.")]
[Command(Name = "dumpsigelem", DefaultOptions = "DumpSigElem", Help = "This command dumps a single element of a signature object.")]
[Command(Name = "dumpvc", DefaultOptions = "DumpVC", Help = "Displays info about the fields of a value class.")]
[Command(Name = "eeheap", DefaultOptions = "EEHeap", Help = "Displays info about process memory consumed by internal runtime data structures.")]
[Command(Name = "eeversion", DefaultOptions = "EEVersion", Help = "Displays information about the runtime version.")]
[Command(Name = "finalizequeue", DefaultOptions = "FinalizeQueue", Help = "Displays all objects registered for finalization.")]
[Command(Name = "gchandles", DefaultOptions = "GCHandles", Help = "Provides statistics about GCHandles in the process.")]
[Command(Name = "gcroot", DefaultOptions = "GCRoot", Help = "Displays info about references (or roots) to an object at the specified address.")]
[Command(Name = "gcwhere", DefaultOptions = "GCWhere", Help = "Displays the location in the GC heap of the argument passed in.")]
[Command(Name = "ip2md", DefaultOptions = "IP2MD", Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")]
Expand All @@ -42,20 +47,18 @@ namespace SOS.Hosting
[Command(Name = "histobj", DefaultOptions = "HistObj", Help = "Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.")]
[Command(Name = "histobjfind", DefaultOptions = "HistObjFind", Help = "Displays all the log entries that reference an object at the specified address.")]
[Command(Name = "histroot", DefaultOptions = "HistRoot", Help = "Displays information related to both promotions and relocations of the specified root.")]
[Command(Name = "verifyheap", DefaultOptions = "VerifyHeap", Help = "Checks the GC heap for signs of corruption.")]
[Command(Name = "listnearobj", DefaultOptions = "ListNearObj", Help = "Displays the object preceding and succeeding the address specified.")]
[Command(Name = "threadpool", DefaultOptions = "ThreadPool", Help = "Lists basic information about the thread pool.")]
[Command(Name = "soshelp", DefaultOptions = "Help", Help = "Displays help for a specific SOS command.")]
[Command(Name = "verifyheap", DefaultOptions = "VerifyHeap", Help = "Checks the GC heap for signs of corruption.")]
[Command(Name = "verifyobj", DefaultOptions = "VerifyObj", Help = "Checks the object for signs of corruption.")]
[Command(Name = "dumprcw", DefaultOptions = "DumpRCW", Platform = CommandPlatform.Windows, Help = "Displays information about a Runtime Callable Wrapper.")]
[Command(Name = "dumpccw", DefaultOptions = "DumpCCW", Platform = CommandPlatform.Windows, Help = "Displays information about a COM Callable Wrapper.")]
[Command(Name = "dumppermissionset",DefaultOptions = "DumpPermissionSet", Platform = CommandPlatform.Windows, Help = "Displays a PermissionSet object (debug build only).")]
[Command(Name = "traverseheap", DefaultOptions = "TraverseHeap", Platform = CommandPlatform.Windows, Help = "Writes out a file in a format understood by the CLR Profiler.")]
[Command(Name = "analyzeoom", DefaultOptions = "AnalyzeOOM", Platform = CommandPlatform.Windows, Help = "Displays the info of the last OOM occurred on an allocation request to the GC heap.")]
[Command(Name = "verifyobj", DefaultOptions = "VerifyObj", Platform = CommandPlatform.Windows, Help = "Checks the object for signs of corruption.")]
[Command(Name = "listnearobj", DefaultOptions = "ListNearObj", Platform = CommandPlatform.Windows, Help = "Displays the object preceding and succeeding the address specified.")]
[Command(Name = "gcheapstat", DefaultOptions = "GCHeapStat", Platform = CommandPlatform.Windows, Help = "Display various GC heap stats.")]
[Command(Name = "watsonbuckets", DefaultOptions = "WatsonBuckets", Platform = CommandPlatform.Windows, Help = "Displays the Watson buckets.")]
[Command(Name = "comstate", DefaultOptions = "COMState", Platform = CommandPlatform.Windows, Help = "Lists the COM apartment model for each thread.")]
[Command(Name = "gchandles", DefaultOptions = "GCHandles", Help = "Provides statistics about GCHandles in the process.")]
[Command(Name = "objsize", DefaultOptions = "ObjSize", Platform = CommandPlatform.Windows, Help = "Lists the sizes of the all the objects found on managed threads.")]
[Command(Name = "gchandleleaks", DefaultOptions = "GCHandleLeaks", Platform = CommandPlatform.Windows, Help = "Helps in tracking down GCHandle leaks")]
public class SOSCommand : CommandBase
Expand Down
3 changes: 3 additions & 0 deletions src/SOS/Strike/sos_unixexports.src
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
; The .NET Foundation licenses this file to you under the MIT license.
; See the LICENSE file in the project root for more information.

AnalyzeOOM
bpmd
clrmodules
ClrStack
Expand Down Expand Up @@ -45,6 +46,7 @@ HistObjFind
HistRoot
HistStats
IP2MD
ListNearObj
logging
Name2EE
PrintException
Expand All @@ -63,6 +65,7 @@ ThreadState
Token2EE
u
VerifyHeap
VerifyObj

SOSInitializeByHost
SOSUninitializeByHost
114 changes: 111 additions & 3 deletions src/SOS/Strike/sosdocsunix.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ EEHeap (eeheap) FindAppDomain
Name2EE (name2ee) DumpLog (dumplog)
SyncBlk (syncblk) SuppressJitOptimization
DumpMT (dumpmt) ThreadPool (threadpool)
DumpClass (dumpclass)
DumpMD (dumpmd)
DumpClass (dumpclass) AnalyzeOOM (analyzeoom)
DumpMD (dumpmd) VerifyObj (verifyobj)
Token2EE
DumpModule (dumpmodule)
DumpAssembly (dumpassembly)
DumpRuntimeTypes
DumpRuntimeTypes (dumpruntimetypes)
DumpIL (dumpil)
DumpSig
DumpSigElem
Expand Down Expand Up @@ -1583,6 +1583,21 @@ is a simple example of the output for a dynamic method:
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: ret

\\

COMMAND: verifyobj.
VerifyObj <object address>

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 0680017c
object 0x680017c: bad member 00000001 at 06800184

\\

COMMAND: verifyheap.
Expand Down Expand Up @@ -1623,6 +1638,65 @@ in the CLR. In user code, an error in constructing PInvoke calls can cause
this problem, and running with Managed Debugging Assistants is advised. If that
possibility is eliminated, consider contacting Microsoft Product Support for
help.

\\

COMMAND: analyzeoom.
AnalyzeOOM

AnalyzeOOM displays the info of the last OOM occurred on an allocation request to
the GC heap (in Server GC it displays OOM, if any, on each GC heap).

To see the managed exception(s) use the clrthreads command which will show you
managed exception(s), if any, on each managed thread. If you do see an
OutOfMemoryException exception you can use the !PrintException command on it.
To get the full callstack use the "kb" command in the debugger for that thread.
For example, to display thread 3's stack use ~3kb.

OOM exceptions could be because of the following reasons:

1) allocation request to GC heap
in which case you will see JIT_New* on the call stack because managed code called new.
2) other runtime allocation failure
for example, failure to expand the finalize queue when GC.ReRegisterForFinalize is
called.
3) some other code you use throws a managed OOM exception
for example, some .NET framework code converts a native OOM exception to managed
and throws it.

The !AnalyzeOOM command aims to help you with investigating 1) which is the most
difficult because it requires some internal info from GC. The only exception is
we don't support allocating objects larger than 2GB on CLR v2.0 or prior. And this
command will not display any managed OOM because we will throw OOM right away
instead of even trying to allocate it on the GC heap.

There are 2 legitimate scenarios where GC would return OOM to allocation requests -
one is if the process is running out of VM space to reserve a segment; the other
is if the system is running out physical memory (+ page file if you have one) so
GC can not commit memory it needs. You can look at these scenarios by using performance
counters or debugger commands. For example for the former scenario the "!address
-summary" debugger command will show you the largest free region in the VM. For
the latter scenario you can look at the "Memory% Committed Bytes In Use" see
if you are running low on commit space. One important thing to keep in mind is
when you do this kind of memory analysis it could an aftereffect and doesn't
completely agree with what this command tells you, in which case the command should
be respected because it truly reflects what happened during GC.

The other cases should be fairly obvious from the callstack.

Sample output:

0:011> !analyzeoom
---------Heap 2 ---------
Managed OOM occurred after GC #28 (Requested to allocate 1234 bytes)
Reason: Didn't have enough memory to commit
Detail: SOH: Didn't have enough memory to grow the internal GC datastructures (800000 bytes) -
on GC entry available commit space was 500 MB
---------Heap 4 ---------
Managed OOM occurred after GC #12 (Requested to allocate 100000 bytes)
Reason: Didn't have enough memory to allocate an LOH segment
Detail: LOH: Failed to reserve memory (16777216 bytes)

\\

COMMAND: gcwhere.
Expand All @@ -1642,6 +1716,40 @@ the "size" is displayed as 0:
0280003c 2 0 02800000 02800038 0282b740 0
\\

COMMAND: listnearobj.
ListNearObj <object address>

ListNearObj is a diagnostic tool that displays the object preceeding and
succeeding the address passed in:

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 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
OR
prev_obj_addr + prev_obj_size = next_obj_addr

When the condition is not satisfied:

0:002> listnearobj 028000ec
Before: 0x28000a4 72 (0x48 ) System.StackOverflowException
After: 0x2800134 72 (0x48 ) System.Threading.ThreadAbortException
Heap local consistency not confirmed.

\\

COMMAND: dumplog.
DumpLog [-addr <addressOfStressLog>] [<Filename>]

Expand Down
22 changes: 0 additions & 22 deletions src/SOS/Strike/strike.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,6 @@ DECLARE_API(DumpSig)
INIT_API();

MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

//
// Fetch arguments
Expand Down Expand Up @@ -1092,7 +1091,6 @@ DECLARE_API(DumpSigElem)
INIT_API();

MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

//
// Fetch arguments
Expand Down Expand Up @@ -3940,7 +3938,6 @@ DECLARE_API(DumpRuntimeTypes)
{
INIT_API();
MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

BOOL dml = FALSE;

Expand Down Expand Up @@ -4495,7 +4492,6 @@ DECLARE_API(VerifyHeap)
{
INIT_API();
MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

if (!g_snapshot.Build())
{
Expand Down Expand Up @@ -4631,9 +4627,6 @@ DECLARE_API(AnalyzeOOM)
{
INIT_API();
MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

#ifndef FEATURE_PAL

if (!InitializeHeapData ())
{
Expand Down Expand Up @@ -4699,17 +4692,12 @@ DECLARE_API(AnalyzeOOM)
}

return S_OK;
#else
_ASSERTE(false);
return E_FAIL;
#endif // FEATURE_PAL
}

DECLARE_API(VerifyObj)
{
INIT_API();
MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

TADDR taddrObj = 0;
TADDR taddrMT;
Expand Down Expand Up @@ -4772,9 +4760,6 @@ DECLARE_API(ListNearObj)
{
INIT_API();
MINIDUMP_NOT_SUPPORTED();
ONLY_SUPPORTED_ON_WINDOWS_TARGET();

#if !defined(FEATURE_PAL)

TADDR taddrArg = 0;
TADDR taddrObj = 0;
Expand Down Expand Up @@ -4946,13 +4931,6 @@ DECLARE_API(ListNearObj)
}

return Status;

#else

_ASSERTE(false);
return E_FAIL;

#endif // FEATURE_PAL
}


Expand Down
2 changes: 2 additions & 0 deletions src/SOS/lldbplugin/soscommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
g_services->AddCommand("gcroot", new sosCommand("GCRoot"), "Displays info about references (or roots) to an object at the specified address.");
g_services->AddCommand("gcwhere", new sosCommand("GCWhere"), "Displays the location in the GC heap of the argument passed in.");
g_services->AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.");
g_services->AddCommand("listnearobj", new sosCommand("ListNearObj"), "displays the object preceeding and succeeding the address passed.");
g_services->AddCommand("loadsymbols", new sosCommand("SetSymbolServer", "-loadsymbols"), "Load the .NET Core native module symbols.");
g_services->AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.");
g_services->AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address.");
Expand All @@ -201,5 +202,6 @@ sosCommandInitialize(lldb::SBDebugger debugger)
g_services->AddCommand("threadstate", new sosCommand("ThreadState"), "Pretty prints the meaning of a threads state.");
g_services->AddCommand("token2ee", new sosCommand("token2ee"), "Displays the MethodTable structure and MethodDesc structure for the specified token and module.");
g_services->AddCommand("verifyheap", new sosCommand("VerifyHeap"), "Checks the GC heap for signs of corruption.");
g_services->AddCommand("verifyobj", new sosCommand("VerifyObj"), "Checks the object that is passed as an argument for signs of corruption.");
return true;
}

0 comments on commit 7d07627

Please sign in to comment.