Fix GetGenerationBounds under USE_REGIONS (#57101) (#58277)
authorAndrew Au <andrewau@microsoft.com>
Fri, 27 Aug 2021 22:48:42 +0000 (15:48 -0700)
committerGitHub <noreply@github.com>
Fri, 27 Aug 2021 22:48:42 +0000 (15:48 -0700)
src/coreclr/gc/env/gcenv.ee.h
src/coreclr/gc/gc.cpp
src/coreclr/gc/gcenv.ee.standalone.inl
src/coreclr/gc/gcimpl.h
src/coreclr/gc/gcinterface.ee.h
src/coreclr/gc/gcinterface.h
src/coreclr/gc/sample/gcenv.ee.cpp
src/coreclr/vm/eeprofinterfaces.h
src/coreclr/vm/gcenv.ee.cpp
src/coreclr/vm/gcenv.ee.h
src/coreclr/vm/proftoeeinterfaceimpl.cpp

index 7ecbfdd..4726797 100644 (file)
@@ -92,6 +92,8 @@ public:
     static void UpdateGCEventStatus(int publicLevel, int publicKeywords, int privateLevel, int privateKeywords);
     static void LogStressMsg(unsigned level, unsigned facility, const StressLogMsg &msg);
     static uint32_t GetCurrentProcessCpuCount();
+
+    static void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved);
 };
 
 #endif // __GCENV_EE_H__
index 47ff77d..934cfb0 100644 (file)
@@ -5807,8 +5807,6 @@ heap_segment* gc_heap::get_segment_for_uoh (int gen_number, size_t size
                 gc_etw_segment_pinned_object_heap :
                 gc_etw_segment_large_object_heap);
 
-        GCToEEInterface::DiagUpdateGenerationBounds();
-
 #ifndef USE_REGIONS
 #ifdef MULTIPLE_HEAPS
         hp->thread_uoh_segment (gen_number, res);
@@ -5816,6 +5814,12 @@ heap_segment* gc_heap::get_segment_for_uoh (int gen_number, size_t size
         thread_uoh_segment (gen_number, res);
 #endif //MULTIPLE_HEAPS
 #endif //!USE_REGIONS
+        GCToEEInterface::DiagAddNewRegion(
+                            gen_number,
+                            heap_segment_mem (res),
+                            heap_segment_allocated (res),
+                            heap_segment_reserved (res)
+                        );
     }
 
     return res;
@@ -15904,11 +15908,13 @@ BOOL gc_heap::soh_try_fit (int gen_number,
                 fix_youngest_allocation_area();
 
                 heap_segment* next_seg = heap_segment_next (ephemeral_heap_segment);
+                bool new_seg = false;
 
                 if (!next_seg)
                 {
                     assert (ephemeral_heap_segment == generation_tail_region (generation_of (gen_number)));
                     next_seg = get_new_region (gen_number);
+                    new_seg = true;
                 }
 
                 if (next_seg)
@@ -15916,6 +15922,15 @@ BOOL gc_heap::soh_try_fit (int gen_number,
                     dprintf (REGIONS_LOG, ("eph seg %Ix -> next %Ix", 
                         heap_segment_mem (ephemeral_heap_segment), heap_segment_mem (next_seg)));
                     ephemeral_heap_segment = next_seg;
+                    if (new_seg)
+                    {
+                        GCToEEInterface::DiagAddNewRegion(
+                            heap_segment_gen_num (next_seg),
+                            heap_segment_mem (next_seg),
+                            heap_segment_allocated (next_seg),
+                            heap_segment_reserved (next_seg)
+                        );
+                    }
                 }
                 else
                 {
@@ -20996,9 +21011,22 @@ bool gc_heap::extend_soh_for_no_gc()
             }
 
             region = heap_segment_next (region);
-            if ((region == nullptr) && !(region = get_new_region (0)))
+            if (region == nullptr)
             {
-                break;
+                region = get_new_region (0);
+                if (region == nullptr)
+                {
+                    break;
+                }
+                else
+                {
+                    GCToEEInterface::DiagAddNewRegion(
+                            0,
+                            heap_segment_mem (region),
+                            heap_segment_allocated (region),
+                            heap_segment_reserved (region)
+                        );
+                }
             }
         }
         else
@@ -42941,6 +42969,75 @@ unsigned int GCHeap::WhichGeneration (Object* object)
     return g;
 }
 
+unsigned int GCHeap::GetGenerationWithRange (Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved)
+{
+    int generation = -1;
+    heap_segment * hs = gc_heap::find_segment ((uint8_t*)object, FALSE);
+#ifdef USE_REGIONS
+    generation = heap_segment_gen_num (hs);
+    if (generation == max_generation)
+    {
+        if (heap_segment_loh_p (hs))
+        {
+            generation = loh_generation;
+        }
+        else if (heap_segment_poh_p (hs))
+        {
+            generation = poh_generation;
+        }
+    }
+
+    *ppStart = heap_segment_mem (hs);
+    *ppAllocated = heap_segment_allocated (hs);
+    *ppReserved = heap_segment_reserved (hs);
+#else
+#ifdef MULTIPLE_HEAPS
+    gc_heap* hp = heap_segment_heap (hs);
+#else
+    gc_heap* hp = __this;
+#endif //MULTIPLE_HEAPS
+    if (hs == hp->ephemeral_heap_segment)
+    {
+        uint8_t* reserved = heap_segment_reserved (hs);
+        uint8_t* end = heap_segment_allocated(hs);
+        for (int gen = 0; gen < max_generation; gen++)
+        {
+            uint8_t* start = generation_allocation_start (hp->generation_of (gen));
+            if ((uint8_t*)object >= start)
+            {
+                generation = gen;
+                *ppStart = start;
+                *ppAllocated = end;
+                *ppReserved = reserved;
+                break;
+            }
+            end = reserved = start;
+        }
+        if (generation == -1)
+        {
+            *ppStart = heap_segment_mem (hs);
+            *ppAllocated = *ppReserved = generation_allocation_start (hp->generation_of (max_generation - 1));
+        }
+    }
+    else
+    {
+        generation = max_generation;
+        if (heap_segment_loh_p (hs))
+        {
+            generation = loh_generation;
+        }
+        else if (heap_segment_poh_p (hs))
+        {
+            generation = poh_generation;
+        }
+        *ppStart = heap_segment_mem (hs);
+        *ppAllocated = heap_segment_allocated (hs);
+        *ppReserved = heap_segment_reserved (hs);
+    }
+#endif //USE_REGIONS
+    return (unsigned int)generation;
+}
+
 bool GCHeap::IsEphemeral (Object* object)
 {
     uint8_t* o = (uint8_t*)object;
index c7e4dc4..c2a023f 100644 (file)
@@ -306,4 +306,9 @@ inline uint32_t GCToEEInterface::GetCurrentProcessCpuCount()
     return g_theGCToCLR->GetCurrentProcessCpuCount();
 }
 
+inline void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved)
+{
+    g_theGCToCLR->DiagAddNewRegion(generation, rangeStart, rangeEnd, rangeEndReserved);
+}
+
 #endif // __GCTOENV_EE_STANDALONE_INL__
index 2c21cea..5f63164 100644 (file)
@@ -309,6 +309,7 @@ protected:
 
     virtual void DiagGetGCSettings(EtwGCSettingsInfo* etw_settings);
 
+    virtual unsigned int GetGenerationWithRange(Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved);
 public:
     Object * NextObj (Object * object);
 
index 6525cc6..e2019c8 100644 (file)
@@ -443,6 +443,9 @@ public:
 
     virtual
     uint32_t GetCurrentProcessCpuCount() = 0;
+
+    virtual
+    void DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved) = 0;
 };
 
 #endif // _GCINTERFACE_EE_H_
index 920c321..d7c08c4 100644 (file)
@@ -6,7 +6,7 @@
 
 // The major version of the GC/EE interface. Breaking changes to this interface
 // require bumps in the major version number.
-#define GC_INTERFACE_MAJOR_VERSION 4
+#define GC_INTERFACE_MAJOR_VERSION 5
 
 // The minor version of the GC/EE interface. Non-breaking changes are required
 // to bump the minor version number. GCs and EEs with minor version number
@@ -921,6 +921,8 @@ public:
     // Enables or disables the given keyword or level on the private event provider.
     virtual void ControlPrivateEvents(GCEventKeyword keyword, GCEventLevel level) = 0;
 
+    virtual unsigned int GetGenerationWithRange(Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved) = 0;
+
     IGCHeap() {}
     virtual ~IGCHeap() {}
 };
index 060a555..84274f2 100644 (file)
@@ -354,3 +354,7 @@ uint32_t GCToEEInterface::GetCurrentProcessCpuCount()
 {
     return GCToOSInterface::GetTotalProcessorCount();
 }
+
+void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved)
+{
+}
index ec51b01..9267fc3 100644 (file)
@@ -47,6 +47,8 @@ void __stdcall GarbageCollectionStartedCallback(int generation, BOOL induced);
 void __stdcall GarbageCollectionFinishedCallback();
 
 void __stdcall UpdateGenerationBounds();
+
+void __stdcall ProfilerAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved);
 #include "eetoprofinterfaceimpl.h"
 
 
index f963146..f62a282 100644 (file)
@@ -1732,3 +1732,8 @@ uint32_t GCToEEInterface::GetCurrentProcessCpuCount()
 {
     return ::GetCurrentProcessCpuCount();
 }
+
+void GCToEEInterface::DiagAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved)
+{
+    ProfilerAddNewRegion(generation, rangeStart, rangeEnd, rangeEndReserved);
+}
\ No newline at end of file
index 6ac7924..6403f26 100644 (file)
@@ -85,6 +85,8 @@ public:
 
     void LogStressMsg(unsigned level, unsigned facility, const StressLogMsg& msg);
     uint32_t GetCurrentProcessCpuCount();
+
+    void DiagAddNewRegion(int generation, BYTE * rangeStart, BYTE * rangeEnd, BYTE * rangeEndReserved);
 };
 
 } // namespace standalone
index 40b3ce5..831f023 100644 (file)
@@ -719,20 +719,132 @@ struct GenerationDesc
     BYTE *rangeEndReserved;
 };
 
-struct GenerationTable
-{
+class GenerationTable
+{
+public:
+    GenerationTable();
+    void AddRecord(int generation, BYTE* rangeStart, BYTE* rangeEnd, BYTE* rangeEndReserved);
+    void AddRecordNoLock(int generation, BYTE* rangeStart, BYTE* rangeEnd, BYTE* rangeEndReserved);
+    void Refresh();
+    HRESULT GetGenerationBounds(ULONG cObjectRanges, ULONG* pcObjectRanges, COR_PRF_GC_GENERATION_RANGE* ranges);
+private:
+    Crst mutex;
     ULONG count;
     ULONG capacity;
     static const ULONG defaultCapacity = 5; // that's the minimum for Gen0-2 + LOH + POH
-    GenerationTable *prev;
     GenerationDesc *genDescTable;
-#ifdef  _DEBUG
-    ULONG magic;
-#define GENERATION_TABLE_MAGIC 0x34781256
-#define GENERATION_TABLE_BAD_MAGIC 0x55aa55aa
-#endif
 };
 
+GenerationTable::GenerationTable() : mutex(CrstLeafLock, CRST_UNSAFE_ANYMODE)
+{
+    count = 0;
+    capacity = GenerationTable::defaultCapacity;
+    genDescTable = new (nothrow) GenerationDesc[capacity];
+    if (genDescTable == NULL)
+    {
+        capacity = 0;
+    }
+}
+
+void GenerationTable::AddRecord(int generation, BYTE* rangeStart, BYTE* rangeEnd, BYTE* rangeEndReserved)
+{
+    CONTRACT_VOID
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY; // can be called even on GC threads
+        PRECONDITION(0 <= generation && generation <= 4);
+        PRECONDITION(CheckPointer(rangeStart));
+        PRECONDITION(CheckPointer(rangeEnd));
+        PRECONDITION(CheckPointer(rangeEndReserved));
+    } CONTRACT_END;
+
+    CrstHolder holder(&mutex);
+
+    // Because the segment/region are added to the heap before they are reported to the profiler,
+    // it is possible that the region is added to the heap, a racing GenerationTable refresh happened,
+    // that refresh would contain the new region, and then it get reported again here. 
+    // This check will make sure we never add duplicated record to the table.
+    for (ULONG i = 0; i < count; i++)
+    {
+        if (genDescTable[i].rangeStart == rangeStart)
+        {
+            _ASSERTE (genDescTable[i].generation == generation);
+            _ASSERTE (genDescTable[i].rangeEnd == rangeEnd);
+            _ASSERTE (genDescTable[i].rangeEndReserved == rangeEndReserved);
+            RETURN;
+        }
+    }
+    AddRecordNoLock(generation, rangeStart, rangeEnd, rangeEndReserved);
+    RETURN;
+}
+
+void GenerationTable::AddRecordNoLock(int generation, BYTE* rangeStart, BYTE* rangeEnd, BYTE* rangeEndReserved)
+{
+    CONTRACT_VOID
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY; // can be called even on GC threads
+        PRECONDITION(0 <= generation && generation <= 4);
+        PRECONDITION(CheckPointer(rangeStart));
+        PRECONDITION(CheckPointer(rangeEnd));
+        PRECONDITION(CheckPointer(rangeEndReserved));
+    } CONTRACT_END;
+
+    _ASSERTE (mutex.OwnedByCurrentThread());
+    if (count >= capacity)
+    {
+        ULONG newCapacity = capacity == 0 ? GenerationTable::defaultCapacity : capacity * 2;
+        GenerationDesc *newGenDescTable = new (nothrow) GenerationDesc[newCapacity];
+        if (newGenDescTable == NULL)
+        {
+            count = capacity = 0;
+            delete[] genDescTable;
+            genDescTable = nullptr;
+            RETURN;
+        }
+        memcpy(newGenDescTable, genDescTable, sizeof(genDescTable[0]) * count);
+        delete[] genDescTable;
+        genDescTable = newGenDescTable;
+        capacity = newCapacity;
+    }
+    _ASSERTE(count < capacity);
+
+    genDescTable[count].generation = generation;
+    genDescTable[count].rangeStart = rangeStart;
+    genDescTable[count].rangeEnd = rangeEnd;
+    genDescTable[count].rangeEndReserved = rangeEndReserved;
+
+    count = count + 1;
+    RETURN;
+}
+
+HRESULT GenerationTable::GetGenerationBounds(ULONG cObjectRanges, ULONG* pcObjectRanges, COR_PRF_GC_GENERATION_RANGE* ranges)
+{
+    if ((cObjectRanges > 0) && (ranges == nullptr))
+    {
+        return E_INVALIDARG;
+    }
+    CrstHolder holder(&mutex);
+    if (genDescTable == nullptr)
+    {
+        return E_FAIL;
+    }
+    ULONG copy = min(count, cObjectRanges);
+    for (ULONG i = 0; i < copy; i++)
+    {
+        ranges[i].generation          = (COR_PRF_GC_GENERATION)genDescTable[i].generation;
+        ranges[i].rangeStart          = (ObjectID)genDescTable[i].rangeStart;
+        ranges[i].rangeLength         = genDescTable[i].rangeEnd         - genDescTable[i].rangeStart;
+        ranges[i].rangeLengthReserved = genDescTable[i].rangeEndReserved - genDescTable[i].rangeStart;
+    }
+    if (pcObjectRanges != nullptr)
+    {
+        *pcObjectRanges = count;
+    }
+    return S_OK;
+}
 
 //---------------------------------------------------------------------------------------
 //
@@ -769,46 +881,24 @@ static void GenWalkFunc(void * context,
     } CONTRACT_END;
 
     GenerationTable *generationTable = (GenerationTable *)context;
+    generationTable->AddRecordNoLock(generation, rangeStart, rangeEnd, rangeEndReserved);
+    RETURN;
+}
 
-    _ASSERTE(generationTable->magic == GENERATION_TABLE_MAGIC);
-
-    ULONG count = generationTable->count;
-    if (count >= generationTable->capacity)
-    {
-        ULONG newCapacity = generationTable->capacity == 0 ? GenerationTable::defaultCapacity : generationTable->capacity * 2;
-        GenerationDesc *newGenDescTable = new (nothrow) GenerationDesc[newCapacity];
-        if (newGenDescTable == NULL)
-        {
-            // if we can't allocate a bigger table, we'll have to ignore this call
-            RETURN;
-        }
-        memcpy(newGenDescTable, generationTable->genDescTable, sizeof(generationTable->genDescTable[0]) * generationTable->count);
-        delete[] generationTable->genDescTable;
-        generationTable->genDescTable = newGenDescTable;
-        generationTable->capacity = newCapacity;
-    }
-    _ASSERTE(count < generationTable->capacity);
-
-    GenerationDesc *genDescTable = generationTable->genDescTable;
-
-    genDescTable[count].generation = generation;
-    genDescTable[count].rangeStart = rangeStart;
-    genDescTable[count].rangeEnd = rangeEnd;
-    genDescTable[count].rangeEndReserved = rangeEndReserved;
-
-    generationTable->count = count + 1;
+void GenerationTable::Refresh()
+{
+    // fill in the values by calling back into the gc, which will report
+    // the ranges by calling GenWalkFunc for each one
+    CrstHolder holder(&mutex);    
+    IGCHeap *hp = GCHeapUtilities::GetGCHeap();
+    this->count = 0;
+    hp->DiagDescrGenerations(GenWalkFunc, this);
 }
 
 // This is the table of generation bounds updated by the gc
-// and read by the profiler. So this is a single writer,
-// multiple readers scenario.
+// and read by the profiler. 
 static GenerationTable *s_currentGenerationTable;
 
-// The generation table is updated atomically by replacing the
-// pointer to it. The only tricky part is knowing when
-// the old table can be deleted.
-static Volatile<LONG> s_generationTableLock;
-
 // This is just so we can assert there's a single writer
 #ifdef  ENABLE_CONTRACTS
 static Volatile<LONG> s_generationTableWriterCount;
@@ -837,67 +927,41 @@ void __stdcall UpdateGenerationBounds()
     // Notify the profiler of start of the collection
     if (CORProfilerTrackGC() || CORProfilerTrackBasicGC())
     {
-        // generate a new generation table
-        GenerationTable *newGenerationTable = new (nothrow) GenerationTable();
-        if (newGenerationTable == NULL)
-            RETURN;
-        newGenerationTable->count = 0;
-        newGenerationTable->capacity = GenerationTable::defaultCapacity;
-        // if there is already a current table, use its capacity as a guess for the capacity
-        if (s_currentGenerationTable != NULL)
-            newGenerationTable->capacity = s_currentGenerationTable->capacity;
-        newGenerationTable->prev = NULL;
-        newGenerationTable->genDescTable = new (nothrow) GenerationDesc[newGenerationTable->capacity];
-        if (newGenerationTable->genDescTable == NULL)
-            newGenerationTable->capacity = 0;
-
-#ifdef  _DEBUG
-        newGenerationTable->magic = GENERATION_TABLE_MAGIC;
-#endif
-        // fill in the values by calling back into the gc, which will report
-        // the ranges by calling GenWalkFunc for each one
-        IGCHeap *hp = GCHeapUtilities::GetGCHeap();
-        hp->DiagDescrGenerations(GenWalkFunc, newGenerationTable);
-
-        // remember the old table and plug in the new one
-        GenerationTable *oldGenerationTable = s_currentGenerationTable;
-        s_currentGenerationTable = newGenerationTable;
 
-        // WARNING: tricky code!
-        //
-        // We sample the generation table lock *after* plugging in the new table
-        // We do so using an interlocked operation so the cpu can't reorder
-        // the write to the s_currentGenerationTable with the increment.
-        // If the interlocked increment returns 1, we know nobody can be using
-        // the old table (readers increment the lock before using the table,
-        // and decrement it afterwards). Any new readers coming in
-        // will use the new table. So it's safe to delete the old
-        // table.
-        // On the other hand, if the interlocked increment returns
-        // something other than one, we put the old table on a list
-        // dangling off of the new one. Next time around, we'll try again
-        // deleting any old tables.
-        if (FastInterlockIncrement(&s_generationTableLock) == 1)
+        if (s_currentGenerationTable == nullptr)
         {
-            // We know nobody can be using any of the old tables
-            while (oldGenerationTable != NULL)
+            EX_TRY
             {
-                _ASSERTE(oldGenerationTable->magic == GENERATION_TABLE_MAGIC);
-#ifdef  _DEBUG
-                oldGenerationTable->magic = GENERATION_TABLE_BAD_MAGIC;
-#endif
-                GenerationTable *temp = oldGenerationTable;
-                oldGenerationTable = oldGenerationTable->prev;
-                delete[] temp->genDescTable;
-                delete temp;
+                s_currentGenerationTable = new (nothrow) GenerationTable();
             }
+            EX_CATCH
+            {
+            }
+            EX_END_CATCH(SwallowAllExceptions)
         }
-        else
+
+        if (s_currentGenerationTable == nullptr)
         {
-            // put the old table on a list
-            newGenerationTable->prev = oldGenerationTable;
+            RETURN;
         }
-        FastInterlockDecrement(&s_generationTableLock);
+        s_currentGenerationTable->Refresh();        
+    }
+#endif // PROFILING_SUPPORTED
+    RETURN;
+}
+
+void __stdcall ProfilerAddNewRegion(int generation, uint8_t* rangeStart, uint8_t* rangeEnd, uint8_t* rangeEndReserved)
+{
+    CONTRACT_VOID
+    {
+        NOTHROW;
+        GC_NOTRIGGER;
+        MODE_ANY; // can be called even on GC threads
+    } CONTRACT_END;
+#ifdef PROFILING_SUPPORTED
+    if (CORProfilerTrackGC() || CORProfilerTrackBasicGC())
+    {
+        s_currentGenerationTable->AddRecord(generation, rangeStart, rangeEnd, rangeEndReserved);
     }
 #endif // PROFILING_SUPPORTED
     RETURN;
@@ -8885,13 +8949,11 @@ HRESULT ProfToEEInterfaceImpl::GetGenerationBounds(ULONG cObjectRanges,
         // Yay!
         EE_THREAD_NOT_REQUIRED;
 
-        // Yay!
-        CANNOT_TAKE_LOCK;
-
+        // Lock is required to ensure this is synchronized with GC's updates.
+        CAN_TAKE_LOCK;
 
         PRECONDITION(CheckPointer(pcObjectRanges));
         PRECONDITION(cObjectRanges <= 0 || ranges != NULL);
-        PRECONDITION(s_generationTableLock >= 0);
     }
     CONTRACTL_END;
 
@@ -8900,31 +8962,12 @@ HRESULT ProfToEEInterfaceImpl::GetGenerationBounds(ULONG cObjectRanges,
         LL_INFO1000,
         "**PROF: GetGenerationBounds.\n"));
 
-    // Announce we are using the generation table now
-    CounterHolder genTableLock(&s_generationTableLock);
-
-    GenerationTable *generationTable = s_currentGenerationTable;
-
-    if (generationTable == NULL)
+    if (s_currentGenerationTable == NULL)
     {
         return E_FAIL;
     }
 
-    _ASSERTE(generationTable->magic == GENERATION_TABLE_MAGIC);
-
-    GenerationDesc *genDescTable = generationTable->genDescTable;
-    ULONG count = min(generationTable->count, cObjectRanges);
-    for (ULONG i = 0; i < count; i++)
-    {
-        ranges[i].generation          = (COR_PRF_GC_GENERATION)genDescTable[i].generation;
-        ranges[i].rangeStart          = (ObjectID)genDescTable[i].rangeStart;
-        ranges[i].rangeLength         = genDescTable[i].rangeEnd         - genDescTable[i].rangeStart;
-        ranges[i].rangeLengthReserved = genDescTable[i].rangeEndReserved - genDescTable[i].rangeStart;
-    }
-
-    *pcObjectRanges = generationTable->count;
-
-    return S_OK;
+    return s_currentGenerationTable->GetGenerationBounds(cObjectRanges, pcObjectRanges, ranges);
 }
 
 
@@ -9020,7 +9063,6 @@ HRESULT ProfToEEInterfaceImpl::GetObjectGeneration(ObjectID objectId,
 
         PRECONDITION(objectId != NULL);
         PRECONDITION(CheckPointer(range));
-        PRECONDITION(s_generationTableLock >= 0);
     }
     CONTRACTL_END;
 
@@ -9032,36 +9074,22 @@ HRESULT ProfToEEInterfaceImpl::GetObjectGeneration(ObjectID objectId,
 
     
     _ASSERTE((GetThreadNULLOk() == NULL) || (GetThreadNULLOk()->PreemptiveGCDisabled()));
-    
-
-    // Announce we are using the generation table now
-    CounterHolder genTableLock(&s_generationTableLock);
-
-    GenerationTable *generationTable = s_currentGenerationTable;
-
-    if (generationTable == NULL)
-    {
-        return E_FAIL;
-    }
 
-    _ASSERTE(generationTable->magic == GENERATION_TABLE_MAGIC);
+    IGCHeap *hp = GCHeapUtilities::GetGCHeap();
 
-    GenerationDesc *genDescTable = generationTable->genDescTable;
-    ULONG count = generationTable->count;
-    for (ULONG i = 0; i < count; i++)
-    {
-        if (genDescTable[i].rangeStart <= (BYTE *)objectId && (BYTE *)objectId < genDescTable[i].rangeEndReserved)
-        {
-            range->generation          = (COR_PRF_GC_GENERATION)genDescTable[i].generation;
-            range->rangeStart          = (ObjectID)genDescTable[i].rangeStart;
-            range->rangeLength         = genDescTable[i].rangeEnd         - genDescTable[i].rangeStart;
-            range->rangeLengthReserved = genDescTable[i].rangeEndReserved - genDescTable[i].rangeStart;
+    uint8_t* pStart;
+    uint8_t* pAllocated;
+    uint8_t* pReserved;
+    unsigned int generation = hp->GetGenerationWithRange((Object*)objectId, &pStart, &pAllocated, &pReserved);
 
-            return S_OK;
-        }
-    }
+    UINT_PTR rangeLength = pAllocated - pStart;
+    UINT_PTR rangeLengthReserved = pReserved - pStart;
 
-    return E_FAIL;
+    range->generation = (COR_PRF_GC_GENERATION)generation;
+    range->rangeStart = (ObjectID)pStart;
+    range->rangeLength = rangeLength;
+    range->rangeLengthReserved = rangeLengthReserved;
+    return S_OK;
 }
 
 HRESULT ProfToEEInterfaceImpl::GetReJITIDs(