From b4932783ca1ff1868054cc5061c66d8a2d90e2f3 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Tue, 17 Aug 2021 07:18:47 -0700 Subject: [PATCH] Fix DacHeapWalker for USE_REGIONS (#56796) --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 139 ++++++++++++++++----- src/coreclr/debug/daccess/request.cpp | 48 +++---- src/coreclr/debug/daccess/request_common.h | 89 +++++++++---- src/coreclr/debug/daccess/request_svr.cpp | 115 ++++++++++++----- .../gc/{gc_typefields.h => dac_gcheap_fields.h} | 16 +-- src/coreclr/gc/dac_generation_fields.h | 7 ++ src/coreclr/gc/gc.cpp | 35 ++++-- src/coreclr/gc/gcinterface.dac.h | 23 +++- src/coreclr/gc/gcinterface.dacvars.def | 5 +- src/coreclr/gc/gcpriv.h | 6 - 10 files changed, 336 insertions(+), 147 deletions(-) rename src/coreclr/gc/{gc_typefields.h => dac_gcheap_fields.h} (79%) create mode 100644 src/coreclr/gc/dac_generation_fields.h diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index d8c13be..2924be3 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -6452,7 +6452,10 @@ HRESULT DacHeapWalker::MoveToNextObject() mCurrObj += mCurrSize; // Check to see if we are in the correct bounds. - if (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj) + bool isGen0 = IsRegion() ? (mHeaps[mCurrHeap].Segments[mCurrSeg].Generation == 0) : + (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj); + + if (isGen0) CheckAllocAndSegmentRange(); // Check to see if we've moved off the end of a segment @@ -6541,7 +6544,10 @@ HRESULT DacHeapWalker::NextSegment() mCurrObj = mHeaps[mCurrHeap].Segments[mCurrSeg].Start; - if (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj) + bool isGen0 = IsRegion() ? (mHeaps[mCurrHeap].Segments[mCurrSeg].Generation == 0) : + (mHeaps[mCurrHeap].Gen0Start <= mCurrObj && mHeaps[mCurrHeap].Gen0End > mCurrObj); + + if (isGen0) CheckAllocAndSegmentRange(); if (!mCache.ReadMT(mCurrObj, &mCurrMT)) @@ -6723,54 +6729,107 @@ HRESULT DacHeapWalker::ListNearObjects(CORDB_ADDRESS obj, CORDB_ADDRESS *pPrev, HRESULT DacHeapWalker::InitHeapDataWks(HeapData *&pHeaps, size_t &pCount) { + bool regions = IsRegion(); + // Scrape basic heap details pCount = 1; pHeaps = new (nothrow) HeapData[1]; if (pHeaps == NULL) return E_OUTOFMEMORY; - dac_generation gen0 = *GenerationTableIndex(g_gcDacGlobals->generation_table, 0); - dac_generation gen1 = *GenerationTableIndex(g_gcDacGlobals->generation_table, 1); - dac_generation gen2 = *GenerationTableIndex(g_gcDacGlobals->generation_table, 2); - dac_generation loh = *GenerationTableIndex(g_gcDacGlobals->generation_table, 3); - dac_generation poh = *GenerationTableIndex(g_gcDacGlobals->generation_table, 4); + dac_generation gen0 = GenerationTableIndex(g_gcDacGlobals->generation_table, 0); + dac_generation gen1 = GenerationTableIndex(g_gcDacGlobals->generation_table, 1); + dac_generation gen2 = GenerationTableIndex(g_gcDacGlobals->generation_table, 2); + dac_generation loh = GenerationTableIndex(g_gcDacGlobals->generation_table, 3); + dac_generation poh = GenerationTableIndex(g_gcDacGlobals->generation_table, 4); pHeaps[0].YoungestGenPtr = (CORDB_ADDRESS)gen0.allocation_context.alloc_ptr; pHeaps[0].YoungestGenLimit = (CORDB_ADDRESS)gen0.allocation_context.alloc_limit; - pHeaps[0].Gen0Start = (CORDB_ADDRESS)gen0.allocation_start; - pHeaps[0].Gen0End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; - pHeaps[0].Gen1Start = (CORDB_ADDRESS)gen1.allocation_start; + if (!regions) + { + pHeaps[0].Gen0Start = (CORDB_ADDRESS)gen0.allocation_start; + pHeaps[0].Gen0End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; + pHeaps[0].Gen1Start = (CORDB_ADDRESS)gen1.allocation_start; + } // Segments int count = GetSegmentCount(loh.start_segment); count += GetSegmentCount(poh.start_segment); count += GetSegmentCount(gen2.start_segment); + if (regions) + { + count += GetSegmentCount(gen1.start_segment); + count += GetSegmentCount(gen0.start_segment); + } pHeaps[0].SegmentCount = count; pHeaps[0].Segments = new (nothrow) SegmentData[count]; if (pHeaps[0].Segments == NULL) return E_OUTOFMEMORY; - // Small object heap segments - DPTR(dac_heap_segment) seg = gen2.start_segment; + DPTR(dac_heap_segment) seg; int i = 0; - for (; seg && (i < count); ++i) + + // Small object heap segments + if (regions) { - pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; - if (seg.GetAddr() == (TADDR)*g_gcDacGlobals->ephemeral_heap_segment) + seg = gen2.start_segment; + for (; seg && (i < count); ++i) { - pHeaps[0].Segments[i].End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; - pHeaps[0].Segments[i].Generation = 1; - pHeaps[0].EphemeralSegment = i; + pHeaps[0].Segments[i].Generation = 2; + pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; + + seg = seg->next; } - else + seg = gen1.start_segment; + for (; seg && (i < count); ++i) { + pHeaps[0].Segments[i].Generation = 1; + pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; - pHeaps[0].Segments[i].Generation = 2; + + seg = seg->next; } + seg = gen0.start_segment; + for (; seg && (i < count); ++i) + { + pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; + if (seg.GetAddr() == (TADDR)*g_gcDacGlobals->ephemeral_heap_segment) + { + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; + pHeaps[0].EphemeralSegment = i; + } + else + { + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; + } + pHeaps[0].Segments[i].Generation = 0; - seg = seg->next; + seg = seg->next; + } + } + else + { + DPTR(dac_heap_segment) seg = gen2.start_segment; + for (; seg && (i < count); ++i) + { + pHeaps[0].Segments[i].Start = (CORDB_ADDRESS)seg->mem; + if (seg.GetAddr() == (TADDR)*g_gcDacGlobals->ephemeral_heap_segment) + { + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)*g_gcDacGlobals->alloc_allocated; + pHeaps[0].Segments[i].Generation = 1; + pHeaps[0].EphemeralSegment = i; + } + else + { + pHeaps[0].Segments[i].End = (CORDB_ADDRESS)seg->allocated; + pHeaps[0].Segments[i].Generation = 2; + } + + seg = seg->next; + } } // Large object heap segments @@ -6880,6 +6939,8 @@ HRESULT DacDbiInterfaceImpl::GetHeapSegments(OUT DacDbiArrayList *p size_t heapCount = 0; HeapData *heaps = 0; + bool region = IsRegion(); + #ifdef FEATURE_SVR_GC HRESULT hr = GCHeapUtilities::IsServerHeap() ? DacHeapWalker::InitHeapDataSvr(heaps, heapCount) : DacHeapWalker::InitHeapDataWks(heaps, heapCount); #else @@ -6892,16 +6953,20 @@ HRESULT DacDbiInterfaceImpl::GetHeapSegments(OUT DacDbiArrayList *p int total = 0; for (size_t i = 0; i < heapCount; ++i) { - // SegmentCount is +1 due to the ephemeral segment containing more than one - // generation (Gen1 + Gen0, and sometimes part of Gen2). - total += (int)heaps[i].SegmentCount + 1; - - // It's possible that part of Gen2 lives on the ephemeral segment. If so, - // we need to add one more to the output. - const size_t eph = heaps[i].EphemeralSegment; - _ASSERTE(eph < heaps[i].SegmentCount); - if (heaps[i].Segments[eph].Start != heaps[i].Gen1Start) + total += (int)heaps[i].SegmentCount; + if (!region) + { + // SegmentCount is +1 due to the ephemeral segment containing more than one + // generation (Gen1 + Gen0, and sometimes part of Gen2). total++; + + // It's possible that part of Gen2 lives on the ephemeral segment. If so, + // we need to add one more to the output. + const size_t eph = heaps[i].EphemeralSegment; + _ASSERTE(eph < heaps[i].SegmentCount); + if (heaps[i].Segments[eph].Start != heaps[i].Gen1Start) + total++; + } } pSegments->Alloc(total); @@ -6910,9 +6975,10 @@ HRESULT DacDbiInterfaceImpl::GetHeapSegments(OUT DacDbiArrayList *p int curr = 0; for (size_t i = 0; i < heapCount; ++i) { - // Generation 0 is not in the segment list. _ASSERTE(curr < total); + if (!region) { + // Generation 0 is not in the segment list. COR_SEGMENT &seg = (*pSegments)[curr++]; seg.start = heaps[i].Gen0Start; seg.end = heaps[i].Gen0End; @@ -6922,7 +6988,16 @@ HRESULT DacDbiInterfaceImpl::GetHeapSegments(OUT DacDbiArrayList *p for (size_t j = 0; j < heaps[i].SegmentCount; ++j) { - if (heaps[i].Segments[j].Generation == 1) + if (region) + { + _ASSERTE(curr < total); + COR_SEGMENT &seg = (*pSegments)[curr++]; + seg.start = heaps[i].Segments[j].Start; + seg.end = heaps[i].Segments[j].End; + seg.type = (CorDebugGenerationTypes)heaps[i].Segments[j].Generation; + seg.heap = (ULONG)i; + } + else if (heaps[i].Segments[j].Generation == 1) { // This is the ephemeral segment. We have already written Gen0, // now write Gen1. diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 4e729c8..f075dbe 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -747,10 +747,10 @@ ClrDataAccess::GetHeapAllocData(unsigned int count, struct DacpGenerationAllocDa if (data && count >= 1) { - DPTR(dac_generation) table = g_gcDacGlobals->generation_table; + DPTR(unused_generation) table = g_gcDacGlobals->generation_table; for (unsigned int i=0; i < *g_gcDacGlobals->max_gen + 2; i++) { - dac_generation entry = *GenerationTableIndex(table, i); + dac_generation entry = GenerationTableIndex(table, i); data[0].allocData[i].allocBytes = (CLRDATA_ADDRESS)(ULONG_PTR) entry.allocation_context.alloc_bytes; data[0].allocData[i].allocBytesLoh = (CLRDATA_ADDRESS)(ULONG_PTR) entry.allocation_context.alloc_bytes_uoh; } @@ -2809,12 +2809,12 @@ ClrDataAccess::GetGCHeapStaticData(struct DacpGcHeapDetails *detailsData) // get bounds for the different generations for (unsigned int i=0; i < DAC_NUMBERGENERATIONS; i++) { - DPTR(dac_generation) generation = GenerationTableIndex(g_gcDacGlobals->generation_table, i); - detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS) dac_cast(generation->start_segment); - detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS) generation->allocation_start; - DPTR(gc_alloc_context) alloc_context = dac_cast(generation) + offsetof(dac_generation, allocation_context); - detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)alloc_context->alloc_ptr; - detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)alloc_context->alloc_limit; + dac_generation generation = GenerationTableIndex(g_gcDacGlobals->generation_table, i); + detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS) dac_cast(generation.start_segment); + detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS) generation.allocation_start; + gc_alloc_context alloc_context = generation.allocation_context; + detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)alloc_context.alloc_ptr; + detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)alloc_context.alloc_limit; } if (g_gcDacGlobals->finalize_queue.IsValid()) @@ -3790,12 +3790,12 @@ ClrDataAccess::EnumWksGlobalMemoryRegions(CLRDataEnumMemoryFlags flags) // this is the convention in the GC so it is repeated here for (ULONG i = *g_gcDacGlobals->max_gen; i <= *g_gcDacGlobals->max_gen +1; i++) { - dac_generation *gen = GenerationTableIndex(g_gcDacGlobals->generation_table, i); - __DPtr seg = dac_cast(gen->start_segment); + dac_generation gen = GenerationTableIndex(g_gcDacGlobals->generation_table, i); + __DPtr seg = dac_cast(gen.start_segment); while (seg) { - DacEnumMemoryRegion(dac_cast(seg), sizeof(dac_heap_segment)); - seg = seg->next; + DacEnumMemoryRegion(dac_cast(seg), sizeof(dac_heap_segment)); + seg = seg->next; } } } @@ -4619,14 +4619,14 @@ HRESULT ClrDataAccess::GetGenerationTable(unsigned int cGenerations, struct Dacp { for (unsigned int i = 0; i < numGenerationTableEntries; i++) { - DPTR(dac_generation) generation = GenerationTableIndex(g_gcDacGlobals->generation_table, i); - pGenerationData[i].start_segment = (CLRDATA_ADDRESS) dac_cast(generation->start_segment); + dac_generation generation = GenerationTableIndex(g_gcDacGlobals->generation_table, i); + pGenerationData[i].start_segment = (CLRDATA_ADDRESS) dac_cast(generation.start_segment); - pGenerationData[i].allocation_start = (CLRDATA_ADDRESS) generation->allocation_start; + pGenerationData[i].allocation_start = (CLRDATA_ADDRESS) generation.allocation_start; - DPTR(gc_alloc_context) alloc_context = dac_cast(generation) + offsetof(dac_generation, allocation_context); - pGenerationData[i].allocContextPtr = (CLRDATA_ADDRESS)alloc_context->alloc_ptr; - pGenerationData[i].allocContextLimit = (CLRDATA_ADDRESS)alloc_context->alloc_limit; + gc_alloc_context alloc_context = generation.allocation_context; + pGenerationData[i].allocContextPtr = (CLRDATA_ADDRESS)alloc_context.alloc_ptr; + pGenerationData[i].allocContextLimit = (CLRDATA_ADDRESS)alloc_context.alloc_limit; } } else @@ -4709,12 +4709,12 @@ HRESULT ClrDataAccess::GetGenerationTableSvr(CLRDATA_ADDRESS heapAddr, unsigned { for (unsigned int i = 0; i < numGenerationTableEntries; ++i) { - DPTR(dac_generation) generation = ServerGenerationTableIndex(heapAddress, i); - pGenerationData[i].start_segment = (CLRDATA_ADDRESS)dac_cast(generation->start_segment); - pGenerationData[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR)generation->allocation_start; - DPTR(gc_alloc_context) alloc_context = dac_cast(generation) + offsetof(dac_generation, allocation_context); - pGenerationData[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context->alloc_ptr; - pGenerationData[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context->alloc_limit; + dac_generation generation = ServerGenerationTableIndex(heapAddress, i); + pGenerationData[i].start_segment = (CLRDATA_ADDRESS)dac_cast(generation.start_segment); + pGenerationData[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR)generation.allocation_start; + gc_alloc_context alloc_context = generation.allocation_context; + pGenerationData[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR)alloc_context.alloc_ptr; + pGenerationData[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR)alloc_context.alloc_limit; } } else diff --git a/src/coreclr/debug/daccess/request_common.h b/src/coreclr/debug/daccess/request_common.h index 1b86b46..e6c976f 100644 --- a/src/coreclr/debug/daccess/request_common.h +++ b/src/coreclr/debug/daccess/request_common.h @@ -29,35 +29,27 @@ DPTR(T) Dereference(DPTR(T*) ptr) // Indexes into the global heap table, returning a TADDR to the requested // heap instance. inline TADDR -HeapTableIndex(DPTR(opaque_gc_heap**) heaps, size_t index) +HeapTableIndex(DPTR(unused_gc_heap**) heaps, size_t index) { - DPTR(opaque_gc_heap*) heap_table = Dereference(heaps); - DPTR(opaque_gc_heap*) ptr = TableIndex(heap_table, index, sizeof(void*)); + DPTR(unused_gc_heap*) heap_table = Dereference(heaps); + DPTR(unused_gc_heap*) ptr = TableIndex(heap_table, index, sizeof(void*)); return Dereference(ptr).GetAddr(); } -// Indexes into a given generation table, returning a DPTR to the -// requested element (the element at the given index) of the table. -inline DPTR(dac_generation) -GenerationTableIndex(DPTR(dac_generation) base, size_t index) -{ - return TableIndex(base, index, g_gcDacGlobals->generation_size); -} - -// Starting .NET 6, it is possible for the gc_heap object to have different layouts -// for coreclr.dll and clrgc.dll. Instead of using assuming dac_gc_heap and gc_heap -// have identical layout, we have the gc exported an array of field offsets instead. +// Starting .NET 6, it is possible for the gc_heap and generation object to have different +// layouts for coreclr.dll and clrgc.dll. Instead of using assuming dac_gc_heap and gc_heap +// have identical layout, we have the gc exported arrays of field offsets instead. // These offsets could be -1, indicating the field does not exist in the current -// gc_heap being used. +// gc_heap or generation being used. // field_offset = g_gcDacGlobals->gc_heap_field_offsets // p_field_offset = field_offset[field_index] -// p_field = heap + p_field_offset +// p_field = BASE + p_field_offset // field_index++ #define LOAD_BASE(field_name, field_type) \ DPTR(int) p_##field_name##_offset = TableIndex(field_offsets, field_index, sizeof(int)); \ int field_name##_offset = *p_##field_name##_offset; \ - DPTR(field_type) p_##field_name = heap + field_name##_offset; \ + DPTR(field_type) p_##field_name = BASE + field_name##_offset; \ field_index++; // if (field_offset != -1) @@ -95,16 +87,9 @@ GenerationTableIndex(DPTR(dac_generation) base, size_t index) } \ } -// Indexes into a heap's generation table, given the heap instance -// and the desired index. Returns a DPTR to the requested element. -inline DPTR(dac_generation) -ServerGenerationTableIndex(TADDR heap, size_t index) +inline bool IsRegion() { - DPTR(int) field_offsets = g_gcDacGlobals->gc_heap_field_offsets; - int field_index = GENERATION_TABLE_FIELD_INDEX; - LOAD_BASE (generation_table, dac_generation); - assert (generation_table_offset != -1); - return TableIndex(p_generation_table, index, g_gcDacGlobals->generation_size); + return (g_gcDacGlobals->minor_version_number & 1) != 0; } // Load an instance of dac_gc_heap for the heap pointed by heap. @@ -119,21 +104,71 @@ LoadGcHeapData(TADDR heap) DPTR(int) field_offsets = g_gcDacGlobals->gc_heap_field_offsets; int field_index = 0; +#define BASE heap #define ALL_FIELDS #define DEFINE_FIELD(field_name, field_type) LOAD(field_name, field_type) #define DEFINE_DPTR_FIELD(field_name, field_type) LOAD_DPTR(field_name, field_type) #define DEFINE_ARRAY_FIELD(field_name, field_type, array_length) LOAD_ARRAY(field_name, field_type, array_length); -#include "../../gc/gc_typefields.h" +#include "../../gc/dac_gcheap_fields.h" #undef DEFINE_ARRAY_FIELD #undef DEFINE_DPTR_FIELD #undef DEFINE_FIELD #undef ALL_FIELDS +#undef BASE return result; } +// Load an instance of dac_generation for the generation pointed by generation. +// Fields that does not exist in the current generation instance is zero initialized. +// Return the dac_generation object. +inline dac_generation +LoadGeneration(TADDR generation) +{ + dac_generation result; + memset(&result, 0, sizeof(dac_generation)); + + DPTR(int) field_offsets = g_gcDacGlobals->generation_field_offsets; + int field_index = 0; + +#define BASE generation +#define ALL_FIELDS +#define DEFINE_FIELD(field_name, field_type) LOAD(field_name, field_type) +#define DEFINE_DPTR_FIELD(field_name, field_type) LOAD_DPTR(field_name, field_type) + +#include "../../gc/dac_generation_fields.h" + +#undef DEFINE_DPTR_FIELD +#undef DEFINE_FIELD +#undef ALL_FIELDS +#undef BASE + + return result; +} + +// Indexes into a given generation table, returning a dac_generation +inline dac_generation +GenerationTableIndex(DPTR(unused_generation) base, size_t index) +{ + return LoadGeneration(TableIndex(base, index, g_gcDacGlobals->generation_size).GetAddr()); +} + +// Indexes into a heap's generation table, given the heap instance +// and the desired index. Returns a dac_generation +inline dac_generation +ServerGenerationTableIndex(TADDR heap, size_t index) +{ + DPTR(int) field_offsets = g_gcDacGlobals->gc_heap_field_offsets; + int field_index = GENERATION_TABLE_FIELD_INDEX; + #define BASE heap + LOAD_BASE (generation_table, unused_generation); + #undef BASE + assert (generation_table_offset != -1); + return LoadGeneration(TableIndex(p_generation_table, index, g_gcDacGlobals->generation_size).GetAddr()); +} + #undef LOAD_ARRAY #undef LOAD_DPTR #undef LOAD diff --git a/src/coreclr/debug/daccess/request_svr.cpp b/src/coreclr/debug/daccess/request_svr.cpp index 2d176f6..5ff14b0 100644 --- a/src/coreclr/debug/daccess/request_svr.cpp +++ b/src/coreclr/debug/daccess/request_svr.cpp @@ -89,7 +89,7 @@ HRESULT ClrDataAccess::GetServerAllocData(unsigned int count, struct DacpGenerat TADDR pHeap = HeapTableIndex(g_gcDacGlobals->g_heaps, n); for (int i=0;igeneration_table[i].start_segment = (CLRDATA_ADDRESS)dac_cast(generation->start_segment); - detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR)generation->allocation_start; - DPTR(gc_alloc_context) alloc_context = dac_cast(generation) + offsetof(dac_generation, allocation_context); - detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context->alloc_ptr; - detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context->alloc_limit; + dac_generation generation = ServerGenerationTableIndex(heapAddress, i); + detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS)dac_cast(generation.start_segment); + detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR)generation.allocation_start; + gc_alloc_context alloc_context = generation.allocation_context; + detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context.alloc_ptr; + detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context.alloc_limit; } DPTR(dac_finalize_queue) fq = pHeap->finalize_queue; @@ -268,12 +268,12 @@ ClrDataAccess::EnumSvrGlobalMemoryRegions(CLRDataEnumMemoryFlags flags) // this is the convention in the GC so it is repeated here for (ULONG i = *g_gcDacGlobals->max_gen; i <= *g_gcDacGlobals->max_gen +1; i++) { - DPTR(dac_heap_segment) seg = ServerGenerationTableIndex(heapAddress, i)->start_segment; + dac_generation generation = ServerGenerationTableIndex(heapAddress, i); + DPTR(dac_heap_segment) seg = generation.start_segment; while (seg) { - DacEnumMemoryRegion(PTR_HOST_TO_TADDR(seg), sizeof(dac_heap_segment)); - - seg = seg->next; + DacEnumMemoryRegion(PTR_HOST_TO_TADDR(seg), sizeof(dac_heap_segment)); + seg = seg->next; } } } @@ -290,6 +290,8 @@ DWORD DacGetNumHeaps() HRESULT DacHeapWalker::InitHeapDataSvr(HeapData *&pHeaps, size_t &pCount) { + bool regions = IsRegion(); + if (g_gcDacGlobals->n_heaps == nullptr || g_gcDacGlobals->g_heaps == nullptr) return S_OK; @@ -306,48 +308,99 @@ HRESULT DacHeapWalker::InitHeapDataSvr(HeapData *&pHeaps, size_t &pCount) TADDR heapAddress = HeapTableIndex(g_gcDacGlobals->g_heaps, i); dac_gc_heap heap = LoadGcHeapData(heapAddress); dac_gc_heap* pHeap = &heap; - dac_generation gen0 = *ServerGenerationTableIndex(heapAddress, 0); - dac_generation gen1 = *ServerGenerationTableIndex(heapAddress, 1); - dac_generation gen2 = *ServerGenerationTableIndex(heapAddress, 2); - dac_generation loh = *ServerGenerationTableIndex(heapAddress, 3); - dac_generation poh = *ServerGenerationTableIndex(heapAddress, 4); + dac_generation gen0 = ServerGenerationTableIndex(heapAddress, 0); + dac_generation gen1 = ServerGenerationTableIndex(heapAddress, 1); + dac_generation gen2 = ServerGenerationTableIndex(heapAddress, 2); + dac_generation loh = ServerGenerationTableIndex(heapAddress, 3); + dac_generation poh = ServerGenerationTableIndex(heapAddress, 4); pHeaps[i].YoungestGenPtr = (CORDB_ADDRESS)gen0.allocation_context.alloc_ptr; pHeaps[i].YoungestGenLimit = (CORDB_ADDRESS)gen0.allocation_context.alloc_limit; - pHeaps[i].Gen0Start = (CORDB_ADDRESS)gen0.allocation_start; - pHeaps[i].Gen0End = (CORDB_ADDRESS)pHeap->alloc_allocated; - pHeaps[i].Gen1Start = (CORDB_ADDRESS)gen1.allocation_start; + if (!regions) + { + pHeaps[i].Gen0Start = (CORDB_ADDRESS)gen0.allocation_start; + pHeaps[i].Gen0End = (CORDB_ADDRESS)pHeap->alloc_allocated; + pHeaps[i].Gen1Start = (CORDB_ADDRESS)gen1.allocation_start; + } // Segments int count = GetSegmentCount(loh.start_segment); count += GetSegmentCount(poh.start_segment); count += GetSegmentCount(gen2.start_segment); + if (regions) + { + count += GetSegmentCount(gen1.start_segment); + count += GetSegmentCount(gen0.start_segment); + } pHeaps[i].SegmentCount = count; pHeaps[i].Segments = new (nothrow) SegmentData[count]; if (pHeaps[i].Segments == NULL) return E_OUTOFMEMORY; - // Small object heap segments - DPTR(dac_heap_segment) seg = gen2.start_segment; + DPTR(dac_heap_segment) seg; int j = 0; - for (; seg && (j < count); ++j) + // Small object heap segments + if (regions) { - pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; - if (seg.GetAddr() == pHeap->ephemeral_heap_segment.GetAddr()) + seg = gen2.start_segment; + for (; seg && (j < count); ++j) { - pHeaps[i].Segments[j].End = (CORDB_ADDRESS)pHeap->alloc_allocated; - pHeaps[i].EphemeralSegment = j; - pHeaps[i].Segments[j].Generation = 1; + pHeaps[i].Segments[j].Generation = 2; + pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; + + seg = seg->next; } - else + seg = gen1.start_segment; + for (; seg && (j < count); ++j) { + pHeaps[i].Segments[j].Generation = 1; + pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; - pHeaps[i].Segments[j].Generation = 2; - } - seg = seg->next; + seg = seg->next; + } + seg = gen0.start_segment; + for (; seg && (j < count); ++j) + { + pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; + if (seg.GetAddr() == pHeap->ephemeral_heap_segment.GetAddr()) + { + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)pHeap->alloc_allocated; + pHeaps[i].EphemeralSegment = j; + pHeaps[i].Segments[j].Generation = 0; + } + else + { + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; + pHeaps[i].Segments[j].Generation = 2; + } + + seg = seg->next; + } + } + else + { + seg = gen2.start_segment; + for (; seg && (j < count); ++j) + { + pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; + if (seg.GetAddr() == pHeap->ephemeral_heap_segment.GetAddr()) + { + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)pHeap->alloc_allocated; + pHeaps[i].EphemeralSegment = j; + pHeaps[i].Segments[j].Generation = 1; + } + else + { + pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; + pHeaps[i].Segments[j].Generation = 2; + } + + seg = seg->next; + } } // Large object heap segments diff --git a/src/coreclr/gc/gc_typefields.h b/src/coreclr/gc/dac_gcheap_fields.h similarity index 79% rename from src/coreclr/gc/gc_typefields.h rename to src/coreclr/gc/dac_gcheap_fields.h index f690a6c..1041572 100644 --- a/src/coreclr/gc/gc_typefields.h +++ b/src/coreclr/gc/dac_gcheap_fields.h @@ -19,14 +19,14 @@ DEFINE_FIELD (background_saved_highest_address, uint8_t*) DEFINE_DPTR_FIELD (saved_sweep_ephemeral_seg, dac_heap_segment) DEFINE_FIELD (saved_sweep_ephemeral_start, uint8_t*) #else -DEFINE_MISSING_FIELD -DEFINE_MISSING_FIELD +DEFINE_MISSING_FIELD(saved_sweep_ephemeral_seg) +DEFINE_MISSING_FIELD(saved_sweep_ephemeral_start) #endif #else -DEFINE_MISSING_FIELD -DEFINE_MISSING_FIELD -DEFINE_MISSING_FIELD -DEFINE_MISSING_FIELD -DEFINE_MISSING_FIELD -DEFINE_MISSING_FIELD +DEFINE_MISSING_FIELD(mark_array) +DEFINE_MISSING_FIELD(next_sweep_obj) +DEFINE_MISSING_FIELD(background_saved_lowest_address) +DEFINE_MISSING_FIELD(background_saved_highest_address) +DEFINE_MISSING_FIELD(saved_sweep_ephemeral_seg) +DEFINE_MISSING_FIELD(saved_sweep_ephemeral_start) #endif diff --git a/src/coreclr/gc/dac_generation_fields.h b/src/coreclr/gc/dac_generation_fields.h new file mode 100644 index 0000000..c67197a --- /dev/null +++ b/src/coreclr/gc/dac_generation_fields.h @@ -0,0 +1,7 @@ +DEFINE_FIELD (allocation_context, gc_alloc_context) +DEFINE_DPTR_FIELD (start_segment, dac_heap_segment) +#ifndef USE_REGIONS +DEFINE_FIELD (allocation_start, uint8_t*) +#else +DEFINE_MISSING_FIELD(allocation_start) +#endif \ No newline at end of file diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 9aada04..47ff77d 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -46209,25 +46209,32 @@ void PopulateDacVars(GcDacVars *gcDacVars) { #ifndef DACCESS_COMPILE +#define DEFINE_FIELD(field_name, field_type) offsetof(CLASS_NAME, field_name), +#define DEFINE_DPTR_FIELD(field_name, field_type) offsetof(CLASS_NAME, field_name), +#define DEFINE_ARRAY_FIELD(field_name, field_type, array_length) offsetof(CLASS_NAME, field_name), +#define DEFINE_MISSING_FIELD(field_name) -1, + #ifdef MULTIPLE_HEAPS static int gc_heap_field_offsets[] = { +#define CLASS_NAME gc_heap +#include "dac_gcheap_fields.h" +#undef CLASS_NAME -#define DEFINE_FIELD(field_name, field_type) offsetof(gc_heap, field_name), -#define DEFINE_DPTR_FIELD(field_name, field_type) offsetof(gc_heap, field_name), -#define DEFINE_ARRAY_FIELD(field_name, field_type, array_length) offsetof(gc_heap, field_name), -#define DEFINE_MISSING_FIELD -1, + offsetof(gc_heap, generation_table) + }; + static_assert(sizeof(gc_heap_field_offsets) == (GENERATION_TABLE_FIELD_INDEX + 1) * sizeof(int), "GENERATION_TABLE_INDEX mismatch"); +#endif //MULTIPLE_HEAPS + static int generation_field_offsets[] = { -#include "gc_typefields.h" +#define CLASS_NAME generation +#include "dac_generation_fields.h" +#undef CLASS_NAME #undef DEFINE_MISSING_FIELD #undef DEFINE_ARRAY_FIELD #undef DEFINE_DPTR_FIELD #undef DEFINE_FIELD - - offsetof(gc_heap, generation_table) }; - static_assert(sizeof(gc_heap_field_offsets) == (GENERATION_TABLE_FIELD_INDEX + 1) * sizeof(int), "GENERATION_TABLE_INDEX mismatch"); -#endif //MULTIPLE_HEAPS assert(gcDacVars != nullptr); *gcDacVars = {}; @@ -46236,6 +46243,9 @@ void PopulateDacVars(GcDacVars *gcDacVars) // SOS_BREAKING_CHANGE_VERSION in both the runtime and the diagnostics repo gcDacVars->major_version_number = 1; gcDacVars->minor_version_number = 0; +#ifdef USE_REGIONS + gcDacVars->minor_version_number |= 1; +#endif //USE_REGIONS gcDacVars->built_with_svr = &g_built_with_svr_gc; gcDacVars->build_variant = &g_build_variant; gcDacVars->gc_structures_invalid_cnt = const_cast(&GCScan::m_GcStructuresInvalidCnt); @@ -46252,14 +46262,14 @@ void PopulateDacVars(GcDacVars *gcDacVars) #else gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast(&gc_heap::saved_sweep_ephemeral_seg); gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start; -#endif //!USE_REGIONS +#endif //USE_REGIONS gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address; gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address; gcDacVars->alloc_allocated = &gc_heap::alloc_allocated; gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; gcDacVars->oom_info = &gc_heap::oom_info; gcDacVars->finalize_queue = reinterpret_cast(&gc_heap::finalize_queue); - gcDacVars->generation_table = reinterpret_cast(&gc_heap::generation_table); + gcDacVars->generation_table = reinterpret_cast(&gc_heap::generation_table); #ifdef GC_CONFIG_DRIVEN gcDacVars->gc_global_mechanisms = reinterpret_cast(&gc_global_mechanisms); gcDacVars->interesting_data_per_heap = reinterpret_cast(&gc_heap::interesting_data_per_heap); @@ -46274,9 +46284,10 @@ void PopulateDacVars(GcDacVars *gcDacVars) #endif // HEAP_ANALYZE #else gcDacVars->n_heaps = &gc_heap::n_heaps; - gcDacVars->g_heaps = reinterpret_cast(&gc_heap::g_heaps); + gcDacVars->g_heaps = reinterpret_cast(&gc_heap::g_heaps); gcDacVars->gc_heap_field_offsets = reinterpret_cast(&gc_heap_field_offsets); #endif // MULTIPLE_HEAPS + gcDacVars->generation_field_offsets = reinterpret_cast(&generation_field_offsets); #else UNREFERENCED_PARAMETER(gcDacVars); #endif // DACCESS_COMPILE diff --git a/src/coreclr/gc/gcinterface.dac.h b/src/coreclr/gc/gcinterface.dac.h index 3a4697a..cfffcf1 100644 --- a/src/coreclr/gc/gcinterface.dac.h +++ b/src/coreclr/gc/gcinterface.dac.h @@ -46,9 +46,15 @@ public: // of a generation and its allocation context. class dac_generation { public: - gc_alloc_context allocation_context; - DPTR(dac_heap_segment) start_segment; - uint8_t* allocation_start; +#define ALL_FIELDS +#define DEFINE_FIELD(field_name, field_type) field_type field_name; +#define DEFINE_DPTR_FIELD(field_name, field_type) DPTR(field_type) field_name; + +#include "dac_generation_fields.h" + +#undef DEFINE_DPTR_FIELD +#undef DEFINE_FIELD +#undef ALL_FIELDS }; // Analogue for the GC CFinalize class, containing information about the finalize queue. @@ -141,7 +147,7 @@ public: #define DEFINE_DPTR_FIELD(field_name, field_type) DPTR(field_type) field_name; #define DEFINE_ARRAY_FIELD(field_name, field_type, array_length) field_type field_name[array_length]; -#include "gc_typefields.h" +#include "dac_gcheap_fields.h" #undef DEFINE_ARRAY_FIELD #undef DEFINE_DPTR_FIELD @@ -164,7 +170,14 @@ public: #define GENERATION_TABLE_FIELD_INDEX 18 -struct opaque_gc_heap +// Unlike other DACized structures, these types are loaded manually in the debugger. +// To avoid misuse, pointers to them are explicitly casted to these unused type. +struct unused_gc_heap +{ + uint8_t unused; +}; + +struct unused_generation { uint8_t unused; }; diff --git a/src/coreclr/gc/gcinterface.dacvars.def b/src/coreclr/gc/gcinterface.dacvars.def index 2f27df7..fe7edcc 100644 --- a/src/coreclr/gc/gcinterface.dacvars.def +++ b/src/coreclr/gc/gcinterface.dacvars.def @@ -36,7 +36,7 @@ GC_DAC_VAR (uint8_t, build_variant) GC_DAC_VAR (bool, built_with_svr) GC_DAC_ARRAY_VAR (size_t, gc_global_mechanisms) -GC_DAC_ARRAY_VAR (dac_generation, generation_table) +GC_DAC_ARRAY_VAR (unused_generation, generation_table) GC_DAC_VAR (uint32_t, max_gen) GC_DAC_PTR_VAR (uint32_t, mark_array) GC_DAC_VAR (c_gc_state, current_c_gc_state) @@ -53,7 +53,7 @@ GC_DAC_PTR_VAR (uint8_t*, internal_root_array) GC_DAC_VAR (size_t, internal_root_array_index) GC_DAC_VAR (BOOL, heap_analyze_success) GC_DAC_VAR (int, n_heaps) -GC_DAC_PTR_VAR (opaque_gc_heap*, g_heaps) +GC_DAC_PTR_VAR (unused_gc_heap*, g_heaps) GC_DAC_VAR (int32_t, gc_structures_invalid_cnt) GC_DAC_ARRAY_VAR (size_t, interesting_data_per_heap) GC_DAC_ARRAY_VAR (size_t, compact_reasons_per_heap) @@ -61,6 +61,7 @@ GC_DAC_ARRAY_VAR (size_t, expand_mechanisms_per_heap) GC_DAC_ARRAY_VAR (size_t, interesting_mechanism_bits_per_heap) GC_DAC_VAR (dac_handle_table_map, handle_table_map) GC_DAC_ARRAY_VAR (int, gc_heap_field_offsets) +GC_DAC_ARRAY_VAR (int, generation_field_offsets) #undef GC_DAC_VAR #undef GC_DAC_ARRAY_VAR diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index a310514..cda8e68 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -851,12 +851,6 @@ public: #endif //FREE_USAGE_STATS }; -static_assert(offsetof(dac_generation, allocation_context) == offsetof(generation, allocation_context), "DAC generation offset mismatch"); -static_assert(offsetof(dac_generation, start_segment) == offsetof(generation, start_segment), "DAC generation offset mismatch"); -#ifndef USE_REGIONS -static_assert(offsetof(dac_generation, allocation_start) == offsetof(generation, allocation_start), "DAC generation offset mismatch"); -#endif //!USE_REGIONS - // static data remains the same after it's initialized. // It's per generation. // TODO: for gen_time_tuning, we should put the multipliers in static data. -- 2.7.4