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
23int GCHeapCount()
24{
25 if (g_gcDacGlobals->n_heaps == nullptr)
26 return 0;
27 return *g_gcDacGlobals->n_heaps;
28}
29
30HRESULT 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
55HRESULT 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
77HRESULT 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
103HRESULT 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
147HRESULT
148ClrDataAccess::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
164HRESULT
165ClrDataAccess::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
189HRESULT 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
207void
208ClrDataAccess::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
248DWORD 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
257HRESULT 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