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

Randomized allocation sampling #104955

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
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
317 changes: 317 additions & 0 deletions docs/design/features/RandomizedAllocationSampling.md

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6551,10 +6551,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).gc_allocation_context;
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 @@ -715,7 +715,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 @@ -822,7 +822,7 @@ HRESULT ClrDataAccess::GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct Dacp
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 @@ -5493,8 +5493,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).gc_allocation_context;
*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 @@ -132,9 +132,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, gc_allocation_context))
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
12 changes: 7 additions & 5 deletions src/coreclr/inc/eventtracebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -1333,17 +1333,19 @@ namespace ETW
#define ETWLoaderStaticLoad 0 // Static reference load
#define ETWLoaderDynamicLoad 1 // Dynamic assembly load

#if defined (FEATURE_EVENT_TRACE)
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context;
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context;
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context;
#endif // FEATURE_EVENT_TRACE

#if defined(FEATURE_EVENT_TRACE) && !defined(HOST_UNIX)
//
// The ONE and only ONE global instantiation of this class
//
extern ETW::CEtwTracer * g_pEtwTracer;

EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context;
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context;
EXTERN_C DOTNET_TRACE_CONTEXT MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context;

//
// Special Handling of Startup events
//
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/minipal/Unix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(SOURCES
doublemapping.cpp
dn-u16.cpp
${CLR_SRC_NATIVE_DIR}/minipal/time.c
${CLR_SRC_NATIVE_DIR}/minipal/xoshiro128pp.c
)

if(NOT CLR_CROSS_COMPONENTS_BUILD)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/minipal/Windows/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(SOURCES
dn-u16.cpp
${CLR_SRC_NATIVE_DIR}/minipal/utf8.c
${CLR_SRC_NATIVE_DIR}/minipal/time.c
${CLR_SRC_NATIVE_DIR}/minipal/xoshiro128pp.c
)

if(NOT CLR_CROSS_COMPONENTS_BUILD)
Expand Down
21 changes: 12 additions & 9 deletions src/coreclr/nativeaot/Runtime/AsmOffsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,24 @@ ASM_OFFSET( 0, 0, MethodTable, m_uFlags)
ASM_OFFSET( 4, 4, MethodTable, m_uBaseSize)
ASM_OFFSET( 14, 18, MethodTable, m_VTable)

ASM_OFFSET( 0, 0, Thread, m_rgbAllocContextBuffer)
ASM_OFFSET( 28, 38, Thread, m_ThreadStateFlags)
ASM_OFFSET( 2c, 40, Thread, m_pTransitionFrame)
ASM_OFFSET( 30, 48, Thread, m_pDeferredTransitionFrame)
ASM_OFFSET( 40, 68, Thread, m_ppvHijackedReturnAddressLocation)
ASM_OFFSET( 44, 70, Thread, m_pvHijackedReturnAddress)
ASM_OFFSET( 48, 78, Thread, m_uHijackedReturnValueFlags)
ASM_OFFSET( 4c, 80, Thread, m_pExInfoStackHead)
ASM_OFFSET( 50, 88, Thread, m_threadAbortException)
ASM_OFFSET( 0, 0, Thread, m_eeAllocContext)
ASM_OFFSET( 2c, 40, Thread, m_ThreadStateFlags)
ASM_OFFSET( 30, 48, Thread, m_pTransitionFrame)
ASM_OFFSET( 34, 50, Thread, m_pDeferredTransitionFrame)
ASM_OFFSET( 44, 70, Thread, m_ppvHijackedReturnAddressLocation)
ASM_OFFSET( 48, 78, Thread, m_pvHijackedReturnAddress)
ASM_OFFSET( 4c, 80, Thread, m_uHijackedReturnValueFlags)
ASM_OFFSET( 50, 88, Thread, m_pExInfoStackHead)
ASM_OFFSET( 54, 90, Thread, m_threadAbortException)

ASM_SIZEOF( 14, 20, EHEnum)

ASM_OFFSET( 0, 0, gc_alloc_context, alloc_ptr)
ASM_OFFSET( 4, 8, gc_alloc_context, alloc_limit)

ASM_OFFSET( 0, 0, ee_alloc_context, combined_limit)
ASM_OFFSET( 4, 8, ee_alloc_context, m_rgbAllocContextBuffer)

#ifdef FEATURE_CACHED_INTERFACE_DISPATCH
ASM_OFFSET( 4, 8, InterfaceDispatchCell, m_pCache)
#ifdef INTERFACE_DISPATCH_CACHE_HAS_CELL_BACKPOINTER
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/AsmOffsetsVerify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

class AsmOffsets
{
static_assert(sizeof(Thread::m_rgbAllocContextBuffer) >= sizeof(gc_alloc_context), "Thread::m_rgbAllocContextBuffer is not big enough to hold a gc_alloc_context");
static_assert(sizeof(ee_alloc_context::m_rgbAllocContextBuffer) >= sizeof(gc_alloc_context), "ee_alloc_context::m_rgbAllocContextBuffer is not big enough to hold a gc_alloc_context");

// Some assembly helpers for arrays and strings are shared and use the fact that arrays and strings have similar layouts)
static_assert(offsetof(Array, m_Length) == offsetof(String, m_Length), "The length field of String and Array have different offsets");
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/nativeaot/Runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ set(COMMON_RUNTIME_SOURCES

${CLR_SRC_NATIVE_DIR}/minipal/cpufeatures.c
${CLR_SRC_NATIVE_DIR}/minipal/time.c
${CLR_SRC_NATIVE_DIR}/minipal/xoshiro128pp.c
)

set(SERVER_GC_SOURCES
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/nativeaot/Runtime/DebugHeader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ extern "C" void PopulateDebugHeaders()
MAKE_DEBUG_FIELD_ENTRY(dac_gc_heap, finalize_queue);
MAKE_DEBUG_FIELD_ENTRY(dac_gc_heap, generation_table);

MAKE_SIZE_ENTRY(ee_alloc_context);
MAKE_DEBUG_FIELD_ENTRY(ee_alloc_context, m_rgbAllocContextBuffer);

MAKE_SIZE_ENTRY(gc_alloc_context);
MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_ptr);
MAKE_DEBUG_FIELD_ENTRY(gc_alloc_context, alloc_limit);
Expand Down Expand Up @@ -194,7 +197,7 @@ extern "C" void PopulateDebugHeaders()

MAKE_SIZE_ENTRY(RuntimeThreadLocals);
MAKE_DEBUG_FIELD_ENTRY(RuntimeThreadLocals, m_pNext);
MAKE_DEBUG_FIELD_ENTRY(RuntimeThreadLocals, m_rgbAllocContextBuffer);
MAKE_DEBUG_FIELD_ENTRY(RuntimeThreadLocals, m_eeAllocContext);
MAKE_DEBUG_FIELD_ENTRY(RuntimeThreadLocals, m_threadId);
MAKE_DEBUG_FIELD_ENTRY(RuntimeThreadLocals, m_pThreadStressLog);
MAKE_DEBUG_FIELD_ENTRY(RuntimeThreadLocals, m_pExInfoStackHead);
Expand Down
84 changes: 83 additions & 1 deletion src/coreclr/nativeaot/Runtime/GCHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@

#include "gcdesc.h"

#ifdef FEATURE_EVENT_TRACE
#include "clretwallmain.h"
#else // FEATURE_EVENT_TRACE
#include "etmdummy.h"
#endif // FEATURE_EVENT_TRACE

#define RH_LARGE_OBJECT_SIZE 85000

MethodTable g_FreeObjectEEType;
Expand Down Expand Up @@ -471,6 +477,29 @@ EXTERN_C int64_t QCALLTYPE RhGetTotalAllocatedBytesPrecise()
return allocated;
}

void FireAllocationSampled(GC_ALLOC_FLAGS flags, size_t size, size_t samplingBudgetOffset, Object* orObject)
{
#ifdef FEATURE_EVENT_TRACE
void* typeId = GetLastAllocEEType();
// Note: Just as for AllocationTick, the type name cannot be retrieved
WCHAR* name = nullptr;

if (typeId != nullptr)
{
unsigned int allocKind =
(flags & GC_ALLOC_PINNED_OBJECT_HEAP) ? 2 :
(flags & GC_ALLOC_LARGE_OBJECT_HEAP) ? 1 :
0; // SOH
unsigned int heapIndex = 0;
#ifdef BACKGROUND_GC
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

gc_heap* hp = gc_heap::heap_of((BYTE*)orObject);
heapIndex = hp->heap_number;
#endif
FireEtwAllocationSampled(allocKind, GetClrInstanceId(), typeId, name, heapIndex, (BYTE*)orObject, size, samplingBudgetOffset);
}
#endif
}

static Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t numElements, Thread* pThread)
{
ASSERT(!pThread->IsDoNotTriggerGcSet());
Expand Down Expand Up @@ -539,7 +568,47 @@ static Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t
// Save the MethodTable for instrumentation purposes.
tls_pLastAllocationEEType = pEEType;

Object* pObject = GCHeapUtilities::GetGCHeap()->Alloc(pThread->GetAllocContext(), cbSize, uFlags);
// check for dynamic allocation sampling
ee_alloc_context* pEEAllocContext = pThread->GetEEAllocContext();
gc_alloc_context* pAllocContext = pEEAllocContext->GetGCAllocContext();
bool isSampled = false;
size_t availableSpace = 0;
size_t samplingBudget = 0;

bool isRandomizedSamplingEnabled = ee_alloc_context::IsRandomizedSamplingEnabled();
if (isRandomizedSamplingEnabled)
{
// The number bytes we can allocate before we need to emit a sampling event.
// This calculation is only valid if combined_limit < alloc_limit.
samplingBudget = (size_t)(pEEAllocContext->combined_limit - pAllocContext->alloc_ptr);

// The number of bytes available in the current allocation context
availableSpace = (size_t)(pAllocContext->alloc_limit - pAllocContext->alloc_ptr);

// Check to see if the allocated object overlaps a sampled byte
// in this AC. This happens when both:
// 1) The AC contains a sampled byte (combined_limit < alloc_limit)
// 2) The object is large enough to overlap it (samplingBudget < aligned_size)
//
// Note that the AC could have no remaining space for allocations (alloc_ptr =
// alloc_limit = combined_limit). When a thread hasn't done any SOH allocations
// yet it also starts in an empty state where alloc_ptr = alloc_limit =
// combined_limit = nullptr. The (1) check handles both of these situations
// properly as an empty AC can not have a sampled byte inside of it.
isSampled =
(pEEAllocContext->combined_limit < pAllocContext->alloc_limit) &&
(samplingBudget < cbSize);

// if the object overflows the AC, we need to sample the remaining bytes
// the sampling budget only included at most the bytes inside the AC
if (cbSize > availableSpace && !isSampled)
{
samplingBudget = ee_alloc_context::ComputeGeometricRandom() + availableSpace;
isSampled = (samplingBudget < cbSize);
}
}

Object* pObject = GCHeapUtilities::GetGCHeap()->Alloc(pAllocContext, cbSize, uFlags);
if (pObject == NULL)
return NULL;

Expand All @@ -550,6 +619,19 @@ static Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t
((Array*)pObject)->InitArrayLength((uint32_t)numElements);
}

if (isSampled)
{
FireAllocationSampled((GC_ALLOC_FLAGS)uFlags, cbSize, samplingBudget, pObject);
}

// There are a variety of conditions that may have invalidated the previous combined_limit value
noahfalk marked this conversation as resolved.
Show resolved Hide resolved
// such as not allocating the object in the AC memory region (UOH allocations), moving the AC, adding
// extra alignment padding, allocating a new AC, or allocating an object that consumed the sampling budget.
// Rather than test for all the different invalidation conditions individually we conservatively always
// recompute it. If sampling isn't enabled this inlined function is just trivially setting
// combined_limit=alloc_limit.
pEEAllocContext->UpdateCombinedLimit(isRandomizedSamplingEnabled);

if (uFlags & GC_ALLOC_USER_OLD_HEAP)
GCHeapUtilities::GetGCHeap()->PublishObject((uint8_t*)pObject);

Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/nativeaot/Runtime/amd64/AllocFast.S
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ NESTED_ENTRY RhpNewFast, _TEXT, NoHandler

mov rsi, [rax + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
add rdx, rsi
cmp rdx, [rax + OFFSETOF__Thread__m_alloc_context__alloc_limit]
cmp rdx, [rax + OFFSETOF__Thread__m_eeAllocContext__combined_limit]
ja LOCAL_LABEL(RhpNewFast_RarePath)

// set the new alloc pointer
Expand Down Expand Up @@ -143,7 +143,7 @@ NESTED_ENTRY RhNewString, _TEXT, NoHandler
// rcx == Thread*
// rdx == string size
// r12 == element count
cmp rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
cmp rax, [rcx + OFFSETOF__Thread__m_eeAllocContext__combined_limit]
ja LOCAL_LABEL(RhNewString_RarePath)

mov [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax
Expand Down Expand Up @@ -226,7 +226,7 @@ NESTED_ENTRY RhpNewArray, _TEXT, NoHandler
// rcx == Thread*
// rdx == array size
// r12 == element count
cmp rax, [rcx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
cmp rax, [rcx + OFFSETOF__Thread__m_eeAllocContext__combined_limit]
ja LOCAL_LABEL(RhpNewArray_RarePath)

mov [rcx + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/nativeaot/Runtime/amd64/AllocFast.asm
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ LEAF_ENTRY RhpNewFast, _TEXT

mov rax, [rdx + OFFSETOF__Thread__m_alloc_context__alloc_ptr]
add r8, rax
cmp r8, [rdx + OFFSETOF__Thread__m_alloc_context__alloc_limit]
cmp r8, [rdx + OFFSETOF__Thread__m_eeAllocContext__combined_limit]
ja RhpNewFast_RarePath

;; set the new alloc pointer
Expand Down Expand Up @@ -118,7 +118,7 @@ LEAF_ENTRY RhNewString, _TEXT
; rdx == element count
; r8 == array size
; r10 == thread
cmp rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_limit]
cmp rax, [r10 + OFFSETOF__Thread__m_eeAllocContext__combined_limit]
ja RhpNewArrayRare

mov [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax
Expand Down Expand Up @@ -179,7 +179,7 @@ LEAF_ENTRY RhpNewArray, _TEXT
; rdx == element count
; r8 == array size
; r10 == thread
cmp rax, [r10 + OFFSETOF__Thread__m_alloc_context__alloc_limit]
cmp rax, [r10 + OFFSETOF__Thread__m_eeAllocContext__combined_limit]
ja RhpNewArrayRare

mov [r10 + OFFSETOF__Thread__m_alloc_context__alloc_ptr], rax
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ TSF_DoNotTriggerGc equ 10h
;;
;; Rename fields of nested structs
;;
OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr
OFFSETOF__Thread__m_alloc_context__alloc_limit equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit
OFFSETOF__Thread__m_alloc_context__alloc_ptr equ OFFSETOF__Thread__m_eeAllocContext + OFFSETOF__ee_alloc_context__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr
OFFSETOF__Thread__m_eeAllocContext__combined_limit equ OFFSETOF__Thread__m_eeAllocContext + OFFSETOF__ee_alloc_context__combined_limit



Expand Down
Loading
Loading