-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
The structure DacpGcHeapDetails
has many fields:
runtime/src/coreclr/inc/dacprivate.h
Lines 725 to 757 in 1306b03
struct MSLAYOUT DacpGcHeapDetails | |
{ | |
CLRDATA_ADDRESS heapAddr = 0; // Only filled in in server mode, otherwise NULL | |
CLRDATA_ADDRESS alloc_allocated = 0; | |
CLRDATA_ADDRESS mark_array = 0; | |
CLRDATA_ADDRESS current_c_gc_state = 0; | |
CLRDATA_ADDRESS next_sweep_obj = 0; | |
CLRDATA_ADDRESS saved_sweep_ephemeral_seg = 0; | |
CLRDATA_ADDRESS saved_sweep_ephemeral_start = 0; | |
CLRDATA_ADDRESS background_saved_lowest_address = 0; | |
CLRDATA_ADDRESS background_saved_highest_address = 0; | |
DacpGenerationData generation_table [DAC_NUMBERGENERATIONS] = {}; | |
CLRDATA_ADDRESS ephemeral_heap_segment = 0; | |
CLRDATA_ADDRESS finalization_fill_pointers [DAC_NUMBERGENERATIONS + 3] = {}; | |
CLRDATA_ADDRESS lowest_address = 0; | |
CLRDATA_ADDRESS highest_address = 0; | |
CLRDATA_ADDRESS card_table = 0; | |
// Use this for workstation mode (DacpGcHeapDat.bServerMode==FALSE). | |
HRESULT Request(ISOSDacInterface *sos) | |
{ | |
return sos->GetGCHeapStaticData(this); | |
} | |
// Use this for Server mode, as there are multiple heaps, | |
// and you need to pass a heap address in addr. | |
HRESULT Request(ISOSDacInterface *sos, CLRDATA_ADDRESS addr) | |
{ | |
return sos->GetGCHeapDetails(addr, this); | |
} | |
}; |
But the ServerGCHeapDetails
function only loads a subset of them:
runtime/src/coreclr/debug/daccess/request_svr.cpp
Lines 102 to 144 in 1306b03
HRESULT ClrDataAccess::ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, DacpGcHeapDetails *detailsData) | |
{ | |
if (!heapAddr) | |
{ | |
// PREfix. | |
return E_INVALIDARG; | |
} | |
DPTR(dac_gc_heap) pHeap = __DPtr<dac_gc_heap>(TO_TADDR(heapAddr)); | |
int i; | |
//get global information first | |
detailsData->heapAddr = heapAddr; | |
detailsData->lowest_address = PTR_CDADDR(g_lowest_address); | |
detailsData->highest_address = PTR_CDADDR(g_highest_address); | |
detailsData->card_table = PTR_CDADDR(g_card_table); | |
// now get information specific to this heap (server mode gives us several heaps; we're getting | |
// information about only one of them. | |
detailsData->alloc_allocated = (CLRDATA_ADDRESS)pHeap->alloc_allocated; | |
detailsData->ephemeral_heap_segment = (CLRDATA_ADDRESS)dac_cast<TADDR>(pHeap->ephemeral_heap_segment); | |
// get bounds for the different generations | |
for (i=0; i<NUMBERGENERATIONS; i++) | |
{ | |
DPTR(dac_generation) generation = ServerGenerationTableIndex(pHeap, i); | |
detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS)dac_cast<TADDR>(generation->start_segment); | |
detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR)generation->allocation_start; | |
DPTR(gc_alloc_context) alloc_context = dac_cast<TADDR>(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; | |
} | |
DPTR(dac_finalize_queue) fq = pHeap->finalize_queue; | |
DPTR(uint8_t*) pFillPointerArray= dac_cast<TADDR>(fq) + offsetof(dac_finalize_queue, m_FillPointers); | |
for(i=0; i<(NUMBERGENERATIONS+dac_finalize_queue::ExtraSegCount); i++) | |
{ | |
detailsData->finalization_fill_pointers[i] = (CLRDATA_ADDRESS) pFillPointerArray[i]; | |
} | |
return S_OK; | |
} |
Comparing with the workstation GC, so code is also questionable - for example - do we need to check of the validity of the finalization queue?
runtime/src/coreclr/debug/daccess/request.cpp
Lines 2772 to 2828 in 72a26f9
HRESULT | |
ClrDataAccess::GetGCHeapStaticData(struct DacpGcHeapDetails *detailsData) | |
{ | |
if (detailsData == NULL) | |
return E_INVALIDARG; | |
SOSDacEnter(); | |
detailsData->lowest_address = PTR_CDADDR(g_lowest_address); | |
detailsData->highest_address = PTR_CDADDR(g_highest_address); | |
detailsData->card_table = PTR_CDADDR(g_card_table); | |
detailsData->heapAddr = NULL; | |
detailsData->alloc_allocated = (CLRDATA_ADDRESS)*g_gcDacGlobals->alloc_allocated; | |
detailsData->ephemeral_heap_segment = (CLRDATA_ADDRESS)*g_gcDacGlobals->ephemeral_heap_segment; | |
detailsData->mark_array = (CLRDATA_ADDRESS)*g_gcDacGlobals->mark_array; | |
detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state; | |
detailsData->next_sweep_obj = (CLRDATA_ADDRESS)*g_gcDacGlobals->next_sweep_obj; | |
if (g_gcDacGlobals->saved_sweep_ephemeral_seg != nullptr) | |
{ | |
detailsData->saved_sweep_ephemeral_seg = (CLRDATA_ADDRESS)*g_gcDacGlobals->saved_sweep_ephemeral_seg; | |
detailsData->saved_sweep_ephemeral_start = (CLRDATA_ADDRESS)*g_gcDacGlobals->saved_sweep_ephemeral_start; | |
} | |
else | |
{ | |
// with regions, we don't have these variables anymore | |
// use special value -1 in saved_sweep_ephemeral_seg to signal the region case | |
detailsData->saved_sweep_ephemeral_seg = (CLRDATA_ADDRESS)-1; | |
detailsData->saved_sweep_ephemeral_start = 0; | |
} | |
detailsData->background_saved_lowest_address = (CLRDATA_ADDRESS)*g_gcDacGlobals->background_saved_lowest_address; | |
detailsData->background_saved_highest_address = (CLRDATA_ADDRESS)*g_gcDacGlobals->background_saved_highest_address; | |
for (unsigned int i=0; i < *g_gcDacGlobals->max_gen + 2; i++) | |
{ | |
DPTR(dac_generation) generation = GenerationTableIndex(g_gcDacGlobals->generation_table, i); | |
detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS) dac_cast<TADDR>(generation->start_segment); | |
detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS) generation->allocation_start; | |
DPTR(gc_alloc_context) alloc_context = dac_cast<TADDR>(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; | |
} | |
if (g_gcDacGlobals->finalize_queue.IsValid()) | |
{ | |
DPTR(dac_finalize_queue) fq = Dereference(g_gcDacGlobals->finalize_queue); | |
DPTR(uint8_t*) fillPointersTable = dac_cast<TADDR>(fq) + offsetof(dac_finalize_queue, m_FillPointers); | |
for (unsigned int i = 0; i<(*g_gcDacGlobals->max_gen + 2 + dac_finalize_queue::ExtraSegCount); i++) | |
{ | |
detailsData->finalization_fill_pointers[i] = (CLRDATA_ADDRESS)*TableIndex(fillPointersTable, i, sizeof(uint8_t*)); | |
} | |
} | |
SOSDacLeave(); | |
return hr; | |
} |
In particular, the fact that saved_sweep_ephemeral_seg
is not loaded during ServerGCHeapDetails
is leading SOS to believe a region build of the GC to be a segment build, leading to subsequent errors.
Therefore saved_sweep_ephemeral_seg
has to be fixed. The other fields are less clear. As far as I understand the SOS code, it looks like those fields are also accessed in the server GC case as well, so we should fix those as well.
@dotnet/gc for awareness