2929
3030#include " gcdesc.h"
3131
32+ #ifdef FEATURE_EVENT_TRACE
33+ #include " clretwallmain.h"
34+ #else // FEATURE_EVENT_TRACE
35+ #include " etmdummy.h"
36+ #endif // FEATURE_EVENT_TRACE
37+
3238#define RH_LARGE_OBJECT_SIZE 85000
3339
3440MethodTable g_FreeObjectEEType;
@@ -471,6 +477,24 @@ EXTERN_C int64_t QCALLTYPE RhGetTotalAllocatedBytesPrecise()
471477 return allocated;
472478}
473479
480+ void FireAllocationSampled (GC_ALLOC_FLAGS flags, size_t size, size_t samplingBudgetOffset, Object* orObject)
481+ {
482+ #ifdef FEATURE_EVENT_TRACE
483+ void * typeId = GetLastAllocEEType ();
484+ // Note: Just as for AllocationTick, the type name cannot be retrieved
485+ WCHAR* name = nullptr ;
486+
487+ if (typeId != nullptr )
488+ {
489+ unsigned int allocKind =
490+ (flags & GC_ALLOC_PINNED_OBJECT_HEAP) ? 2 :
491+ (flags & GC_ALLOC_LARGE_OBJECT_HEAP) ? 1 :
492+ 0 ; // SOH
493+ FireEtwAllocationSampled (allocKind, GetClrInstanceId (), typeId, name, (BYTE*)orObject, size, samplingBudgetOffset);
494+ }
495+ #endif
496+ }
497+
474498static Object* GcAllocInternal (MethodTable* pEEType, uint32_t uFlags, uintptr_t numElements, Thread* pThread)
475499{
476500 ASSERT (!pThread->IsDoNotTriggerGcSet ());
@@ -539,8 +563,47 @@ static Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t
539563 // Save the MethodTable for instrumentation purposes.
540564 tls_pLastAllocationEEType = pEEType;
541565
542- Object* pObject = GCHeapUtilities::GetGCHeap ()->Alloc (pThread->GetAllocContext (), cbSize, uFlags);
543- pThread->GetEEAllocContext ()->UpdateCombinedLimit ();
566+ // check for dynamic allocation sampling
567+ ee_alloc_context* pEEAllocContext = pThread->GetEEAllocContext ();
568+ gc_alloc_context* pAllocContext = pEEAllocContext->GetGCAllocContext ();
569+ bool isSampled = false ;
570+ size_t availableSpace = 0 ;
571+ size_t samplingBudget = 0 ;
572+
573+ bool isRandomizedSamplingEnabled = ee_alloc_context::IsRandomizedSamplingEnabled ();
574+ if (isRandomizedSamplingEnabled)
575+ {
576+ // The number bytes we can allocate before we need to emit a sampling event.
577+ // This calculation is only valid if combined_limit < alloc_limit.
578+ samplingBudget = (size_t )(pEEAllocContext->combined_limit - pAllocContext->alloc_ptr );
579+
580+ // The number of bytes available in the current allocation context
581+ availableSpace = (size_t )(pAllocContext->alloc_limit - pAllocContext->alloc_ptr );
582+
583+ // Check to see if the allocated object overlaps a sampled byte
584+ // in this AC. This happens when both:
585+ // 1) The AC contains a sampled byte (combined_limit < alloc_limit)
586+ // 2) The object is large enough to overlap it (samplingBudget < aligned_size)
587+ //
588+ // Note that the AC could have no remaining space for allocations (alloc_ptr =
589+ // alloc_limit = combined_limit). When a thread hasn't done any SOH allocations
590+ // yet it also starts in an empty state where alloc_ptr = alloc_limit =
591+ // combined_limit = nullptr. The (1) check handles both of these situations
592+ // properly as an empty AC can not have a sampled byte inside of it.
593+ isSampled =
594+ (pEEAllocContext->combined_limit < pAllocContext->alloc_limit ) &&
595+ (samplingBudget < cbSize);
596+
597+ // if the object overflows the AC, we need to sample the remaining bytes
598+ // the sampling budget only included at most the bytes inside the AC
599+ if (cbSize > availableSpace && !isSampled)
600+ {
601+ samplingBudget = ee_alloc_context::ComputeGeometricRandom () + availableSpace;
602+ isSampled = (samplingBudget < cbSize);
603+ }
604+ }
605+
606+ Object* pObject = GCHeapUtilities::GetGCHeap ()->Alloc (pAllocContext, cbSize, uFlags);
544607 if (pObject == NULL )
545608 return NULL ;
546609
@@ -551,6 +614,19 @@ static Object* GcAllocInternal(MethodTable* pEEType, uint32_t uFlags, uintptr_t
551614 ((Array*)pObject)->InitArrayLength ((uint32_t )numElements);
552615 }
553616
617+ if (isSampled)
618+ {
619+ FireAllocationSampled ((GC_ALLOC_FLAGS)uFlags, cbSize, samplingBudget, pObject);
620+ }
621+
622+ // There are a variety of conditions that may have invalidated the previous combined_limit value
623+ // such as not allocating the object in the AC memory region (UOH allocations), moving the AC, adding
624+ // extra alignment padding, allocating a new AC, or allocating an object that consumed the sampling budget.
625+ // Rather than test for all the different invalidation conditions individually we conservatively always
626+ // recompute it. If sampling isn't enabled this inlined function is just trivially setting
627+ // combined_limit=alloc_limit.
628+ pEEAllocContext->UpdateCombinedLimit (isRandomizedSamplingEnabled);
629+
554630 if (uFlags & GC_ALLOC_USER_OLD_HEAP)
555631 GCHeapUtilities::GetGCHeap ()->PublishObject ((uint8_t *)pObject);
556632
0 commit comments