Enable SyncBlk for xplat SOS (#20830)
authorMike McLaughlin <mikem@microsoft.com>
Tue, 6 Nov 2018 18:42:53 +0000 (10:42 -0800)
committerGitHub <noreply@github.com>
Tue, 6 Nov 2018 18:42:53 +0000 (10:42 -0800)
Add SyncBlk to xplat SOS.

src/ToolBox/SOS/Strike/sos_unixexports.src
src/ToolBox/SOS/Strike/sosdocsunix.txt
src/ToolBox/SOS/Strike/strike.cpp
src/ToolBox/SOS/lldbplugin/soscommand.cpp

index be8ad96..ac27c02 100644 (file)
@@ -43,6 +43,7 @@ IP2MD
 Name2EE
 PrintException
 StopOnCatch
+SyncBlk
 Threads
 ThreadState
 Token2EE
index 45e4c43..5cd3a87 100644 (file)
@@ -34,12 +34,12 @@ GCRoot (gcroot)                    GCInfo
 PrintException (pe)                EHInfo
                                    bpmd (bpmd)
                                    
-
 Examining CLR data structures      Diagnostic Utilities
 -----------------------------      -----------------------------
 DumpDomain                         VerifyHeap
 EEHeap (eeheap)                    FindAppDomain          
 Name2EE (name2ee)                  DumpLog (dumplog)
+SyncBlk (syncblk)
 DumpMT (dumpmt)
 DumpClass (dumpclass)
 DumpMD (dumpmd)                    
@@ -308,7 +308,7 @@ DumpHeap [-stat]
 
 DumpHeap is a powerful command that traverses the garbage collected heap, 
 collection statistics about objects. With it's various options, it can look for
-particular types, restrict to a range, or look for ThinLocks (see SyncBlk 
+particular types, restrict to a range, or look for ThinLocks (see syncblk 
 documentation). Finally, it will provide a warning if it detects excessive 
 fragmentation in the GC heap. 
 
@@ -361,7 +361,7 @@ The arguments in detail:
 -live     Only print live objects
 -dead     Only print dead objects (objects which will be collected in the
           next full GC)
--thinlock Report on any ThinLocks (an efficient locking scheme, see SyncBlk 
+-thinlock Report on any ThinLocks (an efficient locking scheme, see syncblk 
           documentation for more info)
 -startAtLowerBound 
           Force heap walk to begin at lower bound of a supplied address range.
@@ -1208,6 +1208,84 @@ all loaded modules in all domains. And remember that you can browse all the
 types in a module with DumpModule -mt <module pointer>.
 \\
 
+COMMAND: syncblk.
+SyncBlk [-all | <syncblk number>]
+
+A SyncBlock is a holder for extra information that doesn't need to be created 
+for every object. It can hold COM Interop data, HashCodes, and locking 
+information for thread-safe operations.
+
+When called without arguments, syncblk will print the list of SyncBlocks 
+corresponding to objects that are owned by a thread. For example, a
+
+    lock(MyObject)
+    {
+        ....
+    }
+
+statement will set MyObject to be owned by the current thread. A SyncBlock will
+be created for MyObject, and the thread ownership information stored there 
+(this is an oversimplification, see NOTE below). If another thread tries to 
+execute the same code, they won't be able to enter the block until the first 
+thread exits.
+
+This makes syncblk useful for detecting managed deadlocks. Consider that the
+following code is executed by Threads A & B:
+
+    Resource r1 = new Resource();
+    Resource r2 = new Resource();
+
+    ...
+    
+    lock(r1)                             lock(r2)
+    {                                    {
+        lock(r2)                             lock(r1)
+        {                                    {
+            ...                                  ...
+        }                                    }
+    }                                    }
+
+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
+
+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> bt
+        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 "clru 03f00229"),
+you can see that thread 3 is attempting to acquire the Resource 00a7a1a4, which
+is owned by thread 4.
+  
+NOTE:
+It is not always the case that a SyncBlock will be created for every object 
+that is locked by a thread. In version 2.0 of the CLR and above, a mechanism 
+called a ThinLock will be used if there is not already a SyncBlock for the 
+object in question. ThinLocks will not be reported by the syncblk command. 
+You can use "dumpheap -thinlock" to list objects locked in this way.
+\\
+
 COMMAND: dumpmt.
 DumpMT [-MD] <MethodTable address>
 
index b150848..d7794f4 100644 (file)
@@ -5141,6 +5141,8 @@ DECLARE_API(GCHeapStat)
 #endif // FEATURE_PAL
 }
 
+#endif // FEATURE_PAL
+
 /**********************************************************************\
 * Routine Description:                                                 *
 *                                                                      *
@@ -5323,14 +5325,18 @@ DECLARE_API(SyncBlk)
     
     ExtOut("-----------------------------\n");
     ExtOut("Total           %d\n", dwCount);
+#ifdef FEATURE_COMINTEROP
     ExtOut("CCW             %d\n", CCWCount);
     ExtOut("RCW             %d\n", RCWCount);
     ExtOut("ComClassFactory %d\n", CFCount);
+#endif
     ExtOut("Free            %d\n", freeCount);
    
     return Status;
 }
 
+#ifndef FEATURE_PAL
+
 #ifdef FEATURE_COMINTEROP
 struct VisitRcwArgs
 {
index 0a54f63..3565b9e 100644 (file)
@@ -145,6 +145,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     interpreter.AddCommand("ip2md", new sosCommand("IP2MD"), "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.");
     interpreter.AddCommand("name2ee", new sosCommand("Name2EE"), "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.");
     interpreter.AddCommand("pe", new sosCommand("PrintException"), "Displays and formats fields of any object derived from the Exception class at the specified address.");
+    interpreter.AddCommand("syncblk", new sosCommand("SyncBlk"), "Displays the SyncBlock holder info.");
     interpreter.AddCommand("histclear", new sosCommand("HistClear"), "Releases any resources used by the family of Hist commands.");
     interpreter.AddCommand("histinit", new sosCommand("HistInit"), "Initializes the SOS structures from the stress log saved in the debuggee.");
     interpreter.AddCommand("histobj", new sosCommand("HistObj"), "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.");