Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ee_alloc_context #104849

Merged
merged 7 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 5 additions & 4 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down
7 changes: 6 additions & 1 deletion src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/gc/gcinterface.ee.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/inc/dacvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
30 changes: 15 additions & 15 deletions src/coreclr/vm/amd64/JitHelpers_Slow.asm
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -341,16 +341,16 @@ 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

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

Expand Down Expand Up @@ -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

Expand Down
9 changes: 5 additions & 4 deletions src/coreclr/vm/amd64/asmconstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/gccover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 17 additions & 4 deletions src/coreclr/vm/gcenv.ee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/gcheaputilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
66 changes: 65 additions & 1 deletion src/coreclr/vm/gcheaputilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
17 changes: 10 additions & 7 deletions src/coreclr/vm/gchelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
//
//========================================================================

inline gc_alloc_context* GetThreadAllocContext()
inline ee_alloc_context* GetThreadEEAllocContext()
{
WRAPPER_NO_CONTRACT;

Expand Down Expand Up @@ -222,16 +222,19 @@ inline Object* Alloc(size_t size, GC_ALLOC_FLAGS flags)

if (GCHeapUtilities::UseThreadAllocationContexts())
{
gc_alloc_context *threadContext = GetThreadAllocContext();
GCStress<gc_on_alloc>::MaybeTrigger(threadContext);
retVal = GCHeapUtilities::GetGCHeap()->Alloc(threadContext, size, flags);
ee_alloc_context *threadContext = GetThreadEEAllocContext();
GCStress<gc_on_alloc>::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<gc_on_alloc>::MaybeTrigger(globalContext);
retVal = GCHeapUtilities::GetGCHeap()->Alloc(globalContext, size, flags);
ee_alloc_context *globalContext = &g_global_alloc_context;
GCStress<gc_on_alloc>::MaybeTrigger(&globalContext->m_GCAllocContext);
retVal = GCHeapUtilities::GetGCHeap()->Alloc(&globalContext->m_GCAllocContext, size, flags);
globalContext->UpdateCombinedLimit();
}


Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/gcstress.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading