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 | |