Fix heap walking with global allocation context (#66844)
authorAndrew Au <andrewau@microsoft.com>
Tue, 22 Mar 2022 17:06:49 +0000 (10:06 -0700)
committerGitHub <noreply@github.com>
Tue, 22 Mar 2022 17:06:49 +0000 (10:06 -0700)
src/coreclr/debug/daccess/daccess.cpp
src/coreclr/debug/daccess/dacdbiimpl.cpp
src/coreclr/debug/daccess/dacimpl.h
src/coreclr/debug/daccess/request.cpp
src/coreclr/inc/clrconfigvalues.h
src/coreclr/inc/dacvars.h
src/coreclr/inc/sospriv.idl
src/coreclr/pal/prebuilt/idl/sospriv_i.cpp
src/coreclr/pal/prebuilt/inc/sospriv.h
src/coreclr/vm/gcheaputilities.cpp
src/coreclr/vm/gcheaputilities.h

index ff676ee..3981d21 100644 (file)
@@ -3283,6 +3283,10 @@ ClrDataAccess::QueryInterface(THIS_
     {
         ifaceRet = static_cast<ISOSDacInterface11*>(this);
     }
+    else if (IsEqualIID(interfaceId, __uuidof(ISOSDacInterface12)))
+    {
+        ifaceRet = static_cast<ISOSDacInterface12*>(this);
+    }
     else
     {
         *iface = NULL;
index f5420a3..9c5232b 100644 (file)
@@ -6524,7 +6524,7 @@ HRESULT DacHeapWalker::Init(CORDB_ADDRESS start, CORDB_ADDRESS end)
     if (threadStore != NULL)
     {
         int count = (int)threadStore->ThreadCountInEE();
-        mAllocInfo = new (nothrow) AllocInfo[count];
+        mAllocInfo = new (nothrow) AllocInfo[count + 1];
         if (mAllocInfo == NULL)
             return E_OUTOFMEMORY;
 
@@ -6551,6 +6551,11 @@ HRESULT DacHeapWalker::Init(CORDB_ADDRESS start, CORDB_ADDRESS end)
                 j++;
             }
         }
+        if ((&g_global_alloc_context)->alloc_ptr != nullptr)
+        {
+            mAllocInfo[j].Ptr = (CORDB_ADDRESS)(&g_global_alloc_context)->alloc_ptr;
+            mAllocInfo[j].Limit = (CORDB_ADDRESS)(&g_global_alloc_context)->alloc_limit;
+        }
 
         mThreadCount = j;
     }
index 7a03d98..90fddbc 100644 (file)
@@ -822,7 +822,8 @@ class ClrDataAccess
       public ISOSDacInterface8,
       public ISOSDacInterface9,
       public ISOSDacInterface10,
-      public ISOSDacInterface11
+      public ISOSDacInterface11,
+      public ISOSDacInterface12
 {
 public:
     ClrDataAccess(ICorDebugDataTarget * pTarget, ICLRDataTarget * pLegacyTarget=0);
@@ -1206,6 +1207,12 @@ public:
         CLRDATA_ADDRESS objAddr,
         CLRDATA_ADDRESS *taggedMemory,
         size_t *taggedMemorySizeInBytes);
+
+    // ISOSDacInterface12
+    virtual HRESULT STDMETHODCALLTYPE GetGlobalAllocationContext( 
+        CLRDATA_ADDRESS *allocPtr,
+        CLRDATA_ADDRESS *allocLimit);
+
     //
     // ClrDataAccess.
     //
index 6f24d4b..d182cc7 100644 (file)
@@ -5146,3 +5146,19 @@ HRESULT ClrDataAccess::GetTaggedMemory(
     SOSDacLeave();
     return hr;
 }
+
+HRESULT ClrDataAccess::GetGlobalAllocationContext(
+        CLRDATA_ADDRESS *allocPtr,
+        CLRDATA_ADDRESS *allocLimit)
+{
+    if (allocPtr == nullptr || allocLimit == nullptr)
+    {
+        return E_INVALIDARG;
+    }
+
+    SOSDacEnter();
+    *allocPtr = (CLRDATA_ADDRESS)((&g_global_alloc_context)->alloc_ptr);
+    *allocLimit = (CLRDATA_ADDRESS)((&g_global_alloc_context)->alloc_limit);
+    SOSDacLeave();
+    return hr;
+}
index 6291e1e..f4cdfc5 100644 (file)
@@ -280,6 +280,13 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_HeapVerify, W("HeapVerify"), 0, "When set v
 RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNumaAware, W("GCNumaAware"), 1, "Specifies if to enable GC NUMA aware")
 RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups")
 RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "")
+/**
+ * This flag allows us to force the runtime to use global allocation context on Windows x86/amd64 instead of thread allocation context just for testing purpose.
+ * The flag is unsafe for a subtle reason. Although the access to the g_global_alloc_context is protected under a lock. The implementation of
+ * that lock in the JIT helpers are not multi-core safe (in particular, it used and inc instruction without using the LOCK prefix). This is
+ * only useful for ad-hoc testing.
+ */
+CONFIG_DWORD_INFO(INTERNAL_GCUseGlobalAllocationContext, W("GCUseGlobalAllocationContext"), 0, "Force using the global allocation context for testing only")
 
 ///
 /// JIT
index 686c772..5c06f0e 100644 (file)
@@ -145,6 +145,7 @@ DEFINE_DACVAR(ULONG, ProfControlBlock, dac__g_profControlBlock, ::g_profControlB
 DEFINE_DACVAR(ULONG, PTR_DWORD, dac__g_card_table, ::g_card_table)
 DEFINE_DACVAR(ULONG, PTR_BYTE, dac__g_lowest_address, ::g_lowest_address)
 DEFINE_DACVAR(ULONG, PTR_BYTE, dac__g_highest_address, ::g_highest_address)
+DEFINE_DACVAR(ULONG, gc_alloc_context, dac__g_global_alloc_context, ::g_global_alloc_context)
 
 DEFINE_DACVAR(ULONG, IGCHeap, dac__g_pGCHeap, ::g_pGCHeap)
 
index 102801f..b2497cd 100644 (file)
@@ -449,3 +449,13 @@ interface ISOSDacInterface11 : IUnknown
     HRESULT IsTrackedType(CLRDATA_ADDRESS objAddr, BOOL* isTrackedType, BOOL* hasTaggedMemory);
     HRESULT GetTaggedMemory(CLRDATA_ADDRESS objAddr, CLRDATA_ADDRESS* taggedMemory, size_t* taggedMemorySizeInBytes);
 }
+
+[
+    object,
+    local,
+    uuid(1b93bacc-8ca4-432d-943a-3e6e7ec0b0a3)
+]
+interface ISOSDacInterface12 : IUnknown
+{
+    HRESULT GetGlobalAllocationContext(CLRDATA_ADDRESS* allocPtr, CLRDATA_ADDRESS* allocLimit);
+}
index 2f9afdc..b6559ab 100644 (file)
@@ -111,6 +111,10 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface10,0x90B8FCC3,0x7251,0x4B0A,0xAE,0x3D,
 
 MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface11,0x96BA1DB9,0x14CD,0x4492,0x80,0x65,0x1C,0xAA,0xEC,0xF6,0xE5,0xCF);
 
+
+MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface12,0x1b93bacc,0x8ca4,0x432d,0x94,0x3a,0x3e,0x6e,0x7e,0xc0,0xb0,0xa3);
+
+
 #undef MIDL_DEFINE_GUID
 
 #ifdef __cplusplus
index 5c8f17a..0276bff 100644 (file)
@@ -2988,10 +2988,89 @@ EXTERN_C const IID IID_ISOSDacInterface11;
 
 #endif  /* C style interface */
 
+#endif  /* __ISOSDacInterface11_INTERFACE_DEFINED__ */
+
+#ifndef __ISOSDacInterface12_INTERFACE_DEFINED__
+#define __ISOSDacInterface12_INTERFACE_DEFINED__
 
+/* interface ISOSDacInterface12 */
+/* [uuid][local][object] */ 
 
 
-#endif  /* __ISOSDacInterface11_INTERFACE_DEFINED__ */
+EXTERN_C const IID IID_ISOSDacInterface12;
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+    
+    MIDL_INTERFACE("1b93bacc-8ca4-432d-943a-3e6e7ec0b0a3")
+    ISOSDacInterface12 : public IUnknown
+    {
+    public:
+        virtual HRESULT STDMETHODCALLTYPE GetGlobalAllocationContext( 
+            CLRDATA_ADDRESS *allocPtr,
+            CLRDATA_ADDRESS *allocLimit) = 0;
+        
+    };
+    
+    
+#else  /* C style interface */
+
+    typedef struct ISOSDacInterface12Vtbl
+    {
+        BEGIN_INTERFACE
+        
+        HRESULT ( STDMETHODCALLTYPE *QueryInterface )( 
+            ISOSDacInterface12 * This,
+            /* [in] */ REFIID riid,
+            /* [annotation][iid_is][out] */ 
+            _COM_Outptr_  void **ppvObject);
+        
+        ULONG ( STDMETHODCALLTYPE *AddRef )( 
+            ISOSDacInterface12 * This);
+        
+        ULONG ( STDMETHODCALLTYPE *Release )( 
+            ISOSDacInterface12 * This);
+        
+        HRESULT ( STDMETHODCALLTYPE *GetGlobalAllocationContext )( 
+            ISOSDacInterface12 * This,
+            CLRDATA_ADDRESS *allocPtr,
+            CLRDATA_ADDRESS *allocLimit);
+        
+        END_INTERFACE
+    } ISOSDacInterface12Vtbl;
+
+    interface ISOSDacInterface12
+    {
+        CONST_VTBL struct ISOSDacInterface12Vtbl *lpVtbl;
+    };
+
+    
+
+#ifdef COBJMACROS
+
+
+#define ISOSDacInterface12_QueryInterface(This,riid,ppvObject) \
+    ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) 
+
+#define ISOSDacInterface12_AddRef(This)        \
+    ( (This)->lpVtbl -> AddRef(This) ) 
+
+#define ISOSDacInterface12_Release(This)       \
+    ( (This)->lpVtbl -> Release(This) ) 
+
+
+#define ISOSDacInterface12_GetGlobalAllocationContext(This,allocPtr,allocLimit)        \
+    ( (This)->lpVtbl -> GetGlobalAllocationContext(This,allocPtr,allocLimit) ) 
+
+#endif /* COBJMACROS */
+
+
+#endif         /* C style interface */
+
+
+
+
+#endif         /* __ISOSDacInterface12_INTERFACE_DEFINED__ */
+
 
 /* Additional Prototypes for ALL interfaces */
 
index 08f864b..791c4d6 100644 (file)
@@ -35,7 +35,7 @@ bool g_sw_ww_enabled_for_gc_heap = false;
 
 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
 
-gc_alloc_context g_global_alloc_context = {};
+GVAL_IMPL_INIT(gc_alloc_context, g_global_alloc_context, {});
 
 enum GC_LOAD_STATUS {
     GC_LOAD_STATUS_BEFORE_START,
@@ -58,6 +58,8 @@ VersionInfo g_gc_version_info;
 // The module that contains the GC.
 PTR_VOID g_gc_module_base;
 
+bool GCHeapUtilities::s_useThreadAllocationContexts;
+
 // GC entrypoints for the the linked-in GC. These symbols are invoked
 // directly if we are not using a standalone GC.
 extern "C" void GC_VersionInfo(/* Out */ VersionInfo* info);
@@ -307,6 +309,19 @@ HRESULT GCHeapUtilities::LoadAndInitialize()
 {
     LIMITED_METHOD_CONTRACT;
 
+    // When running on a single-proc Intel system, it's more efficient to use a single global
+    // allocation context for SOH allocations than to use one for every thread.
+#if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(TARGET_UNIX)
+#if DEBUG
+    bool useGlobalAllocationContext = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCUseGlobalAllocationContext) != 0);
+#else
+    bool useGlobalAllocationContext = false;
+#endif
+    s_useThreadAllocationContexts = !useGlobalAllocationContext && (IsServerHeap() || ::g_SystemInfo.dwNumberOfProcessors != 1 || CPUGroupInfo::CanEnableGCCPUGroups());
+#else
+    s_useThreadAllocationContexts = true;
+#endif
+
     // we should only call this once on startup. Attempting to load a GC
     // twice is an error.
     assert(g_pGCHeap == nullptr);
index 3d64886..ed8d16d 100644 (file)
@@ -16,15 +16,15 @@ GPTR_DECL(uint8_t,g_lowest_address);
 GPTR_DECL(uint8_t,g_highest_address);
 GPTR_DECL(uint32_t,g_card_table);
 GVAL_DECL(GCHeapType, g_heap_type);
-#ifndef DACCESS_COMPILE
-}
-#endif // !DACCESS_COMPILE
 
 // For single-proc machines, the EE will use a single, shared alloc context
 // for all allocations. In order to avoid extra indirections in assembly
 // allocation helpers, the EE owns the global allocation context and the
 // GC will update it when it needs to.
-extern "C" gc_alloc_context g_global_alloc_context;
+GVAL_DECL(gc_alloc_context, g_global_alloc_context);
+#ifndef DACCESS_COMPILE
+}
+#endif // !DACCESS_COMPILE
 
 extern "C" uint32_t* g_card_bundle_table;
 extern "C" uint8_t* g_ephemeral_low;
@@ -121,14 +121,7 @@ public:
 
     static bool UseThreadAllocationContexts()
     {
-        // When running on a single-proc Intel system, it's more efficient to use a single global
-        // allocation context for SOH allocations than to use one for every thread.
-#if (defined(TARGET_X86) || defined(TARGET_AMD64)) && !defined(TARGET_UNIX)
-        return IsServerHeap() || ::g_SystemInfo.dwNumberOfProcessors != 1 || CPUGroupInfo::CanEnableGCCPUGroups();
-#else
-        return true;
-#endif
-
+        return s_useThreadAllocationContexts;
     }
 
 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
@@ -212,6 +205,8 @@ public:
 private:
     // This class should never be instantiated.
     GCHeapUtilities() = delete;
+
+    static bool s_useThreadAllocationContexts;
 };
 
 #endif // _GCHEAPUTILITIES_H_