diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 4788dd322d2e1e..f7bbcf3dc6590a 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -6518,10 +6518,11 @@ HRESULT DacHeapWalker::Init(CORDB_ADDRESS start, CORDB_ADDRESS end) j++; } } - if ((&g_global_alloc_context)->alloc_ptr != nullptr) + gc_alloc_context globalCtx = ((ee_alloc_context)g_global_alloc_context).m_GCAllocContext; + if (globalCtx.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; + mAllocInfo[j].Ptr = (CORDB_ADDRESS)globalCtx.alloc_ptr; + mAllocInfo[j].Limit = (CORDB_ADDRESS)globalCtx.alloc_limit; } mThreadCount = j; diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 485503e025c4ac..5c124d8e76ce7d 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -795,7 +795,7 @@ ClrDataAccess::GetThreadAllocData(CLRDATA_ADDRESS addr, struct DacpAllocData *da Thread* thread = PTR_Thread(TO_TADDR(addr)); - PTR_gc_alloc_context pAllocContext = thread->GetAllocContext(); + gc_alloc_context* pAllocContext = thread->GetAllocContext(); if (pAllocContext != NULL) { @@ -860,7 +860,7 @@ HRESULT ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThre threadData->state = thread->m_State; threadData->preemptiveGCDisabled = thread->m_fPreemptiveGCDisabled; - PTR_gc_alloc_context allocContext = thread->GetAllocContext(); + gc_alloc_context* allocContext = thread->GetAllocContext(); if (allocContext) { threadData->allocContextPtr = TO_CDADDR(allocContext->alloc_ptr); @@ -5351,8 +5351,9 @@ HRESULT ClrDataAccess::GetGlobalAllocationContext( } SOSDacEnter(); - *allocPtr = (CLRDATA_ADDRESS)((&g_global_alloc_context)->alloc_ptr); - *allocLimit = (CLRDATA_ADDRESS)((&g_global_alloc_context)->alloc_limit); + gc_alloc_context global_alloc_context = ((ee_alloc_context)g_global_alloc_context).m_GCAllocContext; + *allocPtr = (CLRDATA_ADDRESS)global_alloc_context.alloc_ptr; + *allocLimit = (CLRDATA_ADDRESS)global_alloc_context.alloc_limit; SOSDacLeave(); return hr; } diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index cb7bb72627090c..c5dbe51974798e 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -134,9 +134,14 @@ CDAC_TYPE_END(ThreadStore) CDAC_TYPE_BEGIN(RuntimeThreadLocals) CDAC_TYPE_INDETERMINATE(RuntimeThreadLocals) -CDAC_TYPE_FIELD(RuntimeThreadLocals, AllocContext, AllocContext, offsetof(RuntimeThreadLocals, alloc_context)) +CDAC_TYPE_FIELD(RuntimeThreadLocals, /*EEAllocContext*/, AllocContext, offsetof(RuntimeThreadLocals, alloc_context)) CDAC_TYPE_END(RuntimeThreadLocals) +CDAC_TYPE_BEGIN(EEAllocContext) +CDAC_TYPE_INDETERMINATE(EEAllocContext) +CDAC_TYPE_FIELD(EEAllocContext, /*GCAllocContext*/, GCAllocationContext, offsetof(ee_alloc_context, m_GCAllocContext)) +CDAC_TYPE_END(EEAllocContext) + CDAC_TYPE_BEGIN(GCAllocContext) CDAC_TYPE_INDETERMINATE(GCAllocContext) CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, alloc_ptr)) diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h index 31f75e52246e47..afc29e837a9146 100644 --- a/src/coreclr/gc/gcinterface.ee.h +++ b/src/coreclr/gc/gcinterface.ee.h @@ -281,6 +281,9 @@ class IGCToCLR { gc_alloc_context * GetAllocContext() PURE_VIRTUAL // Calls the given enum_alloc_context_func with every active alloc context. + // NOTE: The GC may mutate the allocation context fields inside the callback. + // If the GC does modify the fields, the only legal modification for the alloc_ptr + // alloc_limit fields is to setting them both to zero. virtual void GcEnumAllocContexts(enum_alloc_context_func* fn, void* param) PURE_VIRTUAL diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index b2b983f1078a98..ffe2be90ffb8fa 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -140,7 +140,7 @@ DEFINE_DACVAR(ProfControlBlock, dac__g_profControlBlock, ::g_profControlBlock) DEFINE_DACVAR(PTR_DWORD, dac__g_card_table, ::g_card_table) DEFINE_DACVAR(PTR_BYTE, dac__g_lowest_address, ::g_lowest_address) DEFINE_DACVAR(PTR_BYTE, dac__g_highest_address, ::g_highest_address) -DEFINE_DACVAR(gc_alloc_context, dac__g_global_alloc_context, ::g_global_alloc_context) +DEFINE_DACVAR(ee_alloc_context, dac__g_global_alloc_context, ::g_global_alloc_context) DEFINE_DACVAR(IGCHeap, dac__g_pGCHeap, ::g_pGCHeap) diff --git a/src/coreclr/vm/amd64/JitHelpers_Slow.asm b/src/coreclr/vm/amd64/JitHelpers_Slow.asm index e1bf5410929533..63bc8cc43f1aec 100644 --- a/src/coreclr/vm/amd64/JitHelpers_Slow.asm +++ b/src/coreclr/vm/amd64/JitHelpers_Slow.asm @@ -178,15 +178,15 @@ LEAF_ENTRY JIT_TrialAllocSFastSP, _TEXT inc [g_global_alloc_lock] jnz JIT_NEW - mov rax, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr] ; alloc_ptr - mov r10, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_limit] ; limit_ptr + mov rax, [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr] ; alloc_ptr + mov r10, [g_global_alloc_context + OFFSETOF__ee_alloc_context__m_CombinedLimit] ; m_CombinedLimit add r8, rax cmp r8, r10 ja AllocFailed - mov qword ptr [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr], r8 ; update the alloc ptr + mov qword ptr [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr], r8 ; update the alloc ptr mov [rax], rcx mov [g_global_alloc_lock], -1 @@ -206,8 +206,8 @@ NESTED_ENTRY JIT_BoxFastUP, _TEXT inc [g_global_alloc_lock] jnz JIT_Box - mov rax, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr] ; alloc_ptr - mov r10, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_limit] ; limit_ptr + mov rax, [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr] ; alloc_ptr + mov r10, [g_global_alloc_context + OFFSETOF__ee_alloc_context__m_CombinedLimit] ; m_CombinedLimit add r8, rax @@ -217,7 +217,7 @@ NESTED_ENTRY JIT_BoxFastUP, _TEXT test rdx, rdx je NullRef - mov qword ptr [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr], r8 ; update the alloc ptr + mov qword ptr [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr], r8 ; update the alloc ptr mov [rax], rcx mov [g_global_alloc_lock], -1 @@ -285,15 +285,15 @@ LEAF_ENTRY AllocateStringFastUP, _TEXT inc [g_global_alloc_lock] jnz FramedAllocateString - mov rax, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr] ; alloc_ptr - mov r10, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_limit] ; limit_ptr + mov rax, [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr] ; alloc_ptr + mov r10, [g_global_alloc_context + OFFSETOF__ee_alloc_context__m_CombinedLimit] ; m_CombinedLimit add r8, rax cmp r8, r10 ja AllocFailed - mov qword ptr [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr], r8 ; update the alloc ptr + mov qword ptr [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr], r8 ; update the alloc ptr mov [rax], r11 mov [g_global_alloc_lock], -1 @@ -341,8 +341,8 @@ LEAF_ENTRY JIT_NewArr1VC_UP, _TEXT inc [g_global_alloc_lock] jnz JIT_NewArr1 - mov rax, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr] ; alloc_ptr - mov r10, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_limit] ; limit_ptr + mov rax, [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr] ; alloc_ptr + mov r10, [g_global_alloc_context + OFFSETOF__ee_alloc_context__m_CombinedLimit] ; m_CombinedLimit add r8, rax jc AllocFailed @@ -350,7 +350,7 @@ LEAF_ENTRY JIT_NewArr1VC_UP, _TEXT cmp r8, r10 ja AllocFailed - mov qword ptr [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr], r8 ; update the alloc ptr + mov qword ptr [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr], r8 ; update the alloc ptr mov [rax], rcx mov [g_global_alloc_lock], -1 @@ -394,15 +394,15 @@ LEAF_ENTRY JIT_NewArr1OBJ_UP, _TEXT inc [g_global_alloc_lock] jnz JIT_NewArr1 - mov rax, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr] ; alloc_ptr - mov r10, [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_limit] ; limit_ptr + mov rax, [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr] ; alloc_ptr + mov r10, [g_global_alloc_context + OFFSETOF__ee_alloc_context__m_CombinedLimit] ; m_CombinedLimit add r8, rax cmp r8, r10 ja AllocFailed - mov qword ptr [g_global_alloc_context + OFFSETOF__gc_alloc_context__alloc_ptr], r8 ; update the alloc ptr + mov qword ptr [g_global_alloc_context + OFFSETOF__ee_alloc_context__alloc_ptr], r8 ; update the alloc ptr mov [rax], rcx mov [g_global_alloc_lock], -1 diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index bd5eb832005c95..8815f9ca8b354c 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -111,11 +111,12 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_pFrame #define Thread_m_pFrame OFFSETOF__Thread__m_pFrame -#define OFFSETOF__gc_alloc_context__alloc_ptr 0x0 -ASMCONSTANT_OFFSETOF_ASSERT(gc_alloc_context, alloc_ptr); +#define OFFSETOF__ee_alloc_context__alloc_ptr 0x8 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ee_alloc_context__alloc_ptr == offsetof(ee_alloc_context, m_GCAllocContext) + + offsetof(gc_alloc_context, alloc_ptr)); -#define OFFSETOF__gc_alloc_context__alloc_limit 0x8 -ASMCONSTANT_OFFSETOF_ASSERT(gc_alloc_context, alloc_limit); +#define OFFSETOF__ee_alloc_context__m_CombinedLimit 0x0 +ASMCONSTANTS_C_ASSERT(OFFSETOF__ee_alloc_context__m_CombinedLimit == offsetof(ee_alloc_context, m_CombinedLimit)); #define OFFSETOF__ThreadExceptionState__m_pCurrentTracker 0x000 ASMCONSTANTS_C_ASSERT(OFFSETOF__ThreadExceptionState__m_pCurrentTracker diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index d5c0df8c5a4fd0..21fdb570d1aa94 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -908,7 +908,7 @@ FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread) INT64 currentAllocated = 0; Thread *pThread = GetThread(); - gc_alloc_context* ac = &t_runtime_thread_locals.alloc_context; + gc_alloc_context* ac = &t_runtime_thread_locals.alloc_context.m_GCAllocContext; currentAllocated = ac->alloc_bytes + ac->alloc_bytes_uoh - (ac->alloc_limit - ac->alloc_ptr); return currentAllocated; diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index b7ae97613d507d..8403ab0837174c 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1834,7 +1834,7 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) // BUG(github #10318) - when not using allocation contexts, the alloc lock // must be acquired here. Until fixed, this assert prevents random heap corruption. assert(GCHeapUtilities::UseThreadAllocationContexts()); - GCHeapUtilities::GetGCHeap()->StressHeap(&t_runtime_thread_locals.alloc_context); + GCHeapUtilities::GetGCHeap()->StressHeap(&t_runtime_thread_locals.alloc_context.m_GCAllocContext); // StressHeap can exit early w/o forcing a SuspendEE to trigger the instruction update // We can not rely on the return code to determine if the instruction update happened diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index b4cdca7f272b4c..55329274905a5d 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -445,7 +445,7 @@ gc_alloc_context * GCToEEInterface::GetAllocContext() return nullptr; } - return &t_runtime_thread_locals.alloc_context; + return &t_runtime_thread_locals.alloc_context.m_GCAllocContext; } void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param) @@ -462,16 +462,29 @@ void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* par Thread * pThread = NULL; while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) { - gc_alloc_context* palloc_context = pThread->GetAllocContext(); + ee_alloc_context* palloc_context = pThread->GetEEAllocContext(); if (palloc_context != nullptr) { - fn(palloc_context, param); + gc_alloc_context* ac = &palloc_context->m_GCAllocContext; + fn(ac, param); + // The GC may zero the alloc_ptr and alloc_limit fields of AC during enumeration and we need to keep + // m_CombinedLimit up-to-date. Note that the GC has multiple threads running this enumeration concurrently + // with no synchronization. If you need to change this code think carefully about how that concurrency + // may affect the results. + if (ac->alloc_limit == 0 && palloc_context->m_CombinedLimit != 0) + { + palloc_context->m_CombinedLimit = 0; + } } } } else { - fn(&g_global_alloc_context, param); + fn(&g_global_alloc_context.m_GCAllocContext, param); + if (g_global_alloc_context.m_GCAllocContext.alloc_limit == 0 && g_global_alloc_context.m_CombinedLimit != 0) + { + g_global_alloc_context.m_CombinedLimit = 0; + } } } diff --git a/src/coreclr/vm/gcheaputilities.cpp b/src/coreclr/vm/gcheaputilities.cpp index cd0259eef45d83..32ea33a91cc3ce 100644 --- a/src/coreclr/vm/gcheaputilities.cpp +++ b/src/coreclr/vm/gcheaputilities.cpp @@ -41,7 +41,7 @@ bool g_sw_ww_enabled_for_gc_heap = false; #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP -GVAL_IMPL_INIT(gc_alloc_context, g_global_alloc_context, {}); +GVAL_IMPL_INIT(ee_alloc_context, g_global_alloc_context, {}); enum GC_LOAD_STATUS { GC_LOAD_STATUS_BEFORE_START, diff --git a/src/coreclr/vm/gcheaputilities.h b/src/coreclr/vm/gcheaputilities.h index c652cc52bf417c..f7ea23cbdcd53a 100644 --- a/src/coreclr/vm/gcheaputilities.h +++ b/src/coreclr/vm/gcheaputilities.h @@ -12,6 +12,69 @@ GPTR_DECL(IGCHeap, g_pGCHeap); #ifndef DACCESS_COMPILE extern "C" { #endif // !DACCESS_COMPILE + +// This struct allows adding some state that is only visible to the EE onto the standard gc_alloc_context +struct ee_alloc_context +{ + // Any allocation that would overlap m_CombinedLimit needs to be handled by the allocation slow path. + // m_CombinedLimit is the minimum of: + // - gc_alloc_context.alloc_limit (the end of the current AC) + // - the sampling_limit + // + // In the simple case that randomized sampling is disabled, m_CombinedLimit is always equal to alloc_limit. + // + // There are two different useful interpretations for the sampling_limit. One is to treat the sampling_limit + // as an address and when we allocate an object that overlaps that address we should emit a sampling event. + // The other is that we can treat (sampling_limit - alloc_ptr) as a budget of how many bytes we can allocate + // before emitting a sampling event. If we always allocated objects contiguously in the AC and incremented + // alloc_ptr by the size of the object, these two interpretations would be equivalent. However, when objects + // don't fit in the AC we allocate them in some other address range. The budget interpretation is more + // flexible to handle those cases. + // + // The sampling limit isn't stored in any separate field explicitly, instead it is implied: + // - if m_CombinedLimit == alloc_limit there is no sampled byte in the AC. In the budget interpretation + // we can allocate (alloc_limit - alloc_ptr) unsampled bytes. We'll need a new random number after + // that to determine whether future allocated bytes should be sampled. + // This occurs either because the sampling feature is disabled, or because the randomized selection + // of sampled bytes didn't select a byte in this AC. + // - if m_CombinedLimit < alloc_limit there is a sample limit in the AC. sample_limit = m_CombinedLimit. + uint8_t* m_CombinedLimit; + gc_alloc_context m_GCAllocContext; + + void init() + { + LIMITED_METHOD_CONTRACT; + m_CombinedLimit = 0; + m_GCAllocContext.init(); + } + + uint8_t* getCombinedLimit() + { + LIMITED_METHOD_CONTRACT; + return m_CombinedLimit; + } + + static size_t getAllocPtrFieldOffset() + { + LIMITED_METHOD_CONTRACT; + return offsetof(ee_alloc_context, m_GCAllocContext) + offsetof(gc_alloc_context, alloc_ptr); + } + + static size_t getCombinedLimitFieldOffset() + { + LIMITED_METHOD_CONTRACT; + return offsetof(ee_alloc_context, m_CombinedLimit); + } + + // Regenerate the randomized sampling limit and update the m_CombinedLimit field. + inline void UpdateCombinedLimit() + { + // The randomized sampling feature is being submitted in stages. At this point the sampling is never + // activated so m_CombinedLimit is always equal to alloc_limit. + m_CombinedLimit = m_GCAllocContext.alloc_limit; + } +}; + GPTR_DECL(uint8_t,g_lowest_address); GPTR_DECL(uint8_t,g_highest_address); GPTR_DECL(uint32_t,g_card_table); @@ -21,7 +84,8 @@ GVAL_DECL(GCHeapType, g_heap_type); // 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. -GVAL_DECL(gc_alloc_context, g_global_alloc_context); +GVAL_DECL(ee_alloc_context, g_global_alloc_context); + #ifndef DACCESS_COMPILE } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/gchelpers.cpp b/src/coreclr/vm/gchelpers.cpp index d69796f0104100..80a005319d3bff 100644 --- a/src/coreclr/vm/gchelpers.cpp +++ b/src/coreclr/vm/gchelpers.cpp @@ -40,7 +40,7 @@ // //======================================================================== -inline gc_alloc_context* GetThreadAllocContext() +inline ee_alloc_context* GetThreadEEAllocContext() { WRAPPER_NO_CONTRACT; @@ -222,16 +222,19 @@ inline Object* Alloc(size_t size, GC_ALLOC_FLAGS flags) if (GCHeapUtilities::UseThreadAllocationContexts()) { - gc_alloc_context *threadContext = GetThreadAllocContext(); - GCStress::MaybeTrigger(threadContext); - retVal = GCHeapUtilities::GetGCHeap()->Alloc(threadContext, size, flags); + ee_alloc_context *threadContext = GetThreadEEAllocContext(); + GCStress::MaybeTrigger(&threadContext->m_GCAllocContext); + retVal = GCHeapUtilities::GetGCHeap()->Alloc(&threadContext->m_GCAllocContext, size, flags); + threadContext->UpdateCombinedLimit(); + } else { GlobalAllocLockHolder holder(&g_global_alloc_lock); - gc_alloc_context *globalContext = &g_global_alloc_context; - GCStress::MaybeTrigger(globalContext); - retVal = GCHeapUtilities::GetGCHeap()->Alloc(globalContext, size, flags); + ee_alloc_context *globalContext = &g_global_alloc_context; + GCStress::MaybeTrigger(&globalContext->m_GCAllocContext); + retVal = GCHeapUtilities::GetGCHeap()->Alloc(&globalContext->m_GCAllocContext, size, flags); + globalContext->UpdateCombinedLimit(); } diff --git a/src/coreclr/vm/gcstress.h b/src/coreclr/vm/gcstress.h index 23b11d9989fcf6..09eca2f81045f0 100644 --- a/src/coreclr/vm/gcstress.h +++ b/src/coreclr/vm/gcstress.h @@ -298,7 +298,7 @@ namespace _GCStress // BUG(github #10318) - when not using allocation contexts, the alloc lock // must be acquired here. Until fixed, this assert prevents random heap corruption. _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts()); - GCHeapUtilities::GetGCHeap()->StressHeap(&t_runtime_thread_locals.alloc_context); + GCHeapUtilities::GetGCHeap()->StressHeap(&t_runtime_thread_locals.alloc_context.m_GCAllocContext); } FORCEINLINE diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index f643365aae56ff..67b612386b1bd3 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -218,8 +218,8 @@ void JIT_TrialAlloc::EmitCore(CPUSTUBLINKER *psl, CodeLabel *noLock, CodeLabel * if (flags & (ALIGN8 | SIZE_IN_EAX | ALIGN8OBJ)) { - // MOV EBX, [edx]gc_alloc_context.alloc_ptr - psl->X86EmitOffsetModRM(0x8B, kEBX, kEDX, offsetof(gc_alloc_context, alloc_ptr)); + // MOV EBX, [edx]alloc_context.m_GCAllocContext.alloc_ptr + psl->X86EmitOffsetModRM(0x8B, kEBX, kEDX, ee_alloc_context::getAllocPtrFieldOffset()); // add EAX, EBX psl->Emit16(0xC303); if (flags & ALIGN8) @@ -227,20 +227,20 @@ void JIT_TrialAlloc::EmitCore(CPUSTUBLINKER *psl, CodeLabel *noLock, CodeLabel * } else { - // add eax, [edx]gc_alloc_context.alloc_ptr - psl->X86EmitOffsetModRM(0x03, kEAX, kEDX, offsetof(gc_alloc_context, alloc_ptr)); + // add eax, [edx]alloc_context.m_GCAllocContext.alloc_ptr + psl->X86EmitOffsetModRM(0x03, kEAX, kEDX, ee_alloc_context::getAllocPtrFieldOffset()); } - // cmp eax, [edx]gc_alloc_context.alloc_limit - psl->X86EmitOffsetModRM(0x3b, kEAX, kEDX, offsetof(gc_alloc_context, alloc_limit)); + // cmp eax, [edx]alloc_context.m_CombinedLimit + psl->X86EmitOffsetModRM(0x3b, kEAX, kEDX, ee_alloc_context::getCombinedLimitFieldOffset()); // ja noAlloc psl->X86EmitCondJump(noAlloc, X86CondCode::kJA); // Fill in the allocation and get out. - // mov [edx]gc_alloc_context.alloc_ptr, eax - psl->X86EmitIndexRegStore(kEDX, offsetof(gc_alloc_context, alloc_ptr), kEAX); + // mov [edx]alloc_context.m_GCAllocContext.alloc_ptr, eax + psl->X86EmitIndexRegStore(kEDX, ee_alloc_context::getAllocPtrFieldOffset(), kEAX); if (flags & (ALIGN8 | SIZE_IN_EAX | ALIGN8OBJ)) { @@ -282,9 +282,9 @@ void JIT_TrialAlloc::EmitCore(CPUSTUBLINKER *psl, CodeLabel *noLock, CodeLabel * psl->X86EmitIndexRegLoad(kEDX, kECX, offsetof(MethodTable, m_BaseSize)); } - // mov eax, dword ptr [g_global_alloc_context] + // mov eax, dword ptr [g_global_alloc_context.m_GCAllocContext.alloc_ptr] psl->Emit8(0xA1); - psl->Emit32((int)(size_t)&g_global_alloc_context); + psl->Emit32((int)(size_t)&g_global_alloc_context + ee_alloc_context::getAllocPtrFieldOffset()); // Try the allocation. // add edx, eax @@ -293,17 +293,17 @@ void JIT_TrialAlloc::EmitCore(CPUSTUBLINKER *psl, CodeLabel *noLock, CodeLabel * if (flags & (ALIGN8 | ALIGN8OBJ)) EmitAlignmentRoundup(psl, kEAX, kEDX, flags); // bump up EDX size by 12 if EAX unaligned (so that we are aligned) - // cmp edx, dword ptr [g_global_alloc_context+4] + // cmp edx, dword ptr [g_global_alloc_context.m_CombinedLimit] psl->Emit16(0x153b); - psl->Emit32((int)(size_t)&g_global_alloc_context + 4); + psl->Emit32((int)(size_t)&g_global_alloc_context + ee_alloc_context::getCombinedLimitFieldOffset()); // ja noAlloc psl->X86EmitCondJump(noAlloc, X86CondCode::kJA); // Fill in the allocation and get out. - // mov dword ptr [g_global_alloc_context], edx + // mov dword ptr [g_global_alloc_context.m_GCAllocContext.alloc_ptr], edx psl->Emit16(0x1589); - psl->Emit32((int)(size_t)&g_global_alloc_context); + psl->Emit32((int)(size_t)&g_global_alloc_context + ee_alloc_context::getAllocPtrFieldOffset()); if (flags & (ALIGN8 | ALIGN8OBJ)) EmitDummyObject(psl, kEAX, flags); diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index 2c35adfb65bc6e..846f4393303de9 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -2197,7 +2197,7 @@ namespace { gc_alloc_context* STDCALL GetAllocContextHelper() { - return &t_runtime_thread_locals.alloc_context; + return &t_runtime_thread_locals.alloc_context.m_GCAllocContext; } } #endif diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index e5586d74db0e39..26d30c23c4cf61 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1182,7 +1182,8 @@ HCIMPL1_RAW(Object*, JIT_NewS_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_) } CONTRACTL_END; _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts()); - gc_alloc_context *allocContext = &t_runtime_thread_locals.alloc_context; + ee_alloc_context *eeAllocContext = &t_runtime_thread_locals.alloc_context; + gc_alloc_context *allocContext = &eeAllocContext->m_GCAllocContext; TypeHandle typeHandle(typeHnd_); _ASSERTE(!typeHandle.IsTypeDesc()); // heap objects must have method tables @@ -1192,8 +1193,8 @@ HCIMPL1_RAW(Object*, JIT_NewS_MP_FastPortable, CORINFO_CLASS_HANDLE typeHnd_) _ASSERTE(size % DATA_ALIGNMENT == 0); BYTE *allocPtr = allocContext->alloc_ptr; - _ASSERTE(allocPtr <= allocContext->alloc_limit); - if (size > static_cast(allocContext->alloc_limit - allocPtr)) + _ASSERTE(allocPtr <= eeAllocContext->getCombinedLimit()); + if (size > static_cast(eeAllocContext->getCombinedLimit() - allocPtr)) { // Tail call to the slow helper return HCCALL1(JIT_New, typeHnd_); @@ -1299,7 +1300,8 @@ HCIMPL1_RAW(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength) return HCCALL1(FramedAllocateString, stringLength); } - gc_alloc_context *allocContext = &t_runtime_thread_locals.alloc_context; + ee_alloc_context *eeAllocContext = &t_runtime_thread_locals.alloc_context; + gc_alloc_context *allocContext = &eeAllocContext->m_GCAllocContext; SIZE_T totalSize = StringObject::GetSize(stringLength); @@ -1312,8 +1314,8 @@ HCIMPL1_RAW(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength) totalSize = alignedTotalSize; BYTE *allocPtr = allocContext->alloc_ptr; - _ASSERTE(allocPtr <= allocContext->alloc_limit); - if (totalSize > static_cast(allocContext->alloc_limit - allocPtr)) + _ASSERTE(allocPtr <= eeAllocContext->getCombinedLimit()); + if (totalSize > static_cast(eeAllocContext->getCombinedLimit() - allocPtr)) { // Tail call to the slow helper return HCCALL1(FramedAllocateString, stringLength); @@ -1415,7 +1417,8 @@ HCIMPL2_RAW(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT return HCCALL2(JIT_NewArr1, arrayMT, size); } - gc_alloc_context *allocContext = &t_runtime_thread_locals.alloc_context; + ee_alloc_context* eeAllocContext = &t_runtime_thread_locals.alloc_context; + gc_alloc_context* allocContext = &eeAllocContext->m_GCAllocContext; MethodTable *pArrayMT = (MethodTable *)arrayMT; @@ -1433,8 +1436,8 @@ HCIMPL2_RAW(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT totalSize = alignedTotalSize; BYTE *allocPtr = allocContext->alloc_ptr; - _ASSERTE(allocPtr <= allocContext->alloc_limit); - if (totalSize > static_cast(allocContext->alloc_limit - allocPtr)) + _ASSERTE(allocPtr <= eeAllocContext->getCombinedLimit()); + if (totalSize > static_cast(eeAllocContext->getCombinedLimit() - allocPtr)) { // Tail call to the slow helper return HCCALL2(JIT_NewArr1, arrayMT, size); @@ -1484,10 +1487,11 @@ HCIMPL2_RAW(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayM _ASSERTE(ALIGN_UP(totalSize, DATA_ALIGNMENT) == totalSize); - gc_alloc_context *allocContext = &t_runtime_thread_locals.alloc_context; + ee_alloc_context* eeAllocContext = &t_runtime_thread_locals.alloc_context; + gc_alloc_context* allocContext = &eeAllocContext->m_GCAllocContext; BYTE *allocPtr = allocContext->alloc_ptr; - _ASSERTE(allocPtr <= allocContext->alloc_limit); - if (totalSize > static_cast(allocContext->alloc_limit - allocPtr)) + _ASSERTE(allocPtr <= eeAllocContext->getCombinedLimit()); + if (totalSize > static_cast(eeAllocContext->getCombinedLimit() - allocPtr)) { // Tail call to the slow helper return HCCALL2(JIT_NewArr1, arrayMT, size); @@ -1634,7 +1638,8 @@ HCIMPL2_RAW(Object*, JIT_Box_MP_FastPortable, CORINFO_CLASS_HANDLE type, void* u } _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts()); - gc_alloc_context *allocContext = &t_runtime_thread_locals.alloc_context; + ee_alloc_context* eeAllocContext = &t_runtime_thread_locals.alloc_context; + gc_alloc_context* allocContext = &eeAllocContext->m_GCAllocContext; TypeHandle typeHandle(type); _ASSERTE(!typeHandle.IsTypeDesc()); // heap objects must have method tables @@ -1653,8 +1658,8 @@ HCIMPL2_RAW(Object*, JIT_Box_MP_FastPortable, CORINFO_CLASS_HANDLE type, void* u _ASSERTE(size % DATA_ALIGNMENT == 0); BYTE *allocPtr = allocContext->alloc_ptr; - _ASSERTE(allocPtr <= allocContext->alloc_limit); - if (size > static_cast(allocContext->alloc_limit - allocPtr)) + _ASSERTE(allocPtr <= eeAllocContext->getCombinedLimit()); + if (size > static_cast(eeAllocContext->getCombinedLimit() - allocPtr)) { // Tail call to the slow helper return HCCALL2(JIT_Box, type, unboxedData); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 60de80e15660cf..38643fab0386b8 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -2721,11 +2721,12 @@ void Thread::CooperativeCleanup() if (GCHeapUtilities::IsGCHeapInitialized()) { + gc_alloc_context* gc_alloc_context = &t_runtime_thread_locals.alloc_context.m_GCAllocContext; // If the GC heap is initialized, we need to fix the alloc context for this detaching thread. // GetTotalAllocatedBytes reads dead_threads_non_alloc_bytes, but will suspend EE, being in COOP mode we cannot race with that // however, there could be other threads terminating and doing the same Add. - InterlockedExchangeAdd64((LONG64*)&dead_threads_non_alloc_bytes, t_runtime_thread_locals.alloc_context.alloc_limit - t_runtime_thread_locals.alloc_context.alloc_ptr); - GCHeapUtilities::GetGCHeap()->FixAllocContext(&t_runtime_thread_locals.alloc_context, NULL, NULL); + InterlockedExchangeAdd64((LONG64*)&dead_threads_non_alloc_bytes, gc_alloc_context->alloc_limit - gc_alloc_context->alloc_ptr); + GCHeapUtilities::GetGCHeap()->FixAllocContext(gc_alloc_context, NULL, NULL); t_runtime_thread_locals.alloc_context.init(); // re-initialize the context. // Clear out the alloc context pointer for this thread. When TLS is gone, this pointer will point into freed memory. diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 8fe3db20ab2d4a..762cba584a423c 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -441,7 +441,7 @@ struct RuntimeThreadLocals { // on MP systems, each thread has its own allocation chunk so we can avoid // lock prefixes and expensive MP cache snooping stuff - gc_alloc_context alloc_context; + ee_alloc_context alloc_context; }; #ifdef _MSC_VER @@ -953,7 +953,25 @@ class Thread public: inline void InitRuntimeThreadLocals() { LIMITED_METHOD_CONTRACT; m_pRuntimeThreadLocals = PTR_RuntimeThreadLocals(&t_runtime_thread_locals); } - inline PTR_gc_alloc_context GetAllocContext() { LIMITED_METHOD_CONTRACT; return PTR_gc_alloc_context(&m_pRuntimeThreadLocals->alloc_context); } + inline ee_alloc_context* GetEEAllocContext() + { + LIMITED_METHOD_CONTRACT; + if (m_pRuntimeThreadLocals == nullptr) + { + return nullptr; + } + return &m_pRuntimeThreadLocals->alloc_context; + } + + inline gc_alloc_context* GetAllocContext() + { + LIMITED_METHOD_CONTRACT; + if (m_pRuntimeThreadLocals == nullptr) + { + return nullptr; + } + return &m_pRuntimeThreadLocals->alloc_context.m_GCAllocContext; + } // This is the type handle of the first object in the alloc context at the time // we fire the AllocationTick event. It's only for tooling purpose. diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 1506516f12447e..e487ea028e4508 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -2378,7 +2378,7 @@ void Thread::PerformPreemptiveGC() // BUG(github #10318) - when not using allocation contexts, the alloc lock // must be acquired here. Until fixed, this assert prevents random heap corruption. _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts()); - GCHeapUtilities::GetGCHeap()->StressHeap(&t_runtime_thread_locals.alloc_context); + GCHeapUtilities::GetGCHeap()->StressHeap(&t_runtime_thread_locals.alloc_context.m_GCAllocContext); m_bGCStressing = FALSE; } m_GCOnTransitionsOK = TRUE; diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index 36f1c5312bf50a..5bc2173d11b63d 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -24,6 +24,7 @@ public enum DataType Thread, ThreadStore, GCAllocContext, + EEAllocContext, Exception, ExceptionInfo, RuntimeThreadLocals, diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread.cs index 312c300e5b74fa..f09a2997a1c1a5 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Thread.cs @@ -76,8 +76,8 @@ ThreadData IThread.GetThreadData(TargetPointer threadPointer) thread.OSId, (ThreadState)thread.State, (thread.PreemptiveGCDisabled & 0x1) != 0, - thread.RuntimeThreadLocals?.AllocContext.Pointer ?? TargetPointer.Null, - thread.RuntimeThreadLocals?.AllocContext.Limit ?? TargetPointer.Null, + thread.RuntimeThreadLocals?.AllocContext.GCAllocationContext.Pointer ?? TargetPointer.Null, + thread.RuntimeThreadLocals?.AllocContext.GCAllocationContext.Limit ?? TargetPointer.Null, thread.Frame, firstNestedException, thread.TEB, diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEAllocContext.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEAllocContext.cs new file mode 100644 index 00000000000000..0b7f26d0307b8c --- /dev/null +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEAllocContext.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class EEAllocContext : IData +{ + static EEAllocContext IData.Create(Target target, TargetPointer address) + => new EEAllocContext(target, address); + + public EEAllocContext(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EEAllocContext); + GCAllocationContext = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(GCAllocationContext)].Offset); + } + + public GCAllocContext GCAllocationContext { get; init; } +} diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeThreadLocals.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeThreadLocals.cs index 2d7f92cb4cb247..2841d70417cdb6 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeThreadLocals.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader.Contracts/Data/RuntimeThreadLocals.cs @@ -11,8 +11,8 @@ static RuntimeThreadLocals IData.Create(Target target, Targ public RuntimeThreadLocals(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.RuntimeThreadLocals); - AllocContext = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(AllocContext)].Offset); + AllocContext = target.ProcessedData.GetOrAdd(address + (ulong)type.Fields[nameof(AllocContext)].Offset); } - public GCAllocContext AllocContext { get; init; } + public EEAllocContext AllocContext { get; init; } }