| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | //***************************************************************************** |
| 5 | |
| 6 | // |
| 7 | // File: request.cpp |
| 8 | // |
| 9 | // CorDataAccess::Request implementation. |
| 10 | // |
| 11 | //***************************************************************************** |
| 12 | |
| 13 | #include "stdafx.h" |
| 14 | #include "dacdbiinterface.h" |
| 15 | #include "dacdbiimpl.h" |
| 16 | |
| 17 | #if defined(FEATURE_SVR_GC) |
| 18 | |
| 19 | #include <sigformat.h> |
| 20 | #include <win32threadpool.h> |
| 21 | #include "request_common.h" |
| 22 | |
| 23 | int GCHeapCount() |
| 24 | { |
| 25 | if (g_gcDacGlobals->n_heaps == nullptr) |
| 26 | return 0; |
| 27 | return *g_gcDacGlobals->n_heaps; |
| 28 | } |
| 29 | |
| 30 | HRESULT GetServerHeapData(CLRDATA_ADDRESS addr, DacpHeapSegmentData *pSegment) |
| 31 | { |
| 32 | // get field values (target addresses) for the heap segment at addr |
| 33 | if (!addr) |
| 34 | { |
| 35 | // PREfix. |
| 36 | return E_INVALIDARG; |
| 37 | } |
| 38 | |
| 39 | // marshal the segment from target to host |
| 40 | dac_heap_segment *pHeapSegment = __DPtr<dac_heap_segment>(TO_TADDR(addr)); |
| 41 | |
| 42 | // initialize fields by copying from the marshaled segment (note that these are all target addresses) |
| 43 | pSegment->segmentAddr = addr; |
| 44 | pSegment->allocated = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->allocated; |
| 45 | pSegment->committed = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->committed; |
| 46 | pSegment->reserved = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->reserved; |
| 47 | pSegment->used = (CLRDATA_ADDRESS)(ULONG_PTR) pHeapSegment->used; |
| 48 | pSegment->mem = (CLRDATA_ADDRESS)(ULONG_PTR) (pHeapSegment->mem); |
| 49 | pSegment->next = (CLRDATA_ADDRESS)dac_cast<TADDR>(pHeapSegment->next); |
| 50 | pSegment->gc_heap = (CLRDATA_ADDRESS)pHeapSegment->heap; |
| 51 | |
| 52 | return S_OK; |
| 53 | } |
| 54 | |
| 55 | HRESULT GetServerHeaps(CLRDATA_ADDRESS pGCHeaps[], ICorDebugDataTarget * pTarget) |
| 56 | { |
| 57 | // @todo Microsoft: It would be good to have an assert here to ensure pGCHeaps is large enough to |
| 58 | // hold all the addresses. Currently we check that in the only caller, but if we were to call this from |
| 59 | // somewhere else in the future, we could have a buffer overrun. |
| 60 | |
| 61 | // The runtime declares its own global array of gc heap addresses for multiple heap scenarios. We need to get |
| 62 | // its starting address. This expression is a little tricky to parse, but in DAC builds, g_heaps is |
| 63 | // a DAC global (__GlobalPtr). The __GlobalPtr<...>::GetAddr() function gets the starting address of that global, but |
| 64 | // be sure to note this is a target address. We'll use this as our source for getting our local list of |
| 65 | // heap addresses. |
| 66 | for (int i = 0; i < GCHeapCount(); i++) |
| 67 | { |
| 68 | pGCHeaps[i] = (CLRDATA_ADDRESS)HeapTableIndex(g_gcDacGlobals->g_heaps, i).GetAddr(); |
| 69 | } |
| 70 | |
| 71 | return S_OK; |
| 72 | } |
| 73 | |
| 74 | #define PTR_CDADDR(ptr) TO_CDADDR(PTR_TO_TADDR(ptr)) |
| 75 | #define HOST_CDADDR(host) TO_CDADDR(PTR_HOST_TO_TADDR(host)) |
| 76 | |
| 77 | HRESULT ClrDataAccess::GetServerAllocData(unsigned int count, struct DacpGenerationAllocData *data, unsigned int *pNeeded) |
| 78 | { |
| 79 | unsigned int heaps = (unsigned int)GCHeapCount(); |
| 80 | if (pNeeded) |
| 81 | *pNeeded = heaps; |
| 82 | |
| 83 | if (data) |
| 84 | { |
| 85 | if (count > heaps) |
| 86 | count = heaps; |
| 87 | |
| 88 | for (unsigned int n=0; n < heaps; n++) |
| 89 | { |
| 90 | DPTR(dac_gc_heap) pHeap = HeapTableIndex(g_gcDacGlobals->g_heaps, n); |
| 91 | for (int i=0;i<NUMBERGENERATIONS;i++) |
| 92 | { |
| 93 | dac_generation generation = *ServerGenerationTableIndex(pHeap, i); |
| 94 | data[n].allocData[i].allocBytes = (CLRDATA_ADDRESS)(ULONG_PTR) generation.allocation_context.alloc_bytes; |
| 95 | data[n].allocData[i].allocBytesLoh = (CLRDATA_ADDRESS)(ULONG_PTR) generation.allocation_context.alloc_bytes_loh; |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | return S_OK; |
| 101 | } |
| 102 | |
| 103 | HRESULT ClrDataAccess::ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, DacpGcHeapDetails *detailsData) |
| 104 | { |
| 105 | if (!heapAddr) |
| 106 | { |
| 107 | // PREfix. |
| 108 | return E_INVALIDARG; |
| 109 | } |
| 110 | |
| 111 | DPTR(dac_gc_heap) pHeap = __DPtr<dac_gc_heap>(TO_TADDR(heapAddr)); |
| 112 | int i; |
| 113 | |
| 114 | //get global information first |
| 115 | detailsData->heapAddr = heapAddr; |
| 116 | |
| 117 | detailsData->lowest_address = PTR_CDADDR(g_lowest_address); |
| 118 | detailsData->highest_address = PTR_CDADDR(g_highest_address); |
| 119 | detailsData->card_table = PTR_CDADDR(g_card_table); |
| 120 | |
| 121 | // now get information specific to this heap (server mode gives us several heaps; we're getting |
| 122 | // information about only one of them. |
| 123 | detailsData->alloc_allocated = (CLRDATA_ADDRESS)pHeap->alloc_allocated; |
| 124 | detailsData->ephemeral_heap_segment = (CLRDATA_ADDRESS)dac_cast<TADDR>(pHeap->ephemeral_heap_segment); |
| 125 | |
| 126 | // get bounds for the different generations |
| 127 | for (i=0; i<NUMBERGENERATIONS; i++) |
| 128 | { |
| 129 | DPTR(dac_generation) generation = ServerGenerationTableIndex(pHeap, i); |
| 130 | detailsData->generation_table[i].start_segment = (CLRDATA_ADDRESS)dac_cast<TADDR>(generation->start_segment); |
| 131 | detailsData->generation_table[i].allocation_start = (CLRDATA_ADDRESS)(ULONG_PTR)generation->allocation_start; |
| 132 | DPTR(gc_alloc_context) alloc_context = dac_cast<TADDR>(generation) + offsetof(dac_generation, allocation_context); |
| 133 | detailsData->generation_table[i].allocContextPtr = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context->alloc_ptr; |
| 134 | detailsData->generation_table[i].allocContextLimit = (CLRDATA_ADDRESS)(ULONG_PTR) alloc_context->alloc_limit; |
| 135 | } |
| 136 | |
| 137 | DPTR(dac_finalize_queue) fq = pHeap->finalize_queue; |
| 138 | DPTR(uint8_t*) pFillPointerArray= dac_cast<TADDR>(fq) + offsetof(dac_finalize_queue, m_FillPointers); |
| 139 | for(i=0; i<(NUMBERGENERATIONS+dac_finalize_queue::ExtraSegCount); i++) |
| 140 | { |
| 141 | detailsData->finalization_fill_pointers[i] = (CLRDATA_ADDRESS) pFillPointerArray[i]; |
| 142 | } |
| 143 | |
| 144 | return S_OK; |
| 145 | } |
| 146 | |
| 147 | HRESULT |
| 148 | ClrDataAccess::ServerOomData(CLRDATA_ADDRESS addr, DacpOomData *oomData) |
| 149 | { |
| 150 | DPTR(dac_gc_heap) pHeap = __DPtr<dac_gc_heap>(TO_TADDR(addr)); |
| 151 | |
| 152 | oom_history pOOMInfo = pHeap->oom_info; |
| 153 | oomData->reason = pOOMInfo.reason; |
| 154 | oomData->alloc_size = pOOMInfo.alloc_size; |
| 155 | oomData->available_pagefile_mb = pOOMInfo.available_pagefile_mb; |
| 156 | oomData->gc_index = pOOMInfo.gc_index; |
| 157 | oomData->fgm = pOOMInfo.fgm; |
| 158 | oomData->size = pOOMInfo.size; |
| 159 | oomData->loh_p = pOOMInfo.loh_p; |
| 160 | |
| 161 | return S_OK; |
| 162 | } |
| 163 | |
| 164 | HRESULT |
| 165 | ClrDataAccess::ServerGCInterestingInfoData(CLRDATA_ADDRESS addr, DacpGCInterestingInfoData *interestingInfoData) |
| 166 | { |
| 167 | #ifdef GC_CONFIG_DRIVEN |
| 168 | dac_gc_heap *pHeap = __DPtr<dac_gc_heap>(TO_TADDR(addr)); |
| 169 | |
| 170 | size_t* dataPoints = (size_t*)&(pHeap->interesting_data_per_heap); |
| 171 | for (int i = 0; i < NUM_GC_DATA_POINTS; i++) |
| 172 | interestingInfoData->interestingDataPoints[i] = dataPoints[i]; |
| 173 | size_t* mechanisms = (size_t*)&(pHeap->compact_reasons_per_heap); |
| 174 | for (int i = 0; i < MAX_COMPACT_REASONS_COUNT; i++) |
| 175 | interestingInfoData->compactReasons[i] = mechanisms[i]; |
| 176 | mechanisms = (size_t*)&(pHeap->expand_mechanisms_per_heap); |
| 177 | for (int i = 0; i < MAX_EXPAND_MECHANISMS_COUNT; i++) |
| 178 | interestingInfoData->expandMechanisms[i] = mechanisms[i]; |
| 179 | mechanisms = (size_t*)&(pHeap->interesting_mechanism_bits_per_heap); |
| 180 | for (int i = 0; i < MAX_GC_MECHANISM_BITS_COUNT; i++) |
| 181 | interestingInfoData->bitMechanisms[i] = mechanisms[i]; |
| 182 | |
| 183 | return S_OK; |
| 184 | #else |
| 185 | return E_NOTIMPL; |
| 186 | #endif //GC_CONFIG_DRIVEN |
| 187 | } |
| 188 | |
| 189 | HRESULT ClrDataAccess::ServerGCHeapAnalyzeData(CLRDATA_ADDRESS heapAddr, DacpGcHeapAnalyzeData *analyzeData) |
| 190 | { |
| 191 | if (!heapAddr) |
| 192 | { |
| 193 | // PREfix. |
| 194 | return E_INVALIDARG; |
| 195 | } |
| 196 | |
| 197 | DPTR(dac_gc_heap) pHeap = __DPtr<dac_gc_heap>(TO_TADDR(heapAddr)); |
| 198 | |
| 199 | analyzeData->heapAddr = heapAddr; |
| 200 | analyzeData->internal_root_array = (CLRDATA_ADDRESS)pHeap->internal_root_array; |
| 201 | analyzeData->internal_root_array_index = (size_t)pHeap->internal_root_array_index; |
| 202 | analyzeData->heap_analyze_success = (BOOL)pHeap->heap_analyze_success; |
| 203 | |
| 204 | return S_OK; |
| 205 | } |
| 206 | |
| 207 | void |
| 208 | ClrDataAccess::EnumSvrGlobalMemoryRegions(CLRDataEnumMemoryFlags flags) |
| 209 | { |
| 210 | SUPPORTS_DAC; |
| 211 | |
| 212 | if (g_gcDacGlobals->n_heaps == nullptr || g_gcDacGlobals->g_heaps == nullptr) |
| 213 | return; |
| 214 | |
| 215 | g_gcDacGlobals->n_heaps.EnumMem(); |
| 216 | |
| 217 | int heaps = *g_gcDacGlobals->n_heaps; |
| 218 | DacEnumMemoryRegion(g_gcDacGlobals->g_heaps.GetAddr(), sizeof(TADDR) * heaps); |
| 219 | |
| 220 | g_gcDacGlobals->gc_structures_invalid_cnt.EnumMem(); |
| 221 | g_gcDacGlobals->g_heaps.EnumMem(); |
| 222 | |
| 223 | for (int i = 0; i < heaps; i++) |
| 224 | { |
| 225 | DPTR(dac_gc_heap) pHeap = HeapTableIndex(g_gcDacGlobals->g_heaps, i); |
| 226 | |
| 227 | size_t gen_table_size = g_gcDacGlobals->generation_size * (*g_gcDacGlobals->max_gen + 2); |
| 228 | DacEnumMemoryRegion(dac_cast<TADDR>(pHeap), sizeof(dac_gc_heap)); |
| 229 | DacEnumMemoryRegion(dac_cast<TADDR>(pHeap->finalize_queue), sizeof(dac_finalize_queue)); |
| 230 | DacEnumMemoryRegion(dac_cast<TADDR>(pHeap->generation_table), gen_table_size); |
| 231 | |
| 232 | // enumerating the generations from max (which is normally gen2) to max+1 gives you |
| 233 | // the segment list for all the normal segements plus the large heap segment (max+1) |
| 234 | // this is the convention in the GC so it is repeated here |
| 235 | for (ULONG i = *g_gcDacGlobals->max_gen; i <= *g_gcDacGlobals->max_gen +1; i++) |
| 236 | { |
| 237 | DPTR(dac_heap_segment) seg = ServerGenerationTableIndex(pHeap, i)->start_segment; |
| 238 | while (seg) |
| 239 | { |
| 240 | DacEnumMemoryRegion(PTR_HOST_TO_TADDR(seg), sizeof(dac_heap_segment)); |
| 241 | |
| 242 | seg = seg->next; |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | DWORD DacGetNumHeaps() |
| 249 | { |
| 250 | if (g_heap_type == GC_HEAP_SVR) |
| 251 | return (DWORD)*g_gcDacGlobals->n_heaps; |
| 252 | |
| 253 | // workstation gc |
| 254 | return 1; |
| 255 | } |
| 256 | |
| 257 | HRESULT DacHeapWalker::InitHeapDataSvr(HeapData *&pHeaps, size_t &pCount) |
| 258 | { |
| 259 | if (g_gcDacGlobals->n_heaps == nullptr || g_gcDacGlobals->g_heaps == nullptr) |
| 260 | return S_OK; |
| 261 | |
| 262 | // Scrape basic heap details |
| 263 | int heaps = *g_gcDacGlobals->n_heaps; |
| 264 | pCount = heaps; |
| 265 | pHeaps = new (nothrow) HeapData[heaps]; |
| 266 | if (pHeaps == NULL) |
| 267 | return E_OUTOFMEMORY; |
| 268 | |
| 269 | for (int i = 0; i < heaps; ++i) |
| 270 | { |
| 271 | // Basic heap info. |
| 272 | DPTR(dac_gc_heap) heap = HeapTableIndex(g_gcDacGlobals->g_heaps, i); |
| 273 | dac_generation gen0 = *ServerGenerationTableIndex(heap, 0); |
| 274 | dac_generation gen1 = *ServerGenerationTableIndex(heap, 1); |
| 275 | dac_generation gen2 = *ServerGenerationTableIndex(heap, 2); |
| 276 | dac_generation loh = *ServerGenerationTableIndex(heap, 3); |
| 277 | |
| 278 | pHeaps[i].YoungestGenPtr = (CORDB_ADDRESS)gen0.allocation_context.alloc_ptr; |
| 279 | pHeaps[i].YoungestGenLimit = (CORDB_ADDRESS)gen0.allocation_context.alloc_limit; |
| 280 | |
| 281 | pHeaps[i].Gen0Start = (CORDB_ADDRESS)gen0.allocation_start; |
| 282 | pHeaps[i].Gen0End = (CORDB_ADDRESS)heap->alloc_allocated; |
| 283 | pHeaps[i].Gen1Start = (CORDB_ADDRESS)gen1.allocation_start; |
| 284 | |
| 285 | // Segments |
| 286 | int count = GetSegmentCount(loh.start_segment); |
| 287 | count += GetSegmentCount(gen2.start_segment); |
| 288 | |
| 289 | pHeaps[i].SegmentCount = count; |
| 290 | pHeaps[i].Segments = new (nothrow) SegmentData[count]; |
| 291 | if (pHeaps[i].Segments == NULL) |
| 292 | return E_OUTOFMEMORY; |
| 293 | |
| 294 | // Small object heap segments |
| 295 | DPTR(dac_heap_segment) seg = gen2.start_segment; |
| 296 | int j = 0; |
| 297 | for (; seg && (j < count); ++j) |
| 298 | { |
| 299 | pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; |
| 300 | if (seg.GetAddr() == heap->ephemeral_heap_segment.GetAddr()) |
| 301 | { |
| 302 | pHeaps[i].Segments[j].End = (CORDB_ADDRESS)heap->alloc_allocated; |
| 303 | pHeaps[i].EphemeralSegment = j; |
| 304 | pHeaps[i].Segments[j].Generation = 1; |
| 305 | } |
| 306 | else |
| 307 | { |
| 308 | pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; |
| 309 | pHeaps[i].Segments[j].Generation = 2; |
| 310 | } |
| 311 | |
| 312 | seg = seg->next; |
| 313 | } |
| 314 | |
| 315 | |
| 316 | // Large object heap segments |
| 317 | seg = loh.start_segment; |
| 318 | for (; seg && (j < count); ++j) |
| 319 | { |
| 320 | pHeaps[i].Segments[j].Generation = 3; |
| 321 | pHeaps[i].Segments[j].Start = (CORDB_ADDRESS)seg->mem; |
| 322 | pHeaps[i].Segments[j].End = (CORDB_ADDRESS)seg->allocated; |
| 323 | |
| 324 | seg = seg->next; |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | return S_OK; |
| 329 | } |
| 330 | |
| 331 | #endif // defined(FEATURE_SVR_GC) |
| 332 | |