diff --git a/src/coreclr/gc/dac_gcheap_fields.h b/src/coreclr/gc/dac_gcheap_fields.h index 37b6389ff1ea1..39b2dca81008a 100644 --- a/src/coreclr/gc/dac_gcheap_fields.h +++ b/src/coreclr/gc/dac_gcheap_fields.h @@ -1,3 +1,7 @@ +// Whenever we add field here, we need to bear in mind that we have a scenario for a new clrgc +// is used in an old runtime. In that case, the old runtime's DAC will have to interpret the +// fields the way it was. So fields should only be added at the end of the struct. + DEFINE_FIELD (alloc_allocated, uint8_t*) DEFINE_DPTR_FIELD (ephemeral_heap_segment, dac_heap_segment) DEFINE_DPTR_FIELD (finalize_queue, dac_finalize_queue) @@ -16,8 +20,6 @@ DEFINE_FIELD (mark_array, uint32_t*) DEFINE_FIELD (next_sweep_obj, uint8_t*) DEFINE_FIELD (background_saved_lowest_address, uint8_t*) DEFINE_FIELD (background_saved_highest_address, uint8_t*) -DEFINE_DPTR_FIELD (freeable_soh_segment, dac_heap_segment) -DEFINE_DPTR_FIELD (freeable_uoh_segment, dac_heap_segment) #if defined(ALL_FIELDS) || !defined(USE_REGIONS) DEFINE_DPTR_FIELD (saved_sweep_ephemeral_seg, dac_heap_segment) DEFINE_FIELD (saved_sweep_ephemeral_start, uint8_t*) @@ -30,12 +32,23 @@ 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(freeable_soh_segment) -DEFINE_MISSING_FIELD(freeable_uoh_segment) DEFINE_MISSING_FIELD(saved_sweep_ephemeral_seg) DEFINE_MISSING_FIELD(saved_sweep_ephemeral_start) #endif // defined(ALL_FIELDS) || defined(BACKGROUND_GC) +// This field is unused +DEFINE_FIELD(generation_table, void*) + +// Here is where v5.2 fields starts + +#if defined(ALL_FIELDS) || defined(BACKGROUND_GC) +DEFINE_DPTR_FIELD (freeable_soh_segment, dac_heap_segment) +DEFINE_DPTR_FIELD (freeable_uoh_segment, dac_heap_segment) +#else +DEFINE_MISSING_FIELD(freeable_soh_segment) +DEFINE_MISSING_FIELD(freeable_uoh_segment) +#endif // defined(ALL_FIELDS) || defined(BACKGROUND_GC) + #if defined(ALL_FIELDS) || defined(USE_REGIONS) DEFINE_ARRAY_FIELD (free_regions, dac_region_free_list, FREE_REGION_KINDS) #else diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index cd1736ad8c9cc..7b23a592940c8 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -51916,6 +51916,7 @@ bool GCHeap::IsConcurrentGCEnabled() void PopulateDacVars(GcDacVars *gcDacVars) { + bool v2 = gcDacVars->minor_version_number >= 2; #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), @@ -51927,10 +51928,7 @@ void PopulateDacVars(GcDacVars *gcDacVars) #define CLASS_NAME gc_heap #include "dac_gcheap_fields.h" #undef CLASS_NAME - - 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[] = { @@ -51945,7 +51943,6 @@ void PopulateDacVars(GcDacVars *gcDacVars) }; assert(gcDacVars != nullptr); - *gcDacVars = {}; // Note: These version numbers do not need to be checked in the .Net dac/SOS because // we always match the compiled dac and GC to the version used. NativeAOT's SOS may // work differently than .Net SOS. When making breaking changes here you may need to @@ -51984,8 +51981,11 @@ void PopulateDacVars(GcDacVars *gcDacVars) gcDacVars->mark_array = &gc_heap::mark_array; gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address; gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address; - gcDacVars->freeable_soh_segment = reinterpret_cast(&gc_heap::freeable_soh_segment); - gcDacVars->freeable_uoh_segment = reinterpret_cast(&gc_heap::freeable_uoh_segment); + if (v2) + { + gcDacVars->freeable_soh_segment = reinterpret_cast(&gc_heap::freeable_soh_segment); + gcDacVars->freeable_uoh_segment = reinterpret_cast(&gc_heap::freeable_uoh_segment); + } gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; #ifdef USE_REGIONS gcDacVars->saved_sweep_ephemeral_seg = 0; @@ -52026,7 +52026,10 @@ void PopulateDacVars(GcDacVars *gcDacVars) gcDacVars->gc_heap_field_offsets = reinterpret_cast(&gc_heap_field_offsets); #endif // MULTIPLE_HEAPS gcDacVars->generation_field_offsets = reinterpret_cast(&generation_field_offsets); - gcDacVars->bookkeeping_start = &gc_heap::bookkeeping_start; + if (v2) + { + gcDacVars->bookkeeping_start = &gc_heap::bookkeeping_start; + } } int GCHeap::RefreshMemoryLimit() diff --git a/src/coreclr/gc/gcinterface.dac.h b/src/coreclr/gc/gcinterface.dac.h index 3eb66a61a003a..b3be7b09cee60 100644 --- a/src/coreclr/gc/gcinterface.dac.h +++ b/src/coreclr/gc/gcinterface.dac.h @@ -190,22 +190,9 @@ class dac_gc_heap { #undef DEFINE_DPTR_FIELD #undef DEFINE_FIELD #undef ALL_FIELDS - - // The generation table must always be last, because the size of this array - // (stored inline in the gc_heap class) can vary. - // - // The size of the generation class is not part of the GC-DAC interface, - // despite being embedded by-value into the gc_heap class. The DAC variable - // "generation_size" stores the size of the generation class, so the DAC can - // use it and pointer arithmetic to calculate correct offsets into the generation - // table. (See "GenerationTableIndex" function in the DAC for details) - // - // Also note that this array has length 1 because the C++ standard doesn't allow - // for 0-length arrays, although every major compiler is willing to tolerate it. - dac_generation generation_table[1]; }; -#define GENERATION_TABLE_FIELD_INDEX 21 +#define GENERATION_TABLE_FIELD_INDEX 18 // 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. @@ -242,13 +229,7 @@ struct unused_generation // this structure contains __DPtrs for every DAC variable that will marshal values // from the debugee process to the debugger process when dereferenced. struct GcDacVars { - uint8_t major_version_number; - uint8_t minor_version_number; - size_t generation_size; - size_t total_generation_count; - int total_bookkeeping_elements; - int count_free_region_kinds; - size_t card_table_info_size; +#define GC_DAC_VAL(type, name) type name; #ifdef DACCESS_COMPILE #define GC_DAC_VAR(type, name) DPTR(type) name; #define GC_DAC_PTR_VAR(type, name) DPTR(type*) name; @@ -257,6 +238,7 @@ struct GcDacVars { #define GC_DAC_VAR(type, name) type *name; #endif #include "gcinterface.dacvars.def" +#undef GC_DAC_VAL }; #endif // _GC_INTERFACE_DAC_H_ diff --git a/src/coreclr/gc/gcinterface.dacvars.def b/src/coreclr/gc/gcinterface.dacvars.def index f9e0eb4be7e3b..a78b9d720930c 100644 --- a/src/coreclr/gc/gcinterface.dacvars.def +++ b/src/coreclr/gc/gcinterface.dacvars.def @@ -19,6 +19,15 @@ // degraded debugging experience. // Major version mismatches are not tolerated by the DAC and will be rejected upon load. +// Whenever we add field here, we need to bear in mind that we have a scenario for a new clrgc +// is used in an old runtime. In that case, the old runtime's DAC will have to interpret the +// fields the way it was. So fields should only be added at the end of the struct, and their +// loading in PopulateDacVars need to be version checked against a bumped up minor version. + +#ifndef GC_DAC_VAL + #define GC_DAC_VAL(type, name) +#endif // GC_DAC_VAL + #ifndef GC_DAC_VAR #define GC_DAC_VAR(type, name) #endif // GC_DAC_VAR @@ -33,6 +42,10 @@ // This sequence of macros defines the specific variables that are exposed by the // GC to the DAC. +GC_DAC_VAL (uint8_t, major_version_number) +GC_DAC_VAL (uint8_t, minor_version_number) +GC_DAC_VAL (size_t, generation_size) +GC_DAC_VAL (size_t, total_generation_count) GC_DAC_VAR (uint8_t, build_variant) GC_DAC_VAR (bool, built_with_svr) GC_DAC_ARRAY_VAR (size_t, gc_global_mechanisms) @@ -42,8 +55,6 @@ GC_DAC_PTR_VAR (uint32_t, mark_array) GC_DAC_VAR (c_gc_state, current_c_gc_state) GC_DAC_PTR_VAR (dac_heap_segment, ephemeral_heap_segment) GC_DAC_PTR_VAR (dac_heap_segment, saved_sweep_ephemeral_seg) -GC_DAC_PTR_VAR (dac_heap_segment, freeable_soh_segment) -GC_DAC_PTR_VAR (dac_heap_segment, freeable_uoh_segment) GC_DAC_PTR_VAR (uint8_t, saved_sweep_ephemeral_start) GC_DAC_PTR_VAR (uint8_t, background_saved_lowest_address) GC_DAC_PTR_VAR (uint8_t, background_saved_highest_address) @@ -69,6 +80,14 @@ GC_DAC_ARRAY_VAR (dac_region_free_list, global_regions_to_decommit) GC_DAC_PTR_VAR (dac_region_free_list, global_free_huge_regions) GC_DAC_ARRAY_VAR (dac_region_free_list, free_regions) +// Here is where v5.2 fields starts + +GC_DAC_PTR_VAR (dac_heap_segment, freeable_soh_segment) +GC_DAC_PTR_VAR (dac_heap_segment, freeable_uoh_segment) +GC_DAC_VAL (int, total_bookkeeping_elements) +GC_DAC_VAL (int, count_free_region_kinds) +GC_DAC_VAL (size_t, card_table_info_size) + #undef GC_DAC_VAR #undef GC_DAC_ARRAY_VAR #undef GC_DAC_PTR_VAR diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index 5f779a3611d79..666bc45c621a7 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -11,7 +11,7 @@ // The minor version of the IGCHeap interface. Non-breaking changes are required // to bump the minor version number. GCs and EEs with minor version number // mismatches can still interoperate correctly, with some care. -#define GC_INTERFACE_MINOR_VERSION 1 +#define GC_INTERFACE_MINOR_VERSION 2 // The major version of the IGCToCLR interface. Breaking changes to this interface // require bumps in the major version number. diff --git a/src/coreclr/vm/gcheaputilities.cpp b/src/coreclr/vm/gcheaputilities.cpp index ed4e89f4797a4..a705a428c707b 100644 --- a/src/coreclr/vm/gcheaputilities.cpp +++ b/src/coreclr/vm/gcheaputilities.cpp @@ -344,6 +344,8 @@ HRESULT GCHeapUtilities::LoadAndInitialize() g_gc_load_status = GC_LOAD_STATUS_START; LPCWSTR standaloneGcLocation = Configuration::GetKnobStringValue(W("System.GC.Name"), CLRConfig::EXTERNAL_GCName); + g_gc_dac_vars.major_version_number = GC_INTERFACE_MAJOR_VERSION; + g_gc_dac_vars.minor_version_number = GC_INTERFACE_MINOR_VERSION; if (!standaloneGcLocation) { return InitializeDefaultGC();