Skip to content

Commit

Permalink
Enable more SOS commands for Linux/MacOS (#3233)
Browse files Browse the repository at this point in the history
Enable more SOS commands for Linux/MacOS

Allows more SOS on Linux/MacOS and Linux core dumps on Windows under Windbg.

Commands enabled for Linux/MacOS on lldb, windbg and dotnet-dump:

DumpSig
DumpSigElem
DumpRuntimeTypes
VerifyHeap
AnalyzeOOM
VerifyObj
ListNearObj
GCHeapStat
ThreadPool
EHInfo
GCInfo
ObjSize
FindRoots

Under dotnet-dump and lldb this commands are lowercase.

Issue: #3169

Fix invalid formatting char problems on Windows on a few of the commands.

Fix dumpgcdata for server GCs

Fix verifyheap failures

Fix GCHeapTraverse for empty POH

Co-authored-by: Andrew Au <[email protected]>
  • Loading branch information
mikem8361 and cshung authored Jul 29, 2022
1 parent 6d8a27a commit e842703
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 152 deletions.
35 changes: 22 additions & 13 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 @@ -19,44 +20,52 @@ namespace SOS.Hosting
[Command(Name = "dumpclass", DefaultOptions = "DumpClass", Help = "Displays information about a EE class structure at the specified address.")]
[Command(Name = "dumpdelegate", DefaultOptions = "DumpDelegate", Help = "Displays information about a delegate.")]
[Command(Name = "dumpdomain", DefaultOptions = "DumpDomain", Help = "Displays information all the AppDomains and all assemblies within the domains.")]
[Command(Name = "dumpgcdata", DefaultOptions = "DumpGCData", Help = "Displays information about the GC data.")]
[Command(Name = "dumpheap", DefaultOptions = "DumpHeap", Help = "Displays info about the garbage-collected heap and collection statistics about objects.")]
[Command(Name = "dumpil", DefaultOptions = "DumpIL", Help = "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.")]
[Command(Name = "dumplog", DefaultOptions = "DumpLog", Help = "Writes the contents of an in-memory stress log to the specified file.")]
[Command(Name = "dumpmd", DefaultOptions = "DumpMD", Help = "Displays information about a MethodDesc structure at the specified address.")]
[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 = "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 = "dumpstackobjects", DefaultOptions = "DumpStackObjects", Aliases = new string[] { "dso" }, Help = "Displays all managed objects found within the bounds of the current stack.")]
[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 = "ehinfo", DefaultOptions = "EHInfo", Help = "Displays the exception handling blocks in a jitted method.")]
[Command(Name = "finalizequeue", DefaultOptions = "FinalizeQueue", Help = "Displays all objects registered for finalization.")]
[Command(Name = "findappdomain", DefaultOptions = "FindAppDomain", Help = "Attempts to resolve the AppDomain of a GC object.")]
[Command(Name = "gchandles", DefaultOptions = "GCHandles", Help = "Provides statistics about GCHandles in the process.")]
[Command(Name = "gcheapstat", DefaultOptions = "GCHeapStat", Help = "Display various GC heap stats.")]
[Command(Name = "gcroot", DefaultOptions = "GCRoot", Help = "Displays info about references (or roots) to an object at the specified address.")]
[Command(Name = "gcinfo", DefaultOptions = "GCInfo", Help = "Displays info JIT GC encoding for a method.")]
[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.")]
[Command(Name = "name2ee", DefaultOptions = "Name2EE", Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")]
[Command(Name = "printexception", DefaultOptions = "PrintException", Aliases = new string[] { "pe" }, Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
[Command(Name = "syncblk", DefaultOptions = "SyncBlk", Help = "Displays the SyncBlock holder info.")]
[Command(Name = "histclear", DefaultOptions = "HistClear", Help = "Releases any resources used by the family of Hist commands.")]
[Command(Name = "histinit", DefaultOptions = "HistInit", Help = "Initializes the SOS structures from the stress log saved in the debuggee.")]
[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 = "threadpool", DefaultOptions = "ThreadPool", Help = "Lists basic information about the thread pool.")]
[Command(Name = "histstats", DefaultOptions = "HistStats", Help = "Displays stress log stats.")]
[Command(Name = "ip2md", DefaultOptions = "IP2MD", Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")]
[Command(Name = "listnearobj", DefaultOptions = "ListNearObj", Help = "Displays the object preceding and succeeding the address specified.")]
[Command(Name = "name2ee", DefaultOptions = "Name2EE", Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")]
[Command(Name = "objsize", DefaultOptions = "ObjSize", Help = "Lists the sizes of the all the objects found on managed threads.")]
[Command(Name = "pathto", DefaultOptions = "PathTo", Help = "Displays the GC path from <root> to <target>.")]
[Command(Name = "printexception", DefaultOptions = "PrintException", Aliases = new string[] { "pe" }, Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")]
[Command(Name = "soshelp", DefaultOptions = "Help", Help = "Displays help for a specific SOS command.")]
[Command(Name = "syncblk", DefaultOptions = "SyncBlk", Help = "Displays the SyncBlock holder info.")]
[Command(Name = "threadpool", DefaultOptions = "ThreadPool", Help = "Lists basic information about the thread pool.")]
[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", 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
7 changes: 7 additions & 0 deletions src/SOS/SOS.UnitTests/Scripts/GCPOH.script
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ SOSCOMMAND:DumpObj <POUT>\w+\s+(<HEXVAL>)\s+(System.Byte\[\]!\$0_)*System.Byte\[
VERIFY:\s+Name: System.Byte\[\]\s+
VERIFY:\s+Array: Rank 1, Number of elements 100, Type Byte\s+

SOSCOMMAND:ObjSize <PREVPOUT>

SOSCOMMAND:GCWhere <PREVPOUT>
# we care that the Gen is 4 (POH)
VERIFY:<HEXVAL>\s+4\s+\d\s+<HEXVAL>\s+<HEXVAL>\s+<HEXVAL>\s+0x<HEXVAL>\s*\(\d+\)
Expand All @@ -36,6 +38,11 @@ SOSCOMMAND:GCRoot -all <PREVPOUT>
VERIFY:.*Thread <HEXVAL>:
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+GCPOH\.Main\(\)\s+\[.*[Gg][Cc][Pp][Oo][Hh]\.cs\s+@\s+19\]\s+

SOSCOMMAND:VerifyHeap
VERIFY:\s*No heap corruption detected.\s*

SOSCOMMAND:GCHeapStat

SOSCOMMAND:DumpHeap
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<DECVAL>\s+

Expand Down
10 changes: 10 additions & 0 deletions src/SOS/SOS.UnitTests/Scripts/OtherCommands.script
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ VERIFY:\s*<DECVAL>\s+0x<HEXVAL>\s+\(<DECVAL>\)\s+
EXTCOMMAND:registers
VERIFY:\s*([r|e]ip|pc) = 0x<HEXVAL>\s+

SOSCOMMAND:ThreadPool

SOSCOMMAND:VerifyHeap

SOSCOMMAND:DumpHeap
VERIFY:\s+Address\s+MT\s+Size\s+
VERIFY:\s+<HEXVAL>\s+<HEXVAL>\s+<DECVAL>.*
Expand Down Expand Up @@ -131,3 +135,9 @@ VERIFY:\s*Total\s+<DECVAL>
VERIFY:\s*CCW\s+<DECVAL>
VERIFY:\s*RCW\s+<DECVAL>
ENDIF:WINDOWS

SOSCOMMAND:GCHandles

SOSCOMMAND:DumpGCData

SOSCOMMAND:DumpRuntimeTypes
17 changes: 2 additions & 15 deletions src/SOS/Strike/eeheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include "safemath.h"
#include "releaseholder.h"


// This is the increment for the segment lookup data
const int nSegLookupStgIncrement = 100;

Expand Down Expand Up @@ -849,7 +848,6 @@ BOOL GCObjInHeap(TADDR taddrObj, const GCHeapDetails &heap, TADDR_SEGINFO& rngSe
return FALSE;
}

#ifndef FEATURE_PAL
// this function updates genUsage to reflect statistics from the range defined by [start, end)
void GCGenUsageStats(TADDR start, TADDR alloc_end, TADDR commit_end, const std::unordered_set<TADDR> &liveObjs,
const GCHeapDetails &heap, BOOL bLarge, BOOL bPinned, const AllocInfo *pAllocInfo, GenUsageStat *genUsage)
Expand Down Expand Up @@ -943,7 +941,6 @@ void GCGenUsageStats(TADDR start, TADDR alloc_end, TADDR commit_end, const std::
}
}
}
#endif // !FEATURE_PAL

BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsageStat *hpUsage)
{
Expand Down Expand Up @@ -977,9 +974,7 @@ BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsage
ExtErr("Error requesting heap segment %p\n", SOS_PTR(taddrSeg));
return FALSE;
}
#ifndef FEATURE_PAL
GCGenUsageStats((TADDR)dacpSeg.mem, (TADDR)dacpSeg.highAllocMark, (TADDR)dacpSeg.committed, liveObjs, heap, FALSE, FALSE, &allocInfo, &hpUsage->genUsage[n]);
#endif
taddrSeg = (TADDR)dacpSeg.next;
}
}
Expand All @@ -989,7 +984,6 @@ BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsage
// 1. Start with small object segments
taddrSeg = (TADDR)heap.generation_table[GetMaxGeneration()].start_segment;

#ifndef FEATURE_PAL
// 1a. enumerate all non-ephemeral segments
while (taddrSeg != (TADDR)heap.generation_table[0].start_segment)
{
Expand All @@ -1004,7 +998,6 @@ BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsage
GCGenUsageStats((TADDR)dacpSeg.mem, (TADDR)dacpSeg.allocated, (TADDR)dacpSeg.committed, liveObjs, heap, FALSE, FALSE, &allocInfo, &hpUsage->genUsage[2]);
taddrSeg = (TADDR)dacpSeg.next;
}
#endif

// 1b. now handle the ephemeral segment
if (dacpSeg.Request(g_sos, taddrSeg, heap.original_heap_details) != S_OK)
Expand All @@ -1028,9 +1021,7 @@ BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsage
startGen = TO_TADDR(heap.generation_table[n].allocation_start);
}

#ifndef FEATURE_PAL
GCGenUsageStats(startGen, endGen, (TADDR)dacpSeg.committed, liveObjs, heap, FALSE, FALSE, &allocInfo, &hpUsage->genUsage[n]);
#endif
endGen = startGen;
}
}
Expand All @@ -1048,9 +1039,7 @@ BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsage
return FALSE;
}

#ifndef FEATURE_PAL
GCGenUsageStats((TADDR) dacpSeg.mem, (TADDR) dacpSeg.allocated, (TADDR) dacpSeg.committed, liveObjs, heap, TRUE, FALSE, NULL, &hpUsage->genUsage[3]);
#endif
taddrSeg = (TADDR)dacpSeg.next;
}

Expand All @@ -1069,9 +1058,7 @@ BOOL GCHeapUsageStats(const GCHeapDetails& heap, BOOL bIncUnreachable, HeapUsage
return FALSE;
}

#ifndef FEATURE_PAL
GCGenUsageStats((TADDR) dacpSeg.mem, (TADDR) dacpSeg.allocated, (TADDR) dacpSeg.committed, liveObjs, heap, FALSE, TRUE, NULL, &hpUsage->genUsage[4]);
#endif
taddrSeg = (TADDR)dacpSeg.next;
}
}
Expand Down Expand Up @@ -1197,7 +1184,7 @@ void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, B
SOS_PTR(SegQueueLimit(heapDetails,gen_segment(m))));
}
}
#ifndef FEATURE_PAL

if (bAllReady)
{
if (!bShort)
Expand All @@ -1210,7 +1197,6 @@ void GatherOneHeapFinalization(DacpGcHeapDetails& heapDetails, HeapStat *stat, B

PrintNotReachableInRange(rngStart, rngEnd, TRUE, bAllReady ? stat : NULL, bShort);
}
#endif

if (!bShort)
{
Expand Down Expand Up @@ -1585,6 +1571,7 @@ BOOL GCHeapTraverse(const GCHeapDetails &heap, AllocInfo* pallocInfo, VISITGCHEA
}

dwAddrCurrObj = (DWORD_PTR)segment.mem;
continue;
}
else
{
Expand Down
14 changes: 1 addition & 13 deletions src/SOS/Strike/sos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,9 @@

#include "strike.h"
#include "util.h"

#include "sos.h"


#ifdef _ASSERTE
#undef _ASSERTE
#endif

#define _ASSERTE(a) {;}

#include "gcdesc.h"


#undef _ASSERTE

namespace sos
{
template <class T>
Expand Down Expand Up @@ -505,7 +493,7 @@ namespace sos
int entries = 0;

if (FAILED(MOVE(entries, mt-sizeof(TADDR))))
Throw<DataRead>("Failed to request number of entries.");
Throw<DataRead>("Failed to request number of entries for %p MT %p", mObject, mt);

// array of vc?
if (entries < 0)
Expand Down
2 changes: 1 addition & 1 deletion src/SOS/Strike/sos.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ namespace sos
inline const SyncBlkIterator &operator++()
{
SOS_Assert(mCurr <= mTotal);
mSyncBlk = ++mCurr;
mSyncBlk = mCurr++;

return *this;
}
Expand Down
9 changes: 7 additions & 2 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 @@ -29,15 +30,17 @@ DumpStackObjects
DumpVC
EEHeap
EEVersion
GCWhere
EEStack
EHInfo
ext
FinalizeQueue
FindAppDomain
FindRoots
GCHandles
GCHeapStat
GCInfo
GCRoot
GCWhere
Help
HistClear
HistInit
Expand All @@ -46,17 +49,18 @@ HistObjFind
HistRoot
HistStats
IP2MD
ListNearObj
logging
Name2EE
ObjSize
PrintException
PathTo
runtimes
StopOnCatch
SetClrPath
SetSymbolServer
SOSFlush
SOSStatus
runtimes
SuppressJitOptimization
SyncBlk
Threads
Expand All @@ -65,6 +69,7 @@ ThreadState
Token2EE
u
VerifyHeap
VerifyObj

SOSInitializeByHost
SOSUninitializeByHost
Loading

0 comments on commit e842703

Please sign in to comment.