diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 3a0ceb80e6057f..88003b88935009 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7654,7 +7654,6 @@ void DacHandleWalker::WalkHandles() { if (mask & 1) { - dac_handle_table *pTable = hTable; PTR_AppDomain pDomain = AppDomain::GetCurrentDomain(); param.AppDomain = TO_CDADDR(pDomain.GetAddr()); param.Type = handleType; @@ -8285,3 +8284,234 @@ HRESULT DacStackReferenceErrorEnum::Next(unsigned int count, SOSStackRefError re *pFetched = i; return i < count ? S_FALSE : S_OK; } + + +HRESULT DacMemoryEnumerator::Skip(unsigned int count) +{ + mIteratorIndex += count; + return S_OK; +} + +HRESULT DacMemoryEnumerator::Reset() +{ + mIteratorIndex = 0; + return S_OK; +} + +HRESULT DacMemoryEnumerator::GetCount(unsigned int* pCount) +{ + if (!pCount) + return E_POINTER; + + mRegions.GetCount(); + return S_OK; +} + +HRESULT DacMemoryEnumerator::Next(unsigned int count, SOSMemoryRegion regions[], unsigned int* pFetched) +{ + if (!pFetched) + return E_POINTER; + + if (!regions) + return E_POINTER; + + unsigned int i = 0; + while (i < count && mIteratorIndex < mRegions.GetCount()) + { + regions[i++] = mRegions.Get(mIteratorIndex++); + } + + *pFetched = i; + return i < count ? S_FALSE : S_OK; +} + + +HRESULT DacGCBookkeepingEnumerator::Init() +{ + if (g_gcDacGlobals->bookkeeping_start == nullptr) + return E_FAIL; + + TADDR ctiAddr = TO_TADDR(*g_gcDacGlobals->bookkeeping_start); + if (ctiAddr == 0) + return E_FAIL; + + DPTR(dac_card_table_info) card_table_info(ctiAddr); + + SOSMemoryRegion mem = {0}; + if (card_table_info->recount && card_table_info->size) + { + mem.Start = card_table_info.GetAddr(); + mem.Size = card_table_info->size; + mRegions.Add(mem); + } + + size_t card_table_info_size = g_gcDacGlobals->card_table_info_size; + TADDR next = card_table_info->next_card_table; + + // Cap the number of regions we will walk in case we have run into some kind of + // memory corruption. We shouldn't have more than a few linked card tables anyway. + int maxRegions = 32; + + // This loop is effectively "while (next != 0)" but with an added check to make + // sure we don't underflow next when subtracting card_table_info_size if we encounter + // a bad pointer. + while (next > card_table_info_size) + { + DPTR(dac_card_table_info) ct(next - card_table_info_size); + + if (ct->recount && ct->size) + { + mem = {0}; + mem.Start = ct.GetAddr(); + mem.Size = ct->size; + mRegions.Add(mem); + } + + next = ct->next_card_table; + if (next == card_table_info->next_card_table) + break; + + if (--maxRegions <= 0) + break; + } + + return S_OK; +} + + +HRESULT DacHandleTableMemoryEnumerator::Init() +{ + int max_slots = 1; + +#ifdef FEATURE_SVR_GC + if (GCHeapUtilities::IsServerHeap()) + max_slots = GCHeapCount(); +#endif // FEATURE_SVR_GC + + // Cap the number of regions we will walk in case we hit an infinite loop due + // to memory corruption + int maxRegions = 8192; + + for (dac_handle_table_map *map = g_gcDacGlobals->handle_table_map; map && maxRegions >= 0; map = map->pNext, maxRegions--) + { + for (int i = 0; i < INITIAL_HANDLE_TABLE_ARRAY_SIZE; ++i) + { + if (map->pBuckets[i] != NULL) + { + for (int j = 0; j < max_slots ; ++j) + { + DPTR(dac_handle_table) pTable = map->pBuckets[i]->pTable[j]; + DPTR(dac_handle_table_segment) pFirstSegment = pTable->pSegmentList; + DPTR(dac_handle_table_segment) curr = pFirstSegment; + + do + { + SOSMemoryRegion mem = {0}; + mem.Start = curr.GetAddr(); + mem.Size = HANDLE_SEGMENT_SIZE; + mem.Heap = j; // heap number + + mRegions.Add(mem); + + curr = curr->pNextSegment; + } while (curr != nullptr && curr != pFirstSegment); + } + } + } + } + + return S_OK; +} + +void DacFreeRegionEnumerator::AddSingleSegment(const dac_heap_segment &curr, FreeRegionKind kind, int heap) +{ + SOSMemoryRegion mem = {0}; + mem.Start = TO_CDADDR(curr.mem); + mem.ExtraData = (CLRDATA_ADDRESS)kind; + mem.Heap = heap; + + if (curr.mem < curr.committed) + mem.Size = TO_CDADDR(curr.committed) - mem.Start; + + if (mem.Start) + mRegions.Add(mem); +} + +void DacFreeRegionEnumerator::AddSegmentList(DPTR(dac_heap_segment) start, FreeRegionKind kind, int heap) +{ + int iterationMax = 2048; + + DPTR(dac_heap_segment) curr = start; + while (curr != nullptr) + { + AddSingleSegment(*curr, kind, heap); + + curr = curr->next; + if (curr == start) + break; + + if (iterationMax-- <= 0) + break; + } +} + +void DacFreeRegionEnumerator::AddFreeList(DPTR(dac_region_free_list) free_list, FreeRegionKind kind) +{ + if (free_list != nullptr) + { + AddSegmentList(free_list->head_free_region, kind); + } +} + +HRESULT DacFreeRegionEnumerator::Init() +{ + // Cap the number of free regions we will walk at a sensible number. This is to protect against + // memory corruption, un-initialized data, or just a bug. + int count_free_region_kinds = g_gcDacGlobals->count_free_region_kinds; + count_free_region_kinds = min(count_free_region_kinds, 16); + + unsigned int index = 0; + if (g_gcDacGlobals->global_free_huge_regions != nullptr) + { + DPTR(dac_region_free_list) global_free_huge_regions(g_gcDacGlobals->global_free_huge_regions); + AddFreeList(global_free_huge_regions, FreeRegionKind::FreeGlobalHugeRegion); + } + + if (g_gcDacGlobals->global_regions_to_decommit != nullptr) + { + DPTR(dac_region_free_list) regionList(g_gcDacGlobals->global_regions_to_decommit); + if (regionList != nullptr) + for (int i = 0; i < count_free_region_kinds; i++, regionList++) + AddFreeList(regionList, FreeRegionKind::FreeGlobalRegion); + } + +#if defined(FEATURE_SVR_GC) + if (GCHeapUtilities::IsServerHeap()) + { + AddServerRegions(); + } + else +#endif //FEATURE_SVR_GC + { + DPTR(dac_region_free_list) regionList(g_gcDacGlobals->free_regions); + if (regionList != nullptr) + for (int i = 0; i < count_free_region_kinds; i++, regionList++) + AddFreeList(regionList, FreeRegionKind::FreeRegion); + + if (g_gcDacGlobals->freeable_soh_segment != nullptr) + { + DPTR(DPTR(dac_heap_segment)) freeable_soh_segment_ptr(g_gcDacGlobals->freeable_soh_segment); + if (freeable_soh_segment_ptr != nullptr) + AddSegmentList(*freeable_soh_segment_ptr, FreeRegionKind::FreeSohSegment); + } + + if (g_gcDacGlobals->freeable_uoh_segment != nullptr) + { + DPTR(DPTR(dac_heap_segment)) freeable_uoh_segment_ptr(g_gcDacGlobals->freeable_uoh_segment); + if (freeable_uoh_segment_ptr != nullptr) + AddSegmentList(*freeable_uoh_segment_ptr, FreeRegionKind::FreeUohSegment); + } + } + + return S_OK; +} diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 52ef9cf5979d9c..ddf61370f416e9 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1211,6 +1211,10 @@ class ClrDataAccess virtual HRESULT STDMETHODCALLTYPE GetDomainLoaderAllocator(CLRDATA_ADDRESS domainAddress, CLRDATA_ADDRESS *pLoaderAllocator); virtual HRESULT STDMETHODCALLTYPE GetLoaderAllocatorHeapNames(int count, const char **ppNames, int *pNeeded); virtual HRESULT STDMETHODCALLTYPE GetLoaderAllocatorHeaps(CLRDATA_ADDRESS loaderAllocator, int count, CLRDATA_ADDRESS *pLoaderHeaps, LoaderHeapKind *pKinds, int *pNeeded); + virtual HRESULT STDMETHODCALLTYPE GetHandleTableMemoryRegions(ISOSMemoryEnum **ppEnum); + virtual HRESULT STDMETHODCALLTYPE GetGCBookkeepingMemoryRegions(ISOSMemoryEnum **ppEnum); + virtual HRESULT STDMETHODCALLTYPE GetGCFreeRegions(ISOSMemoryEnum **ppEnum); + virtual HRESULT STDMETHODCALLTYPE LockedFlush(); // // ClrDataAccess. @@ -1954,6 +1958,56 @@ class DacReferenceList unsigned int _capacity; }; + +class DacMemoryEnumerator : public DefaultCOMImpl +{ +public: + DacMemoryEnumerator() + : mIteratorIndex(0) + { + } + + virtual ~DacMemoryEnumerator() {} + virtual HRESULT Init() = 0; + + HRESULT STDMETHODCALLTYPE Skip(unsigned int count); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE GetCount(unsigned int *pCount); + HRESULT STDMETHODCALLTYPE Next(unsigned int count, + SOSMemoryRegion regions[], + unsigned int *pFetched); + +protected: + DacReferenceList mRegions; + +private: + unsigned int mIteratorIndex; +}; + +class DacHandleTableMemoryEnumerator : public DacMemoryEnumerator +{ +public: + virtual HRESULT Init(); +}; + +class DacGCBookkeepingEnumerator : public DacMemoryEnumerator +{ +public: + virtual HRESULT Init(); +}; + +class DacFreeRegionEnumerator : public DacMemoryEnumerator +{ +public: + virtual HRESULT Init(); + +private: + void AddSingleSegment(const dac_heap_segment &seg, FreeRegionKind kind, int heap); + void AddSegmentList(DPTR(dac_heap_segment) seg, FreeRegionKind kind, int heap = 0); + void AddFreeList(DPTR(dac_region_free_list) freeList, FreeRegionKind kind); + void AddServerRegions(); +}; + struct DacGcReference; /* DacStackReferenceWalker. */ diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index b5a0aa1986bd21..9ccb7d6272442a 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -5295,3 +5295,95 @@ HRESULT ClrDataAccess::GetGlobalAllocationContext( SOSDacLeave(); return hr; } + +HRESULT ClrDataAccess::GetHandleTableMemoryRegions(ISOSMemoryEnum** ppEnum) +{ + if (!ppEnum) + return E_POINTER; + + SOSDacEnter(); + + DacHandleTableMemoryEnumerator* htEnum = new (nothrow) DacHandleTableMemoryEnumerator(); + if (htEnum) + { + hr = htEnum->Init(); + + if (SUCCEEDED(hr)) + hr = htEnum->QueryInterface(__uuidof(ISOSMemoryEnum), (void**)ppEnum); + + if (FAILED(hr)) + delete htEnum; + } + else + { + hr = E_OUTOFMEMORY; + } + + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::GetGCBookkeepingMemoryRegions(ISOSMemoryEnum** ppEnum) +{ + if (!ppEnum) + return E_POINTER; + + SOSDacEnter(); + + DacGCBookkeepingEnumerator* bkEnum = new (nothrow) DacGCBookkeepingEnumerator(); + if (bkEnum) + { + hr = bkEnum->Init(); + + if (SUCCEEDED(hr)) + hr = bkEnum->QueryInterface(__uuidof(ISOSMemoryEnum), (void**)ppEnum); + + if (FAILED(hr)) + delete bkEnum; + } + else + { + hr = E_OUTOFMEMORY; + } + + SOSDacLeave(); + return hr; +} + + +HRESULT ClrDataAccess::GetGCFreeRegions(ISOSMemoryEnum **ppEnum) +{ + if (!ppEnum) + return E_POINTER; + + SOSDacEnter(); + + DacFreeRegionEnumerator* frEnum = new (nothrow) DacFreeRegionEnumerator(); + if (frEnum) + { + hr = frEnum->Init(); + + if (SUCCEEDED(hr)) + hr = frEnum->QueryInterface(__uuidof(ISOSMemoryEnum), (void**)ppEnum); + + if (FAILED(hr)) + delete frEnum; + } + else + { + hr = E_OUTOFMEMORY; + } + + SOSDacLeave(); + return hr; +} + +HRESULT ClrDataAccess::LockedFlush() +{ + SOSDacEnter(); + + Flush(); + + SOSDacLeave(); + return hr; +} diff --git a/src/coreclr/debug/daccess/request_svr.cpp b/src/coreclr/debug/daccess/request_svr.cpp index e59255a4e5a68f..3955d8f05db5c1 100644 --- a/src/coreclr/debug/daccess/request_svr.cpp +++ b/src/coreclr/debug/daccess/request_svr.cpp @@ -459,4 +459,26 @@ HRESULT DacHeapWalker::InitHeapDataSvr(HeapData *&pHeaps, size_t &pCount) return S_OK; } +void DacFreeRegionEnumerator::AddServerRegions() +{ + // Cap the number of free regions we will walk at a sensible number. This is to protect against + // memory corruption, un-initialized data, or just a bug. + int count_free_region_kinds = g_gcDacGlobals->count_free_region_kinds; + count_free_region_kinds = min(count_free_region_kinds, 16); + + for (int i = 0; i < GCHeapCount(); i++) + { + TADDR heapAddress = (TADDR)HeapTableIndex(g_gcDacGlobals->g_heaps, i); + if (heapAddress == 0) + continue; + + dac_gc_heap heap = LoadGcHeapData(heapAddress); + for (int i = 0; i < count_free_region_kinds; i++) + AddSegmentList(heap.free_regions[i].head_free_region, FreeRegionKind::FreeRegion, i); + + AddSegmentList(heap.freeable_soh_segment, FreeRegionKind::FreeSohSegment, i); + AddSegmentList(heap.freeable_uoh_segment, FreeRegionKind::FreeUohSegment, i); + } +} + #endif // defined(FEATURE_SVR_GC) diff --git a/src/coreclr/gc/dac_gcheap_fields.h b/src/coreclr/gc/dac_gcheap_fields.h index 104157261635a8..37b6389ff1ea18 100644 --- a/src/coreclr/gc/dac_gcheap_fields.h +++ b/src/coreclr/gc/dac_gcheap_fields.h @@ -10,23 +10,34 @@ DEFINE_FIELD (internal_root_array, uint8_t*) DEFINE_FIELD (internal_root_array_index, size_t) DEFINE_FIELD (heap_analyze_success, BOOL) DEFINE_FIELD (card_table, uint32_t*) + #if defined(ALL_FIELDS) || defined(BACKGROUND_GC) 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*) #else DEFINE_MISSING_FIELD(saved_sweep_ephemeral_seg) DEFINE_MISSING_FIELD(saved_sweep_ephemeral_start) -#endif +#endif // defined(ALL_FIELDS) || !defined(USE_REGIONS) #else 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 +#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 +DEFINE_MISSING_FIELD(free_regions) +#endif // ALL_FIELDS diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 474e041974cf9b..358f803887e35f 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -2228,8 +2228,8 @@ size_t gc_heap::g_bpromoted; #endif //MULTIPLE_HEAPS size_t gc_heap::card_table_element_layout[total_bookkeeping_elements + 1]; +uint8_t* gc_heap::bookkeeping_start = nullptr; #ifdef USE_REGIONS -uint8_t* gc_heap::bookkeeping_covered_start = nullptr; uint8_t* gc_heap::bookkeeping_covered_committed = nullptr; size_t gc_heap::bookkeeping_sizes[total_bookkeeping_elements]; #endif //USE_REGIONS @@ -8378,6 +8378,9 @@ class card_table_info { public: unsigned recount; + size_t size; + uint32_t* next_card_table; + uint8_t* lowest_address; uint8_t* highest_address; short* brick_table; @@ -8391,11 +8394,11 @@ class card_table_info #ifdef BACKGROUND_GC uint32_t* mark_array; #endif //BACKGROUND_GC - - size_t size; - uint32_t* next_card_table; }; +static_assert(offsetof(dac_card_table_info, size) == offsetof(card_table_info, size), "DAC card_table_info layout mismatch"); +static_assert(offsetof(dac_card_table_info, next_card_table) == offsetof(card_table_info, next_card_table), "DAC card_table_info layout mismatch"); + //These are accessors on untranslated cardtable inline unsigned& card_table_refcount (uint32_t* c_table) @@ -8623,6 +8626,9 @@ void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end) inline uint32_t*& card_table_next (uint32_t* c_table) { + // NOTE: The dac takes a dependency on card_table_info being right before c_table. + // It's 100% ok to change this implementation detail as long as a matching change + // is made to DacGCBookkeepingEnumerator::Init in daccess.cpp. return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table; } @@ -8907,21 +8913,21 @@ bool gc_heap::inplace_commit_card_table (uint8_t* from, uint8_t* to) uint8_t* commit_end = nullptr; if (initial_commit) { - required_begin = bookkeeping_covered_start + ((i == card_table_element) ? 0 : card_table_element_layout[i]); - required_end = bookkeeping_covered_start + card_table_element_layout[i] + new_sizes[i]; + required_begin = bookkeeping_start + ((i == card_table_element) ? 0 : card_table_element_layout[i]); + required_end = bookkeeping_start + card_table_element_layout[i] + new_sizes[i]; commit_begin = align_lower_page(required_begin); } else { assert (additional_commit); - required_begin = bookkeeping_covered_start + card_table_element_layout[i] + bookkeeping_sizes[i]; + required_begin = bookkeeping_start + card_table_element_layout[i] + bookkeeping_sizes[i]; required_end = required_begin + new_sizes[i] - bookkeeping_sizes[i]; commit_begin = align_on_page(required_begin); } assert (required_begin <= required_end); commit_end = align_on_page(required_end); - commit_end = min (commit_end, align_lower_page(bookkeeping_covered_start + card_table_element_layout[i + 1])); + commit_end = min (commit_end, align_lower_page(bookkeeping_start + card_table_element_layout[i + 1])); commit_begin = min (commit_begin, commit_end); assert (commit_begin <= commit_end); @@ -8996,9 +9002,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) size_t alloc_size = card_table_element_layout[total_bookkeeping_elements]; uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags); -#ifdef USE_REGIONS - bookkeeping_covered_start = mem; -#endif //USE_REGIONS + bookkeeping_start = mem; if (!mem) return 0; @@ -9495,6 +9499,8 @@ void gc_heap::copy_brick_card_table() uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; own_card_table (ct); card_table = translate_card_table (ct); + bookkeeping_start = (uint8_t*)ct - sizeof(card_table_info); + card_table_size(ct) = card_table_element_layout[total_bookkeeping_elements]; /* End of global lock */ highest_address = card_table_highest_address (ct); lowest_address = card_table_lowest_address (ct); @@ -13728,8 +13734,6 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, &g_gc_lowest_address, &g_gc_highest_address)) return E_OUTOFMEMORY; - bookkeeping_covered_start = global_region_allocator.get_start(); - if (!allocate_initial_regions(number_of_heaps)) return E_OUTOFMEMORY; } @@ -49291,13 +49295,20 @@ void PopulateDacVars(GcDacVars *gcDacVars) assert(gcDacVars != nullptr); *gcDacVars = {}; - // Note: these version numbers are not actually checked by SOS, so if you change - // the GC in a way that makes it incompatible with SOS, please change - // SOS_BREAKING_CHANGE_VERSION in both the runtime and the diagnostics repo - gcDacVars->major_version_number = 1; + // 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 + // find NativeAOT's equivalent of SOS_BREAKING_CHANGE_VERSION and increment it. + gcDacVars->major_version_number = 2; gcDacVars->minor_version_number = 0; + gcDacVars->total_bookkeeping_elements = total_bookkeeping_elements; + gcDacVars->card_table_info_size = sizeof(card_table_info); + #ifdef USE_REGIONS gcDacVars->minor_version_number |= 1; + gcDacVars->count_free_region_kinds = count_free_region_kinds; + gcDacVars->global_regions_to_decommit = reinterpret_cast(&gc_heap::global_regions_to_decommit); + gcDacVars->global_free_huge_regions = reinterpret_cast(&gc_heap::global_free_huge_regions); #endif //USE_REGIONS #ifndef BACKGROUND_GC gcDacVars->minor_version_number |= 2; @@ -49315,10 +49326,15 @@ void PopulateDacVars(GcDacVars *gcDacVars) #endif //BACKGROUND_GC #ifndef MULTIPLE_HEAPS gcDacVars->ephemeral_heap_segment = reinterpret_cast(&gc_heap::ephemeral_heap_segment); +#ifdef USE_REGIONS + gcDacVars->free_regions = reinterpret_cast(&gc_heap::free_regions); +#endif #ifdef BACKGROUND_GC 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); gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; #ifdef USE_REGIONS gcDacVars->saved_sweep_ephemeral_seg = 0; @@ -49331,6 +49347,8 @@ void PopulateDacVars(GcDacVars *gcDacVars) gcDacVars->mark_array = 0; gcDacVars->background_saved_lowest_address = 0; gcDacVars->background_saved_highest_address = 0; + gcDacVars->freeable_soh_segment = 0; + gcDacVars->freeable_uoh_segment = 0; gcDacVars->next_sweep_obj = 0; gcDacVars->saved_sweep_ephemeral_seg = 0; gcDacVars->saved_sweep_ephemeral_start = 0; @@ -49357,4 +49375,5 @@ 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; } diff --git a/src/coreclr/gc/gcinterface.dac.h b/src/coreclr/gc/gcinterface.dac.h index cfe968b74624bc..3eb66a61a003ad 100644 --- a/src/coreclr/gc/gcinterface.dac.h +++ b/src/coreclr/gc/gcinterface.dac.h @@ -16,16 +16,17 @@ #define MAX_EXPAND_MECHANISMS_COUNT 6 #define MAX_GC_MECHANISM_BITS_COUNT 2 #define MAX_GLOBAL_GC_MECHANISMS_COUNT 6 +#define FREE_REGION_KINDS 3 // The number of generations is hardcoded in to the dac APIS (DacpGcHeapDetails hard codes the size of its arrays) // The number of generations is hardcoded into some older dac APIS (for example DacpGcHeapDetails hard codes the size of its arrays) // This value cannot change and should not be used in new DAC APIs. New APIs can query GcDacVars.total_generation_count // variable which is dynamically initialized at runtime - #define NUMBERGENERATIONS 4 -#define INITIAL_HANDLE_TABLE_ARRAY_SIZE 10 -#define HANDLE_MAX_INTERNAL_TYPES 12 + + +#include "handletableconstants.h" // Analogue for the GC heap_segment class, containing information regarding a single // heap segment. @@ -42,6 +43,17 @@ class dac_heap_segment { class dac_gc_heap* heap; }; +class dac_region_free_list { +public: + size_t num_free_regions; + size_t size_free_regions; + size_t size_committed_in_free_regions; + size_t num_free_regions_added; + size_t num_free_regions_removed; + DPTR(dac_heap_segment) head_free_region; + DPTR(dac_heap_segment) tail_free_region; +}; + // Analogue for the GC generation class, containing information about the start segment // of a generation and its allocation context. class dac_generation { @@ -49,12 +61,14 @@ class dac_generation { #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; +#define DEFINE_MISSING_FIELD(field_name) #include "dac_generation_fields.h" #undef DEFINE_DPTR_FIELD #undef DEFINE_FIELD #undef ALL_FIELDS +#undef DEFINE_MISSING_FIELD }; // Analogue for the GC CFinalize class, containing information about the finalize queue. @@ -64,6 +78,21 @@ class dac_finalize_queue { uint8_t** m_FillPointers[NUMBERGENERATIONS + ExtraSegCount]; }; +class dac_handle_table_segment { +public: + uint8_t rgGeneration[HANDLE_BLOCKS_PER_SEGMENT * sizeof(uint32_t) / sizeof(uint8_t)]; + uint8_t rgAllocation[HANDLE_BLOCKS_PER_SEGMENT]; + uint32_t rgFreeMask[HANDLE_MASKS_PER_SEGMENT]; + uint8_t rgBlockType[HANDLE_BLOCKS_PER_SEGMENT]; + uint8_t rgUserData[HANDLE_BLOCKS_PER_SEGMENT]; + uint8_t rgLocks[HANDLE_BLOCKS_PER_SEGMENT]; + uint8_t rgTail[HANDLE_MAX_INTERNAL_TYPES]; + uint8_t rgHint[HANDLE_MAX_INTERNAL_TYPES]; + uint32_t rgFreeCount[HANDLE_MAX_INTERNAL_TYPES]; + DPTR(dac_handle_table_segment) pNextSegment; + }; + + class dac_handle_table { public: // We do try to keep everything that the DAC knows about as close to the @@ -71,6 +100,7 @@ class dac_handle_table { // HandleTable has rgTypeFlags at offset 0 for performance reasons and // we don't want to disrupt that. uint32_t padding[HANDLE_MAX_INTERNAL_TYPES]; + DPTR(dac_handle_table_segment) pSegmentList; }; class dac_handle_table_bucket { @@ -86,6 +116,13 @@ class dac_handle_table_map { uint32_t dwMaxIndex; }; +class dac_card_table_info { +public: + unsigned recount; + size_t size; + TADDR next_card_table; +}; + // Possible values of the current_c_gc_state dacvar, indicating the state of // a background GC. enum c_gc_state @@ -111,7 +148,7 @@ enum oom_reason /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ /* If you modify failure_get_memory and */ /* oom_reason be sure to make the corresponding */ -/* changes in tools\sos\strike\strike.cpp. */ +/* changes in ClrMD. */ /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ enum failure_get_memory { @@ -168,7 +205,7 @@ class dac_gc_heap { dac_generation generation_table[1]; }; -#define GENERATION_TABLE_FIELD_INDEX 18 +#define GENERATION_TABLE_FIELD_INDEX 21 // 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. @@ -209,6 +246,9 @@ struct GcDacVars { 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; #ifdef DACCESS_COMPILE #define GC_DAC_VAR(type, name) DPTR(type) name; #define GC_DAC_PTR_VAR(type, name) DPTR(type*) name; diff --git a/src/coreclr/gc/gcinterface.dacvars.def b/src/coreclr/gc/gcinterface.dacvars.def index a78852a09a220d..f9e0eb4be7e3b8 100644 --- a/src/coreclr/gc/gcinterface.dacvars.def +++ b/src/coreclr/gc/gcinterface.dacvars.def @@ -42,6 +42,8 @@ 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) @@ -62,6 +64,10 @@ 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) +GC_DAC_PTR_VAR (uint8_t, bookkeeping_start) +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) #undef GC_DAC_VAR #undef GC_DAC_ARRAY_VAR diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index 3ca72125148d22..d72c9495dd8eab 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -1343,6 +1343,8 @@ enum free_region_kind count_free_region_kinds, }; +static_assert(count_free_region_kinds == FREE_REGION_KINDS, "Keep count_free_region_kinds in sync with FREE_REGION_KINDS, changing this is not a version breaking change."); + class region_free_list { size_t num_free_regions; @@ -1379,6 +1381,8 @@ class region_free_list void sort_by_committed_and_age(); static bool is_on_free_list (heap_segment* region, region_free_list free_list[count_free_region_kinds]); }; + +static_assert(sizeof(region_free_list) == sizeof(dac_region_free_list), "The DAC relies on the size of these two types matching for pointer arithmetic."); #endif enum bookkeeping_element @@ -4192,10 +4196,10 @@ class gc_heap #endif //BGC_SERVO_TUNING #endif //BACKGROUND_GC + PER_HEAP_ISOLATED_FIELD_INIT_ONLY uint8_t* bookkeeping_start; #ifdef USE_REGIONS PER_HEAP_ISOLATED_FIELD_INIT_ONLY size_t regions_range; PER_HEAP_ISOLATED_FIELD_INIT_ONLY bool enable_special_regions_p; - PER_HEAP_ISOLATED_FIELD_INIT_ONLY uint8_t* bookkeeping_covered_start; #else //USE_REGIONS PER_HEAP_ISOLATED_FIELD_INIT_ONLY size_t eph_gen_starts_size; PER_HEAP_ISOLATED_FIELD_INIT_ONLY size_t min_segment_size; diff --git a/src/coreclr/gc/handletableconstants.h b/src/coreclr/gc/handletableconstants.h new file mode 100644 index 00000000000000..0d6e678f2aebc0 --- /dev/null +++ b/src/coreclr/gc/handletableconstants.h @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#ifndef __HANDLETABLECONSTANTS_H__ +#define __HANDLETABLECONSTANTS_H__ + + // Build support for async pinned handles into standalone GC to make it usable with older runtimes +#if defined(BUILD_AS_STANDALONE) && !defined(FEATURE_NATIVEAOT) +#define FEATURE_ASYNC_PINNED_HANDLES +#endif + +#define INITIAL_HANDLE_TABLE_ARRAY_SIZE 10 +#define HANDLE_MAX_INTERNAL_TYPES 12 + +/*--------------------------------------------------------------------------*/ + +//@TODO: find a home for this in a project-level header file +#ifndef BITS_PER_BYTE +#define BITS_PER_BYTE (8) +#endif +/*--------------------------------------------------------------------------*/ + + + +/**************************************************************************** + * + * MAJOR TABLE DEFINITIONS THAT CHANGE DEPENDING ON THE WEATHER + * + ****************************************************************************/ + +// 64k reserved per segment with 4k as header. +#define HANDLE_SEGMENT_SIZE (0x10000) // MUST be a power of 2 (and currently must be 64K due to VirtualAlloc semantics) +#define HANDLE_HEADER_SIZE (0x1000) // SHOULD be <= OS page size + +#define HANDLE_SEGMENT_ALIGNMENT HANDLE_SEGMENT_SIZE + + +#if !BIGENDIAN + + // little-endian write barrier mask manipulation + #define GEN_CLUMP_0_MASK (0x000000FF) + #define NEXT_CLUMP_IN_MASK(dw) ((dw) >> BITS_PER_BYTE) + +#else + + // big-endian write barrier mask manipulation + #define GEN_CLUMP_0_MASK (0xFF000000) + #define NEXT_CLUMP_IN_MASK(dw) ((dw) << BITS_PER_BYTE) + +#endif + + +// if the above numbers change than these will likely change as well +#define HANDLE_HANDLES_PER_CLUMP (16) // segment write-barrier granularity +#define HANDLE_HANDLES_PER_BLOCK (64) // segment suballocation granularity +#define HANDLE_OPTIMIZE_FOR_64_HANDLE_BLOCKS // flag for certain optimizations + +// number of types allowed for public callers +#define HANDLE_MAX_PUBLIC_TYPES (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve one internal type + +// internal block types +#define HNDTYPE_INTERNAL_DATABLOCK (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve last type for data blocks + +// max number of generations to support statistics on +#define MAXSTATGEN (5) + +/*--------------------------------------------------------------------------*/ + + + +/**************************************************************************** + * + * MORE DEFINITIONS + * + ****************************************************************************/ + +// fast handle-to-segment mapping +#define HANDLE_SEGMENT_CONTENT_MASK (HANDLE_SEGMENT_SIZE - 1) +#define HANDLE_SEGMENT_ALIGN_MASK (~HANDLE_SEGMENT_CONTENT_MASK) + +// table layout metrics +#define HANDLE_SIZE sizeof(_UNCHECKED_OBJECTREF) +#define HANDLE_HANDLES_PER_SEGMENT ((HANDLE_SEGMENT_SIZE - HANDLE_HEADER_SIZE) / HANDLE_SIZE) +#define HANDLE_BLOCKS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_BLOCK) +#define HANDLE_CLUMPS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_CLUMP) +#define HANDLE_CLUMPS_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK / HANDLE_HANDLES_PER_CLUMP) +#define HANDLE_BYTES_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK * HANDLE_SIZE) +#define HANDLE_HANDLES_PER_MASK (sizeof(uint32_t) * BITS_PER_BYTE) +#define HANDLE_MASKS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_MASK) +#define HANDLE_MASKS_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK / HANDLE_HANDLES_PER_MASK) +#define HANDLE_CLUMPS_PER_MASK (HANDLE_HANDLES_PER_MASK / HANDLE_HANDLES_PER_CLUMP) + +// We use this relation to check for free mask per block. +C_ASSERT (HANDLE_HANDLES_PER_MASK * 2 == HANDLE_HANDLES_PER_BLOCK); + + +// cache layout metrics +#define HANDLE_CACHE_TYPE_SIZE 128 // 128 == 63 handles per bank +#define HANDLES_PER_CACHE_BANK ((HANDLE_CACHE_TYPE_SIZE / 2) - 1) + +// cache policy defines +#define REBALANCE_TOLERANCE (HANDLES_PER_CACHE_BANK / 3) +#define REBALANCE_LOWATER_MARK (HANDLES_PER_CACHE_BANK - REBALANCE_TOLERANCE) +#define REBALANCE_HIWATER_MARK (HANDLES_PER_CACHE_BANK + REBALANCE_TOLERANCE) + +// bulk alloc policy defines +#define SMALL_ALLOC_COUNT (HANDLES_PER_CACHE_BANK / 10) + +// misc constants +#define MASK_FULL (0) +#define MASK_EMPTY (0xFFFFFFFF) +#define MASK_LOBYTE (0x000000FF) +#define TYPE_INVALID ((uint8_t)0xFF) +#define BLOCK_INVALID ((uint8_t)0xFF) + +/*--------------------------------------------------------------------------*/ + +#endif // __HANDLETABLECONSTANTS_H__ diff --git a/src/coreclr/gc/handletablepriv.h b/src/coreclr/gc/handletablepriv.h index 4c055389963430..086ef2e018b6fa 100644 --- a/src/coreclr/gc/handletablepriv.h +++ b/src/coreclr/gc/handletablepriv.h @@ -14,113 +14,7 @@ #include "handletable.h" - - // Build support for async pinned handles into standalone GC to make it usable with older runtimes -#if defined(BUILD_AS_STANDALONE) && !defined(FEATURE_NATIVEAOT) -#define FEATURE_ASYNC_PINNED_HANDLES -#endif - - -/*--------------------------------------------------------------------------*/ - -//@TODO: find a home for this in a project-level header file -#define BITS_PER_BYTE (8) -/*--------------------------------------------------------------------------*/ - - - -/**************************************************************************** - * - * MAJOR TABLE DEFINITIONS THAT CHANGE DEPENDING ON THE WEATHER - * - ****************************************************************************/ - -// 64k reserved per segment with 4k as header. -#define HANDLE_SEGMENT_SIZE (0x10000) // MUST be a power of 2 (and currently must be 64K due to VirtualAlloc semantics) -#define HANDLE_HEADER_SIZE (0x1000) // SHOULD be <= OS page size - -#define HANDLE_SEGMENT_ALIGNMENT HANDLE_SEGMENT_SIZE - - -#if !BIGENDIAN - - // little-endian write barrier mask manipulation - #define GEN_CLUMP_0_MASK (0x000000FF) - #define NEXT_CLUMP_IN_MASK(dw) ((dw) >> BITS_PER_BYTE) - -#else - - // big-endian write barrier mask manipulation - #define GEN_CLUMP_0_MASK (0xFF000000) - #define NEXT_CLUMP_IN_MASK(dw) ((dw) << BITS_PER_BYTE) - -#endif - - -// if the above numbers change than these will likely change as well -#define HANDLE_HANDLES_PER_CLUMP (16) // segment write-barrier granularity -#define HANDLE_HANDLES_PER_BLOCK (64) // segment suballocation granularity -#define HANDLE_OPTIMIZE_FOR_64_HANDLE_BLOCKS // flag for certain optimizations - -// number of types allowed for public callers -#define HANDLE_MAX_PUBLIC_TYPES (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve one internal type - -// internal block types -#define HNDTYPE_INTERNAL_DATABLOCK (HANDLE_MAX_INTERNAL_TYPES - 1) // reserve last type for data blocks - -// max number of generations to support statistics on -#define MAXSTATGEN (5) - -/*--------------------------------------------------------------------------*/ - - - -/**************************************************************************** - * - * MORE DEFINITIONS - * - ****************************************************************************/ - -// fast handle-to-segment mapping -#define HANDLE_SEGMENT_CONTENT_MASK (HANDLE_SEGMENT_SIZE - 1) -#define HANDLE_SEGMENT_ALIGN_MASK (~HANDLE_SEGMENT_CONTENT_MASK) - -// table layout metrics -#define HANDLE_SIZE sizeof(_UNCHECKED_OBJECTREF) -#define HANDLE_HANDLES_PER_SEGMENT ((HANDLE_SEGMENT_SIZE - HANDLE_HEADER_SIZE) / HANDLE_SIZE) -#define HANDLE_BLOCKS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_BLOCK) -#define HANDLE_CLUMPS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_CLUMP) -#define HANDLE_CLUMPS_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK / HANDLE_HANDLES_PER_CLUMP) -#define HANDLE_BYTES_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK * HANDLE_SIZE) -#define HANDLE_HANDLES_PER_MASK (sizeof(uint32_t) * BITS_PER_BYTE) -#define HANDLE_MASKS_PER_SEGMENT (HANDLE_HANDLES_PER_SEGMENT / HANDLE_HANDLES_PER_MASK) -#define HANDLE_MASKS_PER_BLOCK (HANDLE_HANDLES_PER_BLOCK / HANDLE_HANDLES_PER_MASK) -#define HANDLE_CLUMPS_PER_MASK (HANDLE_HANDLES_PER_MASK / HANDLE_HANDLES_PER_CLUMP) - -// We use this relation to check for free mask per block. -C_ASSERT (HANDLE_HANDLES_PER_MASK * 2 == HANDLE_HANDLES_PER_BLOCK); - - -// cache layout metrics -#define HANDLE_CACHE_TYPE_SIZE 128 // 128 == 63 handles per bank -#define HANDLES_PER_CACHE_BANK ((HANDLE_CACHE_TYPE_SIZE / 2) - 1) - -// cache policy defines -#define REBALANCE_TOLERANCE (HANDLES_PER_CACHE_BANK / 3) -#define REBALANCE_LOWATER_MARK (HANDLES_PER_CACHE_BANK - REBALANCE_TOLERANCE) -#define REBALANCE_HIWATER_MARK (HANDLES_PER_CACHE_BANK + REBALANCE_TOLERANCE) - -// bulk alloc policy defines -#define SMALL_ALLOC_COUNT (HANDLES_PER_CACHE_BANK / 10) - -// misc constants -#define MASK_FULL (0) -#define MASK_EMPTY (0xFFFFFFFF) -#define MASK_LOBYTE (0x000000FF) -#define TYPE_INVALID ((uint8_t)0xFF) -#define BLOCK_INVALID ((uint8_t)0xFF) - -/*--------------------------------------------------------------------------*/ +#include "handletableconstants.h" @@ -483,6 +377,11 @@ struct HandleTable */ uint32_t rgTypeFlags[HANDLE_MAX_INTERNAL_TYPES]; + /* + * head of segment list for this table + */ + PTR_TableSegment pSegmentList; + /* * lock for this table */ @@ -499,11 +398,6 @@ struct HandleTable */ uint32_t dwCount; - /* - * head of segment list for this table - */ - PTR_TableSegment pSegmentList; - /* * information on current async scan (if any) */ diff --git a/src/coreclr/gc/objecthandle.cpp b/src/coreclr/gc/objecthandle.cpp index 52b25b88adf51f..43e09789bab815 100644 --- a/src/coreclr/gc/objecthandle.cpp +++ b/src/coreclr/gc/objecthandle.cpp @@ -1811,6 +1811,8 @@ void PopulateHandleTableDacVars(GcDacVars* gcDacVars) static_assert(offsetof(HandleTableMap, dwMaxIndex) == offsetof(dac_handle_table_map, dwMaxIndex), "handle table map DAC layout mismatch"); static_assert(offsetof(HandleTableBucket, pTable) == offsetof(dac_handle_table_bucket, pTable), "handle table bucket DAC layout mismatch"); static_assert(offsetof(HandleTableBucket, HandleTableIndex) == offsetof(dac_handle_table_bucket, HandleTableIndex), "handle table bucket DAC layout mismatch"); + static_assert(offsetof(HandleTable, pSegmentList) == offsetof(dac_handle_table, pSegmentList), "handle table bucket DAC layout mismatch"); + static_assert(offsetof(_TableSegmentHeader, pNextSegment) == offsetof(dac_handle_table_segment, pNextSegment), "handle table bucket DAC layout mismatch"); #ifndef DACCESS_COMPILE gcDacVars->handle_table_map = reinterpret_cast(&g_HandleTableMap); diff --git a/src/coreclr/inc/sospriv.idl b/src/coreclr/inc/sospriv.idl index b1a3b18e06b8b9..ac7b93a9a7bc12 100644 --- a/src/coreclr/inc/sospriv.idl +++ b/src/coreclr/inc/sospriv.idl @@ -49,6 +49,7 @@ cpp_quote("#endif") cpp_quote("typedef enum { TYPEDEFTOMETHODTABLE, TYPEREFTOMETHODTABLE } ModuleMapType;") cpp_quote("typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap, VtableHeap} VCSHeapType;") cpp_quote("typedef enum {LoaderHeapKindNormal = 0, LoaderHeapKindExplicitControl = 1} LoaderHeapKind;") +cpp_quote("typedef enum {FreeUnknownRegion = 0, FreeGlobalHugeRegion = 1, FreeGlobalRegion = 2, FreeRegion = 3, FreeSohSegment = 4, FreeUohSegment = 5 } FreeRegionKind;") typedef void (*MODULEMAPTRAVERSE)(UINT index, CLRDATA_ADDRESS methodTable,LPVOID token); typedef void (*VISITHEAP)(CLRDATA_ADDRESS blockData,size_t blockSize,BOOL blockIsCurrentBlock); @@ -172,6 +173,32 @@ interface ISOSStackRefEnum : ISOSEnum } +cpp_quote("#ifndef _SOS_MemoryRegion_") +cpp_quote("#define _SOS_MemoryRegion_") + +typedef struct _SOSMemoryRegion +{ + CLRDATA_ADDRESS Start; + CLRDATA_ADDRESS Size; + CLRDATA_ADDRESS ExtraData; + int Heap; +} SOSMemoryRegion; + +cpp_quote("#endif // _SOS_MemoryRegion_") + +[ + object, + local, + uuid(E4B860EC-337A-40C0-A591-F09A9680690F) +] +interface ISOSMemoryEnum : ISOSEnum +{ + HRESULT Next([in] unsigned int count, + [out, size_is(count), length_is(*pNeeded)] SOSMemoryRegion memRegion[], + [out] unsigned int *pNeeded); +} + + [ object, local, @@ -465,7 +492,7 @@ interface ISOSDacInterface12 : IUnknown [ object, local, - uuid(3176a8ed-597b-4f54-a71f-83695c6a8c5d) + uuid(3176a8ed-597b-4f54-a71f-83695c6a8c5e) ] interface ISOSDacInterface13 : IUnknown { @@ -473,4 +500,8 @@ interface ISOSDacInterface13 : IUnknown HRESULT GetDomainLoaderAllocator(CLRDATA_ADDRESS domainAddress, CLRDATA_ADDRESS *pLoaderAllocator); HRESULT GetLoaderAllocatorHeapNames(int count, const char **ppNames, int *pNeeded); HRESULT GetLoaderAllocatorHeaps(CLRDATA_ADDRESS loaderAllocator, int count, CLRDATA_ADDRESS *pLoaderHeaps, LoaderHeapKind *pKinds, int *pNeeded); + HRESULT GetHandleTableMemoryRegions(ISOSMemoryEnum **ppEnum); + HRESULT GetGCBookkeepingMemoryRegions(ISOSMemoryEnum **ppEnum); + HRESULT GetGCFreeRegions(ISOSMemoryEnum **ppEnum); + HRESULT LockedFlush(); } diff --git a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp index 07f02d061e86c4..141ec62612e48f 100644 --- a/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp +++ b/src/coreclr/pal/prebuilt/idl/sospriv_i.cpp @@ -5,9 +5,11 @@ /* link this file in with the server and any clients */ - /* File created by MIDL compiler version 8.01.0626 */ + /* File created by MIDL compiler version 8.01.0622 */ +/* at Mon Jan 18 19:14:07 2038 + */ /* Compiler settings for sospriv.idl: - Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0626 + Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.01.0622 protocol : dce , ms_ext, c_ext, robust error checks: allocation ref bounds_check enum stub_data VC __declspec() decoration level: @@ -77,6 +79,9 @@ MIDL_DEFINE_GUID(IID, IID_ISOSStackRefErrorEnum,0x774F4E1B,0xFB7B,0x491B,0x97,0x MIDL_DEFINE_GUID(IID, IID_ISOSStackRefEnum,0x8FA642BD,0x9F10,0x4799,0x9A,0xA3,0x51,0x2A,0xE7,0x8C,0x77,0xEE); +MIDL_DEFINE_GUID(IID, IID_ISOSMemoryEnum,0xE4B860EC,0x337A,0x40C0,0xA5,0x91,0xF0,0x9A,0x96,0x80,0x69,0x0F); + + MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface,0x436f00f2,0xb42a,0x4b9f,0x87,0x0c,0xe7,0x3d,0xb6,0x6a,0xe9,0x30); @@ -113,7 +118,7 @@ MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface11,0x96BA1DB9,0x14CD,0x4492,0x80,0x65, MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface12,0x1b93bacc,0x8ca4,0x432d,0x94,0x3a,0x3e,0x6e,0x7e,0xc0,0xb0,0xa3); -MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface13,0x3176a8ed,0x597b,0x4f54,0xa7,0x1f,0x83,0x69,0x5c,0x6a,0x8c,0x5d); +MIDL_DEFINE_GUID(IID, IID_ISOSDacInterface13,0x3176a8ed,0x597b,0x4f54,0xa7,0x1f,0x83,0x69,0x5c,0x6a,0x8c,0x5e); #undef MIDL_DEFINE_GUID diff --git a/src/coreclr/pal/prebuilt/inc/sospriv.h b/src/coreclr/pal/prebuilt/inc/sospriv.h index cc344809965608..e6f2b7b4f049ea 100644 --- a/src/coreclr/pal/prebuilt/inc/sospriv.h +++ b/src/coreclr/pal/prebuilt/inc/sospriv.h @@ -73,6 +73,13 @@ typedef interface ISOSStackRefEnum ISOSStackRefEnum; #endif /* __ISOSStackRefEnum_FWD_DEFINED__ */ +#ifndef __ISOSMemoryEnum_FWD_DEFINED__ +#define __ISOSMemoryEnum_FWD_DEFINED__ +typedef interface ISOSMemoryEnum ISOSMemoryEnum; + +#endif /* __ISOSMemoryEnum_FWD_DEFINED__ */ + + #ifndef __ISOSDacInterface_FWD_DEFINED__ #define __ISOSDacInterface_FWD_DEFINED__ typedef interface ISOSDacInterface ISOSDacInterface; @@ -198,6 +205,7 @@ typedef int VCSHeapType; typedef enum { TYPEDEFTOMETHODTABLE, TYPEREFTOMETHODTABLE } ModuleMapType; typedef enum {IndcellHeap, LookupHeap, ResolveHeap, DispatchHeap, CacheEntryHeap, VtableHeap} VCSHeapType; typedef enum {LoaderHeapKindNormal = 0, LoaderHeapKindExplicitControl = 1} LoaderHeapKind; +typedef enum {FreeUnknownRegion = 0, FreeGlobalHugeRegion = 1, FreeGlobalRegion = 2, FreeRegion = 3, FreeSohSegment = 4, FreeUohSegment = 5 } FreeRegionKind; typedef void ( *MODULEMAPTRAVERSE )( UINT index, CLRDATA_ADDRESS methodTable, @@ -489,6 +497,17 @@ typedef struct _SOS_StackRefError extern RPC_IF_HANDLE __MIDL_itf_sospriv_0000_0002_v0_0_c_ifspec; extern RPC_IF_HANDLE __MIDL_itf_sospriv_0000_0002_v0_0_s_ifspec; +#ifndef _SOS_MemoryRegion_ +#define _SOS_MemoryRegion_ +typedef struct _SOSMemoryRegion + { + CLRDATA_ADDRESS Start; + CLRDATA_ADDRESS Size; + CLRDATA_ADDRESS ExtraData; + int Heap; + } SOSMemoryRegion; +#endif // _SOS_MemoryRegion_ + #ifndef __ISOSStackRefErrorEnum_INTERFACE_DEFINED__ #define __ISOSStackRefErrorEnum_INTERFACE_DEFINED__ @@ -594,6 +613,112 @@ EXTERN_C const IID IID_ISOSStackRefErrorEnum; #endif /* __ISOSStackRefErrorEnum_INTERFACE_DEFINED__ */ + +#ifndef __ISOSMemoryEnum_INTERFACE_DEFINED__ +#define __ISOSMemoryEnum_INTERFACE_DEFINED__ + +/* interface ISOSMemoryEnum */ +/* [uuid][local][object] */ + + +EXTERN_C const IID IID_ISOSMemoryEnum; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("E4B860EC-337A-40C0-A591-F09A9680690F") + ISOSMemoryEnum : public ISOSEnum + { + public: + virtual HRESULT STDMETHODCALLTYPE Next( + /* [in] */ unsigned int count, + /* [length_is][size_is][out] */ SOSMemoryRegion memRegion[ ], + /* [out] */ unsigned int *pNeeded) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ISOSMemoryEnumVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ISOSMemoryEnum * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ISOSMemoryEnum * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ISOSMemoryEnum * This); + + HRESULT ( STDMETHODCALLTYPE *Skip )( + ISOSMemoryEnum * This, + /* [in] */ unsigned int count); + + HRESULT ( STDMETHODCALLTYPE *Reset )( + ISOSMemoryEnum * This); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + ISOSMemoryEnum * This, + /* [out] */ unsigned int *pCount); + + HRESULT ( STDMETHODCALLTYPE *Next )( + ISOSMemoryEnum * This, + /* [in] */ unsigned int count, + /* [length_is][size_is][out] */ SOSMemoryRegion memRegion[ ], + /* [out] */ unsigned int *pNeeded); + + END_INTERFACE + } ISOSMemoryEnumVtbl; + + interface ISOSMemoryEnum + { + CONST_VTBL struct ISOSMemoryEnumVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISOSMemoryEnum_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ISOSMemoryEnum_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ISOSMemoryEnum_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ISOSMemoryEnum_Skip(This,count) \ + ( (This)->lpVtbl -> Skip(This,count) ) + +#define ISOSMemoryEnum_Reset(This) \ + ( (This)->lpVtbl -> Reset(This) ) + +#define ISOSMemoryEnum_GetCount(This,pCount) \ + ( (This)->lpVtbl -> GetCount(This,pCount) ) + + +#define ISOSMemoryEnum_Next(This,count,memRegion,pNeeded) \ + ( (This)->lpVtbl -> Next(This,count,memRegion,pNeeded) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ISOSMemoryEnum_INTERFACE_DEFINED__ */ + + #ifndef __ISOSStackRefEnum_INTERFACE_DEFINED__ #define __ISOSStackRefEnum_INTERFACE_DEFINED__ @@ -3084,7 +3209,7 @@ EXTERN_C const IID IID_ISOSDacInterface13; #if defined(__cplusplus) && !defined(CINTERFACE) - MIDL_INTERFACE("3176a8ed-597b-4f54-a71f-83695c6a8c5d") + MIDL_INTERFACE("3176a8ed-597b-4f54-a71f-83695c6a8c5e") ISOSDacInterface13 : public IUnknown { public: @@ -3108,37 +3233,79 @@ EXTERN_C const IID IID_ISOSDacInterface13; CLRDATA_ADDRESS *pLoaderHeaps, LoaderHeapKind *pKinds, int *pNeeded) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetHandleTableMemoryRegions( + ISOSMemoryEnum **ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGCBookkeepingMemoryRegions( + ISOSMemoryEnum **ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetGCFreeRegions( + ISOSMemoryEnum **ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE LockedFlush( void) = 0; }; #else /* C style interface */ + typedef struct ISOSDacInterface13Vtbl { BEGIN_INTERFACE - DECLSPEC_XFGVIRT(IUnknown, QueryInterface) HRESULT ( STDMETHODCALLTYPE *QueryInterface )( ISOSDacInterface13 * This, /* [in] */ REFIID riid, /* [annotation][iid_is][out] */ _COM_Outptr_ void **ppvObject); - DECLSPEC_XFGVIRT(IUnknown, AddRef) ULONG ( STDMETHODCALLTYPE *AddRef )( ISOSDacInterface13 * This); - DECLSPEC_XFGVIRT(IUnknown, Release) ULONG ( STDMETHODCALLTYPE *Release )( ISOSDacInterface13 * This); - DECLSPEC_XFGVIRT(ISOSDacInterface13, TraverseLoaderHeap) HRESULT ( STDMETHODCALLTYPE *TraverseLoaderHeap )( ISOSDacInterface13 * This, CLRDATA_ADDRESS loaderHeapAddr, LoaderHeapKind kind, VISITHEAP pCallback); + HRESULT ( STDMETHODCALLTYPE *GetDomainLoaderAllocator )( + ISOSDacInterface13 * This, + CLRDATA_ADDRESS domainAddress, + CLRDATA_ADDRESS *pLoaderAllocator); + + HRESULT ( STDMETHODCALLTYPE *GetLoaderAllocatorHeapNames )( + ISOSDacInterface13 * This, + int count, + const unsigned char **ppNames, + int *pNeeded); + + HRESULT ( STDMETHODCALLTYPE *GetLoaderAllocatorHeaps )( + ISOSDacInterface13 * This, + CLRDATA_ADDRESS loaderAllocator, + int count, + CLRDATA_ADDRESS *pLoaderHeaps, + LoaderHeapKind *pKinds, + int *pNeeded); + + HRESULT ( STDMETHODCALLTYPE *GetHandleTableMemoryRegions )( + ISOSDacInterface13 * This, + ISOSMemoryEnum **ppEnum); + + HRESULT ( STDMETHODCALLTYPE *GetGCBookkeepingMemoryRegions )( + ISOSDacInterface13 * This, + ISOSMemoryEnum **ppEnum); + + HRESULT ( STDMETHODCALLTYPE *GetGCFreeRegions )( + ISOSDacInterface13 * This, + ISOSMemoryEnum **ppEnum); + + HRESULT ( STDMETHODCALLTYPE *LockedFlush )( + ISOSDacInterface13 * This); + END_INTERFACE } ISOSDacInterface13Vtbl; diff --git a/src/coreclr/vm/decodemd.cpp b/src/coreclr/vm/decodemd.cpp index 6def9757c9b26a..e1f7e8e3877a70 100644 --- a/src/coreclr/vm/decodemd.cpp +++ b/src/coreclr/vm/decodemd.cpp @@ -73,7 +73,6 @@ const BYTE decoded_10[2] = {10, END_DECODED }; #define DECODING_ERROR ((unsigned) -1) #define MASK(len) (~(~0u <<(len))) #define MASK64(len) ((~((~((unsigned __int64)0))<<(len)))) -#define BITS_PER_BYTE (sizeof(BYTE)*8) const Decoder::Decode emptyDecode = {decoded_end, DECODING_HEADER(0)};