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 | // File: enummem.cpp |
6 | // |
7 | |
8 | // |
9 | // ICLRDataEnumMemoryRegions implementation. |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #include "stdafx.h" |
14 | |
15 | #include <eeconfig.h> |
16 | #include <ecall.h> |
17 | #include <gcinfodecoder.h> |
18 | |
19 | #include "typestring.h" |
20 | #include "daccess.h" |
21 | #include "binder.h" |
22 | #include "win32threadpool.h" |
23 | |
24 | #ifdef FEATURE_PAL |
25 | #include <dactablerva.h> |
26 | #endif |
27 | |
28 | #ifdef FEATURE_APPX |
29 | #include "appxutil.h" |
30 | #endif // FEATURE_APPX |
31 | |
32 | #if defined(DAC_MEASURE_PERF) |
33 | |
34 | unsigned __int64 g_nTotalTime; |
35 | unsigned __int64 g_nStackTotalTime; |
36 | unsigned __int64 g_nReadVirtualTotalTime; |
37 | unsigned __int64 g_nFindTotalTime; |
38 | unsigned __int64 g_nFindHashTotalTime; |
39 | unsigned __int64 g_nFindHits; |
40 | unsigned __int64 g_nFindCalls; |
41 | unsigned __int64 g_nFindFails; |
42 | unsigned __int64 g_nStackWalk; |
43 | unsigned __int64 g_nFindStackTotalTime; |
44 | |
45 | #endif // #if defined(DAC_MEASURE_PERF) |
46 | |
47 | |
48 | // |
49 | // EnumMemCollectImages - collect all images of interest for heap dumps |
50 | // |
51 | // This is used primarily to save ngen images. |
52 | // This is necessary so that heap dumps contain the full native code for the |
53 | // process. Normally mini/heap dump debugging requires that the images be |
54 | // available at debug-time, (in fact, watson explicitly does not want to |
55 | // be downloading 3rd party images). Not including images is the main size |
56 | // advantage of heap dumps over full dumps. However, since ngen images are |
57 | // produced on the client, we can't always ensure that the debugger will |
58 | // have access to the exact ngen image used in the dump. Therefore, managed |
59 | // heap dumps also include full copies of all NGen images in the process. |
60 | // |
61 | // We also currently include in-memory modules (provided by a host, or loaded |
62 | // from a Byte[]). |
63 | // |
64 | HRESULT ClrDataAccess::EnumMemCollectImages() |
65 | { |
66 | SUPPORTS_DAC; |
67 | |
68 | ProcessModIter modIter; |
69 | Module* modDef = NULL; |
70 | HRESULT status = S_OK; |
71 | PEFile *file; |
72 | TADDR pStartAddr = 0; |
73 | ULONG32 ulSize = 0; |
74 | ULONG32 ulSizeBlock; |
75 | |
76 | TSIZE_T cbMemoryReported = m_cbMemoryReported; |
77 | |
78 | // |
79 | // Collect the ngen images - Iterating through module list |
80 | // |
81 | EX_TRY |
82 | { |
83 | while ((modDef = modIter.NextModule())) |
84 | { |
85 | EX_TRY |
86 | { |
87 | ulSize = 0; |
88 | file = modDef->GetFile(); |
89 | |
90 | // We want to save all native images |
91 | if (file->HasNativeImage()) |
92 | { |
93 | // We should only skip if signed by Microsoft! |
94 | pStartAddr = PTR_TO_TADDR(file->GetLoadedNative()->GetBase()); |
95 | ulSize = file->GetLoadedNative()->GetSize(); |
96 | } |
97 | // We also want to save any in-memory images. These show up like mapped files |
98 | // and so would not be in a heap dump by default. Technically it's not clear we |
99 | // should include them in the dump - you can often have the files available |
100 | // after-the-fact. But in-memory modules may be harder to track down at debug time |
101 | // and people may have come to rely on this - so we'll include them for now. |
102 | else |
103 | if ( |
104 | // With Copy On Write feature enabled |
105 | // IL images would not be dumped as part of the private pages. |
106 | // We need to explicitly dump them here. |
107 | #ifndef FEATURE_LAZY_COW_PAGES |
108 | file->GetPath().IsEmpty() && // is in-memory |
109 | #endif // FEATURE_LAZY_COW_PAGES |
110 | file->HasMetadata() && // skip resource assemblies |
111 | file->IsLoaded(FALSE) && // skip files not yet loaded |
112 | !file->IsDynamic()) // skip dynamic (GetLoadedIL asserts anyway) |
113 | { |
114 | pStartAddr = PTR_TO_TADDR(file->GetLoadedIL()->GetBase()); |
115 | ulSize = file->GetLoadedIL()->GetSize(); |
116 | } |
117 | |
118 | // memory are mapped in in GetOsPageSize() size. |
119 | // Some memory are mapped in but some are not. You cannot |
120 | // write all in one block. So iterating through page size |
121 | // |
122 | while (ulSize > 0) |
123 | { |
124 | // |
125 | // Note that we have talked about not writing IL and Metadata to save size. |
126 | // It turns out IL was rarely mapped in. |
127 | // Metadata is needed. The RVA field is needed for it is pointed to a |
128 | // MethodHeader MethodDesc::GetILHeader. Without this RVA, |
129 | // all locals are broken. In case, you are asked about this question again. |
130 | // |
131 | ulSizeBlock = ulSize > GetOsPageSize() ? GetOsPageSize() : ulSize; |
132 | ReportMem(pStartAddr, ulSizeBlock, false); |
133 | pStartAddr += ulSizeBlock; |
134 | ulSize -= ulSizeBlock; |
135 | } |
136 | } |
137 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
138 | } |
139 | } |
140 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
141 | |
142 | m_dumpStats.m_cbNgen = m_cbMemoryReported - cbMemoryReported; |
143 | return status; |
144 | } |
145 | |
146 | |
147 | |
148 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
149 | // |
150 | // collecting memory for mscorwks's heap dump critical statics |
151 | // This include the stress log, config structure, and IPC block |
152 | // |
153 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
154 | HRESULT ClrDataAccess::EnumMemCLRHeapCrticalStatic(IN CLRDataEnumMemoryFlags flags) |
155 | { |
156 | SUPPORTS_DAC; |
157 | |
158 | TSIZE_T cbMemoryReported = m_cbMemoryReported; |
159 | |
160 | // Write out the stress log structure itself |
161 | DacEnumHostDPtrMem(g_pStressLog); |
162 | |
163 | // This is pointing to a static buffer |
164 | DacEnumHostDPtrMem(g_pConfig); |
165 | |
166 | // dump GC heap structures. Note that the managed heap is not dumped out. |
167 | // We are just dump the GC heap structures. |
168 | // |
169 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumWksGlobalMemoryRegions(flags); ); |
170 | #ifdef FEATURE_SVR_GC |
171 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumSvrGlobalMemoryRegions(flags); ); |
172 | #endif |
173 | |
174 | m_dumpStats.m_cbClrHeapStatics = m_cbMemoryReported - cbMemoryReported; |
175 | |
176 | return S_OK; |
177 | } |
178 | |
179 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
180 | // |
181 | // collecting memory for mscorwks's statics. This is the minimal |
182 | // set of global and statics that we need to have !threads, !pe, !ClrStack |
183 | // to work. |
184 | // |
185 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
186 | HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags) |
187 | { |
188 | SUPPORTS_DAC; |
189 | |
190 | TSIZE_T cbMemoryReported = m_cbMemoryReported; |
191 | |
192 | // |
193 | // write out the static and global content that we care. |
194 | // |
195 | |
196 | // The followig macro will report memory all of the dac related mscorwks static and |
197 | // global variables. But it won't report the structures that are pointed by |
198 | // global pointers. |
199 | // |
200 | #define DEFINE_DACVAR(id_type, size_type, id, var) \ |
201 | ReportMem(m_globalBase + g_dacGlobals.id, sizeof(size_type)); |
202 | |
203 | #ifdef FEATURE_PAL |
204 | // Add the dac table memory in coreclr |
205 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED ( ReportMem(m_globalBase + DAC_TABLE_RVA, sizeof(g_dacGlobals)); ) |
206 | #endif |
207 | |
208 | // Cannot use CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED |
209 | // around conditional preprocessor directives in a sane fashion. |
210 | EX_TRY |
211 | { |
212 | #include "dacvars.h" |
213 | } |
214 | EX_CATCH |
215 | { |
216 | // Catch the exception and keep going unless COR_E_OPERATIONCANCELED |
217 | // was thrown. Used generating dumps, where rethrow will cancel dump. |
218 | } |
219 | EX_END_CATCH(RethrowCancelExceptions) |
220 | |
221 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED ( ReportMem(m_globalBase + g_dacGlobals.dac__g_pStressLog, sizeof(StressLog *)); ) |
222 | |
223 | EX_TRY |
224 | { |
225 | // These two static pointers are pointed to static data of byte[] |
226 | // then run constructor in place |
227 | // |
228 | ReportMem(m_globalBase + g_dacGlobals.SystemDomain__m_pSystemDomain, sizeof(SystemDomain)); |
229 | |
230 | // We need IGCHeap pointer to make EEVersion work |
231 | ReportMem(m_globalBase + g_dacGlobals.dac__g_pGCHeap, sizeof(IGCHeap *)); |
232 | |
233 | // see synblk.cpp, the pointer is pointed to a static byte[] |
234 | SyncBlockCache::s_pSyncBlockCache.EnumMem(); |
235 | |
236 | ReportMem(m_globalBase + g_dacGlobals.dac__g_FCDynamicallyAssignedImplementations, |
237 | sizeof(TADDR)*ECall::NUM_DYNAMICALLY_ASSIGNED_FCALL_IMPLEMENTATIONS); |
238 | |
239 | ReportMem(g_gcDacGlobals.GetAddr(), sizeof(GcDacVars)); |
240 | |
241 | // We need all of the dac variables referenced by the GC DAC global struct. |
242 | // This struct contains pointers to pointers, so we first dereference the pointers |
243 | // to obtain the location of the variable that's reported. |
244 | #define GC_DAC_VAR(type, name) ReportMem(g_gcDacGlobals->name.GetAddr(), sizeof(type)); |
245 | #include "../../gc/gcinterface.dacvars.def" |
246 | } |
247 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
248 | |
249 | #ifndef FEATURE_PAL |
250 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_runtimeLoadedBaseAddress.EnumMem(); ) |
251 | #endif // !FEATURE_PAL |
252 | |
253 | // These are the structures that are pointed by global pointers and we care. |
254 | // Some may reside in heap and some may reside as a static byte array in mscorwks.dll |
255 | // That is ok. We will report them explicitly. |
256 | // |
257 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pConfig.EnumMem(); ) |
258 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pPredefinedArrayTypes.EnumMem(); ) |
259 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pObjectClass.EnumMem(); ) |
260 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pStringClass.EnumMem(); ) |
261 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pArrayClass.EnumMem(); ) |
262 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pExceptionClass.EnumMem(); ) |
263 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pThreadAbortExceptionClass.EnumMem(); ) |
264 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pOutOfMemoryExceptionClass.EnumMem(); ) |
265 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pStackOverflowExceptionClass.EnumMem(); ) |
266 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pExecutionEngineExceptionClass.EnumMem(); ) |
267 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDelegateClass.EnumMem(); ) |
268 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pMulticastDelegateClass.EnumMem(); ) |
269 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pValueTypeClass.EnumMem(); ) |
270 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEnumClass.EnumMem(); ) |
271 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pThreadClass.EnumMem(); ) |
272 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pFreeObjectMethodTable.EnumMem(); ) |
273 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_fHostConfig.EnumMem(); ) |
274 | |
275 | // These two static pointers are pointed to static data of byte[] |
276 | // then run constructor in place |
277 | // |
278 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( SystemDomain::m_pSystemDomain.EnumMem(); ) |
279 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger.EnumMem(); ) |
280 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEEInterface.EnumMem(); ) |
281 | if (g_pDebugInterface != nullptr) |
282 | { |
283 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED(g_pDebugInterface.EnumMem(); ) |
284 | } |
285 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pEEDbgInterfaceImpl.EnumMem(); ) |
286 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_CORDebuggerControlFlags.EnumMem(); ) |
287 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_Mscorlib.EnumMem(); ) |
288 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]->EnumMemoryRegions(flags); ) |
289 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( StubManager::EnumMemoryRegions(flags); ) |
290 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pFinalizerThread.EnumMem(); ) |
291 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pSuspensionThread.EnumMem(); ) |
292 | |
293 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_heap_type.EnumMem(); ) |
294 | |
295 | m_dumpStats.m_cbClrStatics = m_cbMemoryReported - cbMemoryReported; |
296 | |
297 | return S_OK; |
298 | } |
299 | |
300 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
301 | // |
302 | // This function reports memory that a heap dump need to debug CLR |
303 | // and managed code efficiently. |
304 | // |
305 | // We will write out - |
306 | // 1. mscorwks.dll's image read/write pages |
307 | // 2. IPC blocks - shared memory (needed for debugging service and perf counter) |
308 | // 3. ngen images excluding Metadata and IL for size perf |
309 | // 4. We may want to touch the code pages on the stack - to be safe.... |
310 | // |
311 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
312 | HRESULT ClrDataAccess::EnumMemoryRegionsWorkerHeap(IN CLRDataEnumMemoryFlags flags) |
313 | { |
314 | SUPPORTS_DAC; |
315 | |
316 | HRESULT status = S_OK; |
317 | |
318 | // clear all of the previous cached memory |
319 | Flush(); |
320 | |
321 | // collect ngen image |
322 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCollectImages(); ); |
323 | |
324 | // collect CLR static |
325 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ); |
326 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(flags); ); |
327 | |
328 | // Note that we do not need to flush out all of the dac instance manager's instance. |
329 | // This is because it is a heap dump here. Assembly and AppDomain objects will be reported |
330 | // by the default heap collection mechanism by dbghelp.lib |
331 | // |
332 | // Microsoft: I suspect if we have all private read-write pages the preceding statement |
333 | // would be true, but I don't think we have that guarantee here. |
334 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); ); |
335 | |
336 | // Iterating to all threads' stacks, as we have to collect data stored inside (core)clr.dll |
337 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); ) |
338 | |
339 | // Dump AppDomain-specific info |
340 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) |
341 | |
342 | // Dump the Debugger object data needed |
343 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger->EnumMemoryRegions(flags); ) |
344 | |
345 | // now dump the memory get dragged in by using DAC API implicitly. |
346 | m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); |
347 | |
348 | // Do not let any remaining implicitly enumerated memory leak out. |
349 | Flush(); |
350 | |
351 | return S_OK; |
352 | } // EnumMemoryRegionsWorkerHeap |
353 | |
354 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
355 | // |
356 | // Helper function for skinny mini-dump |
357 | // Pass in an managed object, this function will dump the EEClass hierachy |
358 | // and field desc of object so SOS's !DumpObj will work |
359 | // |
360 | // |
361 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
362 | HRESULT ClrDataAccess::DumpManagedObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef) |
363 | { |
364 | SUPPORTS_DAC; |
365 | |
366 | HRESULT status = S_OK; |
367 | |
368 | if (objRef == NULL) |
369 | { |
370 | return status; |
371 | } |
372 | |
373 | if (*g_gcDacGlobals->gc_structures_invalid_cnt != 0) |
374 | { |
375 | // GC is in progress, don't dump this object |
376 | return S_OK; |
377 | } |
378 | |
379 | EX_TRY |
380 | { |
381 | // write out the current EE class and the direct/indirect inherited EE Classes |
382 | MethodTable *pMethodTable = objRef->GetGCSafeMethodTable(); |
383 | |
384 | while (pMethodTable) |
385 | { |
386 | EX_TRY |
387 | { |
388 | pMethodTable->EnumMemoryRegions(flags); |
389 | |
390 | StackSString s; |
391 | |
392 | // This might look odd. We are not using variable s after forming it. |
393 | // That is because our DAC inspecting API is using TypeString to form |
394 | // full type name. Form the full name here is a implicit reference to needed |
395 | // memory. |
396 | // |
397 | TypeString::AppendType(s, TypeHandle(pMethodTable), TypeString::FormatNamespace|TypeString::FormatFullInst); |
398 | } |
399 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
400 | |
401 | // Walk up to parent MethodTable |
402 | pMethodTable = pMethodTable->GetParentMethodTable(); |
403 | } |
404 | |
405 | // now dump the content for the managed object |
406 | objRef->EnumMemoryRegions(); |
407 | } |
408 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
409 | |
410 | return status; |
411 | } |
412 | |
413 | |
414 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
415 | // |
416 | // Helper function for skinny mini-dump |
417 | // Pass in an managed excption object, this function will dump |
418 | // the managed exception object and some of its fields, such as message, stack trace string, |
419 | // inner exception. |
420 | // |
421 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
422 | HRESULT ClrDataAccess::DumpManagedExcepObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef) |
423 | { |
424 | SUPPORTS_DAC; |
425 | |
426 | if (objRef == NULL) |
427 | { |
428 | return S_OK; |
429 | } |
430 | |
431 | if (*g_gcDacGlobals->gc_structures_invalid_cnt != 0) |
432 | { |
433 | // GC is in progress, don't dump this object |
434 | return S_OK; |
435 | } |
436 | |
437 | // write out the managed object for exception. This will only write out the |
438 | // direct field value. After this, we need to visit some selected fields, such as |
439 | // exception message and stack trace field, and dump out the object referenced via |
440 | // the fields. |
441 | // |
442 | DumpManagedObject(flags, objRef); |
443 | |
444 | // If this is not a pre-allocated exception type, then we'll need to dump out enough memory to ensure |
445 | // that the lookup codepath from the Module to information for the type of this Exception will |
446 | // be present. Simply dumping the managed object itself isn't enough. Sos doesn't need this. |
447 | EX_TRY |
448 | { |
449 | MethodTable * pMethodTable = objRef->GetGCSafeMethodTable(); |
450 | PTR_Module pModule = pMethodTable->GetModule(); |
451 | mdTypeDef exceptionTypeDef = pMethodTable->GetCl(); |
452 | |
453 | if (TypeFromToken(exceptionTypeDef) != mdtTypeDef) |
454 | { |
455 | _ASSERTE(!"Module should have contained a TypeDef, dump will likely be missing exception type lookup!" ); |
456 | } |
457 | |
458 | // The lookup from the Module that contains this TypeDef: |
459 | pModule->LookupTypeDef(RidFromToken(exceptionTypeDef)); |
460 | |
461 | // If it's a generic class, we need to implicitly enumerate the memory needed to look it up |
462 | // and enable the calls that ICD will want to make against the TypeHandle when retrieving the |
463 | // Exception info. |
464 | TypeHandle th; |
465 | th = ClassLoader::LookupTypeDefOrRefInModule(pModule, exceptionTypeDef); |
466 | th.EnumMemoryRegions(flags); |
467 | } |
468 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
469 | |
470 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
471 | // store the exception type name |
472 | EX_TRY |
473 | { |
474 | MethodTable * pMethodTable = objRef->GetGCSafeMethodTable(); |
475 | StackSString s; |
476 | TypeString::AppendType(s, TypeHandle(pMethodTable), TypeString::FormatNamespace|TypeString::FormatFullInst); |
477 | DacMdCacheAddEEName(dac_cast<TADDR>(pMethodTable), s); |
478 | } |
479 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
480 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
481 | |
482 | EXCEPTIONREF exceptRef = (EXCEPTIONREF)objRef; |
483 | |
484 | if (flags != CLRDATA_ENUM_MEM_TRIAGE) |
485 | { |
486 | // dump the exception message field |
487 | DumpManagedObject(flags, (OBJECTREF)exceptRef->GetMessage()); |
488 | } |
489 | |
490 | // dump the exception's stack trace field |
491 | DumpManagedStackTraceStringObject(flags, exceptRef->GetStackTraceString()); |
492 | |
493 | // dump the exception's remote stack trace field only if we are not generating a triage dump, or |
494 | // if we are generating a triage dump of an AppX process, or the exception type does not override |
495 | // the StackTrace getter (see Exception.InternalPreserveStackTrace to understand why) |
496 | if (flags != CLRDATA_ENUM_MEM_TRIAGE || |
497 | #ifdef FEATURE_APPX |
498 | AppX::DacIsAppXProcess() || |
499 | #endif // FEATURE_APPX |
500 | !ExceptionTypeOverridesStackTraceGetter(exceptRef->GetGCSafeMethodTable())) |
501 | { |
502 | DumpManagedStackTraceStringObject(flags, exceptRef->GetRemoteStackTraceString()); |
503 | } |
504 | |
505 | // Dump inner exception |
506 | DumpManagedExcepObject(flags, exceptRef->GetInnerException()); |
507 | |
508 | // Dump the stack trace array object and its underlying type |
509 | I1ARRAYREF stackTraceArrayObj = exceptRef->GetStackTraceArrayObject(); |
510 | |
511 | // There are cases where a managed exception does not have a stack trace. |
512 | // These cases are: |
513 | // * exception was thrown by VM and no managed frames are on the thread. |
514 | // * exception thrown is a preallocated exception. |
515 | if (stackTraceArrayObj != NULL) |
516 | { |
517 | // first dump the array's element type |
518 | TypeHandle arrayTypeHandle = stackTraceArrayObj->GetTypeHandle(); |
519 | ArrayTypeDesc* pArrayTypeDesc = arrayTypeHandle.AsArray(); |
520 | TypeHandle elementTypeHandle = pArrayTypeDesc->GetArrayElementTypeHandle(); |
521 | elementTypeHandle.AsMethodTable()->EnumMemoryRegions(flags); |
522 | elementTypeHandle.AsMethodTable()->GetClass()->EnumMemoryRegions(flags, elementTypeHandle.AsMethodTable()); |
523 | |
524 | // now dump the actual stack trace array object |
525 | DumpManagedObject(flags, (OBJECTREF)stackTraceArrayObj); |
526 | } |
527 | |
528 | // Dump the stack trace native structure. Unfortunately, we need to write out the |
529 | // native structure and also dump the MethodDesc that we care about! |
530 | // We need to ensure the entire _stackTrace from the Exception is enumerated and |
531 | // included in the dump. When we touch the header and each element looking for the |
532 | // MD this happens. |
533 | StackTraceArray stackTrace; |
534 | exceptRef->GetStackTrace(stackTrace); |
535 | for(size_t i = 0; i < stackTrace.Size(); i++) |
536 | { |
537 | MethodDesc* pMD = stackTrace[i].pFunc; |
538 | if (!DacHasMethodDescBeenEnumerated(pMD) && DacValidateMD(pMD)) |
539 | { |
540 | pMD->EnumMemoryRegions(flags); |
541 | |
542 | // The following calls are to ensure that mscordacwks!DacDbiInterfaceImpl::GetNativeCodeInfo |
543 | // will succeed for all dumps. |
544 | |
545 | // Pulls in data to translate from token to MethodDesc |
546 | FindLoadedMethodRefOrDef(pMD->GetMethodTable()->GetModule(), pMD->GetMemberDef()); |
547 | |
548 | // Pulls in sequence points. |
549 | DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMD); |
550 | PCODE addr = pMD->GetNativeCode(); |
551 | if (addr != NULL) |
552 | { |
553 | IJitManager::MethodRegionInfo methodRegionInfo = { NULL, 0, NULL, 0 }; |
554 | EECodeInfo codeInfo(addr); |
555 | codeInfo.GetMethodRegionInfo(&methodRegionInfo); |
556 | } |
557 | } |
558 | |
559 | // Enumerate the code around call site to help SOS resolve the source lines |
560 | TADDR callEnd = PCODEToPINSTR(stackTrace[i].ip); |
561 | DacEnumCodeForStackwalk(callEnd); |
562 | } |
563 | |
564 | return S_OK; |
565 | } |
566 | |
567 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
568 | // |
569 | // Helper function for skinny mini-dump |
570 | // Pass in a string object representing a managed stack trace, this function will |
571 | // dump it and "poison" the contents with a PII-free version of the stack trace. |
572 | // |
573 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
574 | HRESULT ClrDataAccess::DumpManagedStackTraceStringObject(CLRDataEnumMemoryFlags flags, STRINGREF orefStackTrace) |
575 | { |
576 | SUPPORTS_DAC; |
577 | |
578 | if (orefStackTrace == NULL) |
579 | { |
580 | return S_OK; |
581 | } |
582 | |
583 | // dump the stack trace string object |
584 | DumpManagedObject(flags, (OBJECTREF)orefStackTrace); |
585 | |
586 | if (flags == CLRDATA_ENUM_MEM_TRIAGE) |
587 | { |
588 | // StringObject::GetSString does not support DAC, use GetBuffer/GetStringLength |
589 | SString stackTrace(dac_cast<PTR_WSTR>((TADDR)orefStackTrace->GetBuffer()), orefStackTrace->GetStringLength()); |
590 | |
591 | StripFileInfoFromStackTrace(stackTrace); |
592 | |
593 | COUNT_T traceCharCount = stackTrace.GetCount(); |
594 | _ASSERTE(traceCharCount <= orefStackTrace->GetStringLength()); |
595 | |
596 | // fill the rest of the string with \0 |
597 | WCHAR *buffer = stackTrace.OpenUnicodeBuffer(orefStackTrace->GetStringLength()); |
598 | memset(buffer + traceCharCount, 0, sizeof(WCHAR) * (orefStackTrace->GetStringLength() - traceCharCount)); |
599 | |
600 | // replace the string |
601 | DacUpdateMemoryRegion(dac_cast<TADDR>(orefStackTrace) + StringObject::GetBufferOffset(), sizeof(WCHAR) * orefStackTrace->GetStringLength(), (BYTE *)buffer); |
602 | } |
603 | |
604 | return S_OK; |
605 | } |
606 | |
607 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
608 | // |
609 | // Iterating through module list and report the memory. |
610 | // Remember to call |
611 | // m_instances.DumpAllInstances(m_enumMemCb); |
612 | // when all memory enumeration are done if you call this function! |
613 | // This is because using ProcessModIter will drag in some memory implicitly. |
614 | // |
615 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
616 | HRESULT ClrDataAccess::EnumMemDumpModuleList(CLRDataEnumMemoryFlags flags) |
617 | { |
618 | SUPPORTS_DAC; |
619 | |
620 | ProcessModIter modIter; |
621 | Module* modDef; |
622 | TADDR base; |
623 | ULONG32 length; |
624 | PEFile *file; |
625 | TSIZE_T cbMemoryReported = m_cbMemoryReported; |
626 | #ifdef FEATURE_PREJIT |
627 | COUNT_T count; |
628 | #endif // FEATURE_PREJIT |
629 | |
630 | // |
631 | // Iterating through module list |
632 | // |
633 | |
634 | // Cannot use CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED around |
635 | // conditional pre-processor directives in a sane fashion |
636 | EX_TRY |
637 | { |
638 | while ((modDef = modIter.NextModule())) |
639 | { |
640 | // We also want to dump the link from the Module back to the AppDomain, |
641 | // since the stackwalker uses it to find the AD. |
642 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED |
643 | ( |
644 | // Pass false to ensure we force enumeration of this module's references. |
645 | modDef->EnumMemoryRegions(flags, false); |
646 | ); |
647 | |
648 | EX_TRY |
649 | { |
650 | // To enable a debugger to check on whether a module is an NI or IL image, they need |
651 | // the DOS header, PE headers, and IMAGE_COR20_HEADER for the Flags member. |
652 | // We expose no API today to find this out. |
653 | PTR_PEFile pPEFile = modDef->GetFile(); |
654 | PEImage * pILImage = pPEFile->GetILimage(); |
655 | PEImage * pNIImage = pPEFile->GetNativeImage(); |
656 | |
657 | // Implicitly gets the COR header. |
658 | if ((pILImage) && (pILImage->HasLoadedLayout())) |
659 | { |
660 | pILImage->GetCorHeaderFlags(); |
661 | } |
662 | if ((pNIImage) && (pNIImage->HasLoadedLayout())) |
663 | { |
664 | pNIImage->GetCorHeaderFlags(); |
665 | } |
666 | } |
667 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
668 | |
669 | |
670 | EX_TRY |
671 | { |
672 | file = modDef->GetFile(); |
673 | base = PTR_TO_TADDR(file->GetLoadedImageContents(&length)); |
674 | file->EnumMemoryRegions(flags); |
675 | #ifdef FEATURE_PREJIT |
676 | |
677 | // If module has native image and it has debug map, we need to get the debug map. |
678 | // |
679 | if (modDef->HasNativeImage() && modDef->GetNativeImage()->HasNativeDebugMap()) |
680 | { |
681 | modDef->GetNativeImage()->GetNativeDebugMap(&count); |
682 | } |
683 | #endif // FEATURE_PREJIT |
684 | } |
685 | EX_CATCH |
686 | { |
687 | // Catch the exception and keep going unless COR_E_OPERATIONCANCELED |
688 | // was thrown. Used generating dumps, where rethrow will cancel dump. |
689 | } |
690 | EX_END_CATCH(RethrowCancelExceptions) |
691 | } |
692 | } |
693 | EX_CATCH |
694 | { |
695 | // Catch the exception and keep going unless COR_E_OPERATIONCANCELED |
696 | // was thrown. Used generating dumps, where rethrow will cancel dump. |
697 | } |
698 | EX_END_CATCH(RethrowCancelExceptions) |
699 | |
700 | m_dumpStats.m_cbModuleList = m_cbMemoryReported - cbMemoryReported; |
701 | |
702 | return S_OK; |
703 | } |
704 | |
705 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
706 | // |
707 | // Iterate through AppDomains and report specific memory needed |
708 | // for all dumps, such as the Module lookup path. |
709 | // This is intended for MiniDumpNormal and should be kept small. |
710 | // |
711 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
712 | HRESULT ClrDataAccess::EnumMemDumpAppDomainInfo(CLRDataEnumMemoryFlags flags) |
713 | { |
714 | SUPPORTS_DAC; |
715 | |
716 | AppDomainIterator adIter(FALSE); |
717 | EX_TRY |
718 | { |
719 | while (adIter.Next()) |
720 | { |
721 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED |
722 | ( |
723 | // Note that the flags being CLRDATA_ENUM_MEM_MINI prevents |
724 | // you from pulling entire files loaded into memory into the dump. |
725 | adIter.GetDomain()->EnumMemoryRegions(flags, true); |
726 | ); |
727 | } |
728 | } |
729 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
730 | |
731 | return S_OK; |
732 | } |
733 | |
734 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
735 | // |
736 | // Iterating through each frame to make sure |
737 | // we dump out MethodDesc, DJI etc related info |
738 | // This is a generic helper for walking stack. However, if you call |
739 | // this function, make sure to flush instance in the DAC Instance manager. |
740 | // |
741 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
742 | HRESULT ClrDataAccess::EnumMemWalkStackHelper(CLRDataEnumMemoryFlags flags, |
743 | IXCLRDataStackWalk *pStackWalk, |
744 | Thread * pThread) |
745 | { |
746 | SUPPORTS_DAC; |
747 | |
748 | #if defined(DAC_MEASURE_PERF) |
749 | g_nStackWalk = 1; |
750 | unsigned __int64 nStart= GetCycleCount(); |
751 | #endif |
752 | |
753 | HRESULT status = S_OK; |
754 | ReleaseHolder<IXCLRDataFrame> pFrame(NULL); |
755 | ReleaseHolder<IXCLRDataMethodInstance> pMethod(NULL); |
756 | ReleaseHolder<IXCLRDataMethodDefinition> pMethodDefinition(NULL); |
757 | ReleaseHolder<IXCLRDataTypeInstance> pTypeInstance(NULL); |
758 | |
759 | MethodDesc * pMethodDesc = NULL; |
760 | EX_TRY |
761 | { |
762 | TADDR previousSP = 0; //start at zero; this allows first check to always succeed. |
763 | TADDR currentSP; |
764 | currentSP = dac_cast<TADDR>(pThread->GetCachedStackLimit()) + sizeof(TADDR); |
765 | |
766 | // exhaust the frames using DAC api |
767 | for (; status == S_OK; ) |
768 | { |
769 | bool frameHadContext = false; |
770 | status = pStackWalk->GetFrame(&pFrame); |
771 | PCODE addr = NULL; |
772 | if (status == S_OK && pFrame != NULL) |
773 | { |
774 | // write out the code that ip pointed to |
775 | T_CONTEXT context; |
776 | REGDISPLAY regDisp; |
777 | if ((status=pFrame->GetContext(CONTEXT_ALL, sizeof(T_CONTEXT), |
778 | NULL, (BYTE *)&context))==S_OK) |
779 | { |
780 | // Enumerate the code around the call site to help debugger stack walking heuristics |
781 | ::FillRegDisplay(®Disp, &context); |
782 | addr = GetControlPC(®Disp); |
783 | TADDR callEnd = PCODEToPINSTR(addr); |
784 | DacEnumCodeForStackwalk(callEnd); |
785 | frameHadContext = true; |
786 | } |
787 | |
788 | // |
789 | // There are identical stack pointer checking semantics in code:Thread::EnumMemoryRegionsWorker |
790 | // See that code for comments. |
791 | // You ***MUST*** maintain identical semantics for both checks! |
792 | // |
793 | CLRDataSimpleFrameType simpleFrameType; |
794 | CLRDataDetailedFrameType detailedFrameType; |
795 | if (SUCCEEDED(pFrame->GetFrameType(&simpleFrameType, &detailedFrameType))) |
796 | { |
797 | if (!frameHadContext) |
798 | { |
799 | _ASSERTE(!"Stack frame should always have an associated context!" ); |
800 | break; |
801 | } |
802 | |
803 | // This is StackFrameIterator::SFITER_FRAMELESS_METHOD, initialized by Code:ClrDataStackWalk::GetFrame |
804 | // from code:ClrDataStackWalk::RawGetFrameType |
805 | if (simpleFrameType == CLRDATA_SIMPFRAME_MANAGED_METHOD) |
806 | { |
807 | currentSP = (TADDR)GetRegdisplaySP(®Disp); |
808 | |
809 | if (currentSP <= previousSP) |
810 | { |
811 | _ASSERTE(!"Target stack has been corrupted, SP for current frame must be larger than previous frame." ); |
812 | break; |
813 | } |
814 | |
815 | if (currentSP % sizeof(TADDR) != 0) |
816 | { |
817 | _ASSERTE(!"Target stack has been corrupted, SP must be aligned." ); |
818 | break; |
819 | } |
820 | |
821 | if (!pThread->IsAddressInStack(currentSP)) |
822 | { |
823 | _ASSERTE(!"Target stack has been corrupted, SP must in in the stack range." ); |
824 | break; |
825 | } |
826 | } |
827 | } |
828 | else |
829 | { |
830 | _ASSERTE(!"The stack frame should always know what type it is!" ); |
831 | break; |
832 | } |
833 | |
834 | status = pFrame->GetMethodInstance(&pMethod); |
835 | if (status == S_OK && pMethod != NULL) |
836 | { |
837 | // managed frame |
838 | if (SUCCEEDED(pMethod->GetTypeInstance(&pTypeInstance)) && |
839 | (pTypeInstance != NULL)) |
840 | { |
841 | pTypeInstance.Clear(); |
842 | } |
843 | |
844 | if(SUCCEEDED(pMethod->GetDefinition(&pMethodDefinition)) && |
845 | (pMethodDefinition != NULL)) |
846 | { |
847 | pMethodDesc = ((ClrDataMethodDefinition *)pMethodDefinition.GetValue())->GetMethodDesc(); |
848 | if (pMethodDesc) |
849 | { |
850 | |
851 | // If this is a generic, we'll need to pull in enough extra info that |
852 | // we get decent results later when stackwalking. Note that we do not guarantee |
853 | // we'll always get an exact type for any reference type; most of the time the |
854 | // stack walk will just show System.__Canon, which is the level of support we |
855 | // guarantee for minidumps without full memory. |
856 | EX_TRY |
857 | { |
858 | if ((pMethodDesc->AcquiresInstMethodTableFromThis()) || |
859 | (pMethodDesc->RequiresInstMethodTableArg())) |
860 | { |
861 | // MethodTable |
862 | ReleaseHolder<IXCLRDataValue> pDV(NULL); |
863 | ReleaseHolder<IXCLRDataValue> pAssociatedValue(NULL); |
864 | CLRDATA_ADDRESS address; |
865 | PTR_Object pObjThis = NULL; |
866 | |
867 | if (SUCCEEDED(pFrame->GetArgumentByIndex(0, &pDV, 0, NULL, NULL)) && |
868 | SUCCEEDED(pDV->GetAssociatedValue(&pAssociatedValue)) && |
869 | SUCCEEDED(pAssociatedValue->GetAddress(&address))) |
870 | { |
871 | // Implicitly enumerate the object itself. |
872 | TADDR addrObjThis = CLRDATA_ADDRESS_TO_TADDR(address); |
873 | pObjThis = dac_cast<PTR_Object>(addrObjThis); |
874 | } |
875 | |
876 | // And now get the extra info we need for the AcquiresInstMethodTableFromThis case. |
877 | if (pMethodDesc->AcquiresInstMethodTableFromThis()) |
878 | { |
879 | // When working with the 'this' case, we need to pick up the MethodTable from |
880 | // object lookup. |
881 | PTR_MethodTable pMT = NULL; |
882 | if (pObjThis != NULL) |
883 | { |
884 | pMT = pObjThis->GetMethodTable(); |
885 | } |
886 | |
887 | TypeHandle th; |
888 | if (pMT != NULL) |
889 | { |
890 | th = TypeHandle(pMT); |
891 | } |
892 | |
893 | Instantiation classInst = pMethodDesc->GetExactClassInstantiation(th); |
894 | Instantiation methodInst = pMethodDesc->GetMethodInstantiation(); |
895 | } |
896 | |
897 | } |
898 | else if (pMethodDesc->RequiresInstMethodDescArg()) |
899 | { |
900 | // This method has a generic type token which is required to figure out the exact instantiation |
901 | // of the method. |
902 | // We need to to use the variable index of the generic type token in order to do the look up. |
903 | CLRDATA_ADDRESS address = NULL; |
904 | DWORD dwExactGenericArgsTokenIndex = 0; |
905 | ReleaseHolder<IXCLRDataValue> pDV(NULL); |
906 | ReleaseHolder<IXCLRDataValue> pAssociatedValue(NULL); |
907 | ReleaseHolder<IXCLRDataFrame2> pFrame2(NULL); |
908 | |
909 | if (SUCCEEDED(pFrame->QueryInterface(__uuidof(IXCLRDataFrame2), (void**)&pFrame2)) && |
910 | SUCCEEDED(pFrame2->GetExactGenericArgsToken(&pDV)) && |
911 | SUCCEEDED(pDV->GetAssociatedValue(&pAssociatedValue)) && |
912 | SUCCEEDED(pAssociatedValue->GetAddress(&address))) |
913 | { |
914 | TADDR addrMD = CLRDATA_ADDRESS_TO_TADDR(address); |
915 | PTR_MethodDesc pMD = dac_cast<PTR_MethodDesc>(addrMD); |
916 | pMD->EnumMemoryRegions(flags); |
917 | } |
918 | |
919 | pMethodDesc->EnumMemoryRegions(flags); |
920 | MethodTable * pCanonicalMT = pMethodDesc->GetCanonicalMethodTable(); |
921 | MethodTable * pNormalMT = pMethodDesc->GetMethodTable(); |
922 | pCanonicalMT->EnumMemoryRegions(flags); |
923 | pNormalMT->EnumMemoryRegions(flags); |
924 | } |
925 | } |
926 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
927 | |
928 | pMethodDesc->EnumMemoryRegions(flags); |
929 | |
930 | // The following calls are to ensure that mscordacwks!DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo |
931 | // will succeed for all dumps. Local variable info usefulness is somewhat questionable |
932 | // since most dumps will be for optimized targets. However, being able to map |
933 | // back to source lines for functions on stacks is very useful and we don't |
934 | // want to allow the function to fail for all targets. |
935 | |
936 | // Pulls in sequence points and local variable info |
937 | DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, pMethodDesc); |
938 | |
939 | #if defined(WIN64EXCEPTIONS) && defined(USE_GC_INFO_DECODER) |
940 | |
941 | if (addr != NULL) |
942 | { |
943 | EECodeInfo codeInfo(addr); |
944 | |
945 | if (codeInfo.IsValid()) |
946 | { |
947 | // We want IsFilterFunclet to work for anything on the stack |
948 | codeInfo.GetJitManager()->IsFilterFunclet(&codeInfo); |
949 | |
950 | // The stackwalker needs GC info to find the parent 'stack pointer' or PSP |
951 | GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken(); |
952 | PTR_BYTE pGCInfo = dac_cast<PTR_BYTE>(gcInfoToken.Info); |
953 | if (pGCInfo != NULL) |
954 | { |
955 | GcInfoDecoder gcDecoder(gcInfoToken, DECODE_PSP_SYM, 0); |
956 | DacEnumMemoryRegion(dac_cast<TADDR>(pGCInfo), gcDecoder.GetNumBytesRead(), true); |
957 | } |
958 | } |
959 | } |
960 | #endif // WIN64EXCEPTIONS && USE_GC_INFO_DECODER |
961 | } |
962 | pMethodDefinition.Clear(); |
963 | } |
964 | pMethod.Clear(); |
965 | } |
966 | pFrame.Clear(); |
967 | } |
968 | |
969 | previousSP = currentSP; |
970 | status = pStackWalk->Next(); |
971 | } |
972 | |
973 | } |
974 | EX_CATCH |
975 | { |
976 | status = E_FAIL; |
977 | // Catch the exception and keep going unless a COR_E_OPERATIONCANCELED |
978 | // was thrown. In which case, rethrow to cancel the dump gathering |
979 | } |
980 | EX_END_CATCH(RethrowCancelExceptions) |
981 | |
982 | #if defined(DAC_MEASURE_PERF) |
983 | unsigned __int64 nEnd = GetCycleCount(); |
984 | g_nStackTotalTime += nEnd - nStart; |
985 | g_nStackWalk = 0; |
986 | #endif // #if defined(DAC_MEASURE_PERF) |
987 | |
988 | return status; |
989 | } |
990 | |
991 | // code: ClrDataAccess::EnumMemDumpAllThreadsStack needs a trivial implementation of |
992 | // an un-DACized container class to track what exceptions have happened so far. |
993 | // It shouldn't get used anywhere else. |
994 | class DebuggingExceptionTrackerList |
995 | { |
996 | private: |
997 | |
998 | struct TrivialTADDRNode |
999 | { |
1000 | TADDR m_exceptionAddress; |
1001 | TrivialTADDRNode * m_pNext; |
1002 | |
1003 | TrivialTADDRNode(TrivialTADDRNode *pNext, TADDR address) |
1004 | : m_exceptionAddress(address), m_pNext(pNext) |
1005 | { |
1006 | SUPPORTS_DAC_HOST_ONLY; |
1007 | } |
1008 | |
1009 | private: |
1010 | TrivialTADDRNode() { _ASSERTE(!"You should never call this ctor." ); } |
1011 | }; |
1012 | |
1013 | TrivialTADDRNode *m_pHead; |
1014 | |
1015 | bool Find(TADDR address) |
1016 | { |
1017 | SUPPORTS_DAC_HOST_ONLY; |
1018 | for (TrivialTADDRNode *pFind = m_pHead; pFind != NULL; pFind = pFind->m_pNext) |
1019 | if (pFind->m_exceptionAddress == address) |
1020 | return true; |
1021 | |
1022 | return false; |
1023 | } |
1024 | |
1025 | public: |
1026 | DebuggingExceptionTrackerList() |
1027 | : m_pHead(NULL) |
1028 | { |
1029 | SUPPORTS_DAC_HOST_ONLY; |
1030 | } |
1031 | |
1032 | bool AddNewAddressOnly(TADDR address) |
1033 | { |
1034 | SUPPORTS_DAC_HOST_ONLY; |
1035 | if (Find(address)) |
1036 | { |
1037 | return false; |
1038 | } |
1039 | else |
1040 | { |
1041 | TrivialTADDRNode *pNew = new TrivialTADDRNode(m_pHead, address); |
1042 | m_pHead = pNew; |
1043 | return true; |
1044 | } |
1045 | } |
1046 | |
1047 | ~DebuggingExceptionTrackerList() |
1048 | { |
1049 | SUPPORTS_DAC_HOST_ONLY; |
1050 | for (TrivialTADDRNode *pTemp = m_pHead; m_pHead != NULL; pTemp = m_pHead) |
1051 | { |
1052 | m_pHead = m_pHead->m_pNext; |
1053 | delete pTemp; |
1054 | } |
1055 | } |
1056 | }; |
1057 | |
1058 | |
1059 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1060 | // |
1061 | // This function will walk all threads, all the context in the |
1062 | // exception state to report memory. This can also drag in memory implicitly. |
1063 | // So do call |
1064 | // m_instances.DumpAllInstances(m_enumMemCb); |
1065 | // when function is done. |
1066 | // |
1067 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1068 | HRESULT ClrDataAccess::EnumMemDumpAllThreadsStack(CLRDataEnumMemoryFlags flags) |
1069 | { |
1070 | SUPPORTS_DAC; |
1071 | |
1072 | #ifdef FEATURE_COMINTEROP |
1073 | // Dump the exception object stored in the WinRT stowed exception |
1074 | EnumMemStowedException(flags); |
1075 | #endif |
1076 | |
1077 | HRESULT status = S_OK; |
1078 | TSIZE_T cbMemoryReported = m_cbMemoryReported; |
1079 | |
1080 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1081 | |
1082 | // Duplicate the enumeration code below, to allow Exception stacks to be enumerated first. |
1083 | // These exception stacks will get MethodDesc names cached to the DacStreamManager before |
1084 | // MethodDescs residing on the "regular" callstacks |
1085 | EX_TRY |
1086 | { |
1087 | DebuggingExceptionTrackerList exceptionTrackingInner; |
1088 | |
1089 | CLRDATA_ENUM handle; |
1090 | ReleaseHolder<IXCLRDataTask> pIXCLRDataTask(NULL); |
1091 | ReleaseHolder<IXCLRDataExceptionState> pExcepState(NULL); |
1092 | Thread *pThread = NULL; |
1093 | |
1094 | // enumerating through each thread |
1095 | StartEnumTasks(&handle); |
1096 | status = EnumTask(&handle, &pIXCLRDataTask); |
1097 | for (unsigned nbThreads = 0; status == S_OK && pIXCLRDataTask != NULL; nbThreads++) |
1098 | { |
1099 | // Avoid infinite loop if target process is corrupted. |
1100 | if (nbThreads > 100000) |
1101 | { |
1102 | break; |
1103 | } |
1104 | EX_TRY |
1105 | { |
1106 | // get Thread * |
1107 | pThread = ((ClrDataTask *)pIXCLRDataTask.GetValue())->GetThread(); |
1108 | |
1109 | // dump the exception object |
1110 | DumpManagedExcepObject(flags, pThread->LastThrownObject()); |
1111 | |
1112 | // Now probe into the exception info |
1113 | status = pIXCLRDataTask->GetCurrentExceptionState(&pExcepState); |
1114 | while (status == S_OK && pExcepState != NULL) |
1115 | { |
1116 | EX_TRY |
1117 | { |
1118 | // touch the throwable in exception state |
1119 | PTR_UNCHECKED_OBJECTREF throwRef(((ClrDataExceptionState *)pExcepState.GetValue())->m_throwable); |
1120 | |
1121 | // If we've already attempted enumeration for this exception, it's time to quit. |
1122 | if (!exceptionTrackingInner.AddNewAddressOnly(throwRef.GetAddr())) |
1123 | { |
1124 | break; |
1125 | } |
1126 | |
1127 | DumpManagedExcepObject(flags, *throwRef); |
1128 | } |
1129 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1130 | |
1131 | // get the previous exception |
1132 | IXCLRDataExceptionState * pExcepStatePrev = NULL; |
1133 | status = pExcepState->GetPrevious(&pExcepStatePrev); |
1134 | |
1135 | // Release our current exception object, and transfer ref ownership of the previous |
1136 | // exception object into the holder. |
1137 | pExcepState = pExcepStatePrev; |
1138 | } |
1139 | } |
1140 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1141 | |
1142 | // get next thread |
1143 | pIXCLRDataTask.Clear(); |
1144 | status = EnumTask(&handle, &pIXCLRDataTask); |
1145 | } |
1146 | EndEnumTasks(handle); |
1147 | } |
1148 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1149 | |
1150 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1151 | |
1152 | // exceptionTracking is used for exactly that; it is a per-dump list of the |
1153 | // addresses of all exceptions enumerated for this dump. If an exception is |
1154 | // enumerated more than once it indicates that we have multiple threads pointing to |
1155 | // the same object, or the same thread has an InnerException chain with a cycle. |
1156 | // In either case, we need to terminate exception reporting. |
1157 | DebuggingExceptionTrackerList exceptionTracking; |
1158 | |
1159 | EX_TRY |
1160 | { |
1161 | CLRDATA_ENUM handle; |
1162 | ReleaseHolder<IXCLRDataTask> pIXCLRDataTask(NULL); |
1163 | ReleaseHolder<IXCLRDataExceptionState> pExcepState(NULL); |
1164 | ReleaseHolder<IXCLRDataStackWalk> pStackWalk(NULL); |
1165 | Thread *pThread = NULL; |
1166 | |
1167 | // enumerating through each thread's each frame, dump out some interesting |
1168 | // code memory needed to debugger to recognize frame |
1169 | // |
1170 | ThreadStore::EnumMemoryRegions(flags); |
1171 | |
1172 | // enumerating through each thread |
1173 | StartEnumTasks(&handle); |
1174 | status = EnumTask(&handle, &pIXCLRDataTask); |
1175 | for (unsigned nbThreads = 0; status == S_OK && pIXCLRDataTask != NULL; nbThreads++) |
1176 | { |
1177 | // Avoid infinite loop if target process is corrupted. |
1178 | if (nbThreads > 100000) |
1179 | { |
1180 | break; |
1181 | } |
1182 | EX_TRY |
1183 | { |
1184 | // get Thread * |
1185 | pThread = ((ClrDataTask *)pIXCLRDataTask.GetValue())->GetThread(); |
1186 | |
1187 | // Write out the Thread instance |
1188 | DacEnumHostDPtrMem(pThread); |
1189 | |
1190 | // @TODO |
1191 | // write TEB pointed by the thread |
1192 | // DacEnumHostDPtrMem(pThread->GetTEB()); |
1193 | |
1194 | // @TODO |
1195 | // If CLR is hosted, we want to write out fiber data |
1196 | |
1197 | // Dump the managed thread object |
1198 | DumpManagedObject(flags, pThread->GetExposedObjectRaw()); |
1199 | |
1200 | #ifndef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1201 | // dump the exception object |
1202 | DumpManagedExcepObject(flags, pThread->LastThrownObject()); |
1203 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1204 | |
1205 | // Stack Walking |
1206 | // We need for the ClrDataTask::CreateStackWalk from IXCLRDataTask to work, which is the |
1207 | // following walk. However, the CordbStackWalk code requires some different (extra) data |
1208 | // to walk the stack, such as info being present for |
1209 | // mscordacwks!DacDbiInterfaceImpl::GetNativeCodeSequencePointsAndVarInfo. |
1210 | status = pIXCLRDataTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED | CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, |
1211 | &pStackWalk); |
1212 | if (status == S_OK && pStackWalk != NULL) |
1213 | { |
1214 | status = EnumMemWalkStackHelper(flags, pStackWalk, pThread); |
1215 | pStackWalk.Clear(); |
1216 | } |
1217 | |
1218 | // Now probe into the exception info |
1219 | status = pIXCLRDataTask->GetCurrentExceptionState(&pExcepState); |
1220 | while (status == S_OK && pExcepState != NULL) |
1221 | { |
1222 | EX_TRY |
1223 | { |
1224 | // touch the throwable in exception state |
1225 | PTR_UNCHECKED_OBJECTREF throwRef(((ClrDataExceptionState *)pExcepState.GetValue())->m_throwable); |
1226 | |
1227 | // If we've already attempted enumeration for this exception, it's time to quit. |
1228 | if (!exceptionTracking.AddNewAddressOnly(throwRef.GetAddr())) |
1229 | { |
1230 | break; |
1231 | } |
1232 | |
1233 | #ifndef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1234 | DumpManagedExcepObject(flags, *throwRef); |
1235 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1236 | |
1237 | // get the type of the exception |
1238 | ReleaseHolder<IXCLRDataValue> pValue(NULL); |
1239 | status = pExcepState->GetManagedObject(&pValue); |
1240 | if (status == S_OK && pValue != NULL) |
1241 | { |
1242 | ReleaseHolder<IXCLRDataTypeInstance> pTypeInstance(NULL); |
1243 | // Make sure that we can get back a TypeInstance during inspection |
1244 | status = pValue->GetType(&pTypeInstance); |
1245 | pValue.Clear(); |
1246 | } |
1247 | |
1248 | // If Exception state has a new context, we will walk with the stashed context as well. |
1249 | // Note that in stack overflow exception's case, m_pContext is null. |
1250 | // |
1251 | // It is possible that we are in exception's catch clause when we |
1252 | // try to walk the stack below. This is a very weird situation where |
1253 | // stack is logically unwind and not physically unwind. We may not be able |
1254 | // to walk the stack correctly here. Anyway, we try to catch exception thrown |
1255 | // by bad stack walk in EnumMemWalkStackHelper. |
1256 | // |
1257 | PTR_CONTEXT pContext = ((ClrDataExceptionState*)pExcepState.GetValue())->GetCurrentContextRecord(); |
1258 | if (pContext != NULL) |
1259 | { |
1260 | T_CONTEXT newContext; |
1261 | newContext = *pContext; |
1262 | |
1263 | // We need to trigger stack walk again using the exception's context! |
1264 | status = pIXCLRDataTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED | CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE | CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE, |
1265 | &pStackWalk); |
1266 | if (status == S_OK && pStackWalk != NULL) |
1267 | { |
1268 | status = pStackWalk->SetContext2(CLRDATA_STACK_SET_CURRENT_CONTEXT, sizeof(T_CONTEXT), (BYTE *) &newContext); |
1269 | if (status == S_OK) |
1270 | { |
1271 | status = EnumMemWalkStackHelper(flags, pStackWalk, pThread); |
1272 | } |
1273 | pStackWalk.Clear(); |
1274 | } |
1275 | } |
1276 | } |
1277 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1278 | |
1279 | // get the previous exception |
1280 | IXCLRDataExceptionState * pExcepStatePrev = NULL; |
1281 | status = pExcepState->GetPrevious(&pExcepStatePrev); |
1282 | |
1283 | // Release our current exception object, and transfer ref ownership of the previous |
1284 | // exception object into the holder. |
1285 | pExcepState = pExcepStatePrev; |
1286 | } |
1287 | } |
1288 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1289 | |
1290 | // get next thread |
1291 | pIXCLRDataTask.Clear(); |
1292 | status = EnumTask(&handle, &pIXCLRDataTask); |
1293 | } |
1294 | EndEnumTasks(handle); |
1295 | } |
1296 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1297 | |
1298 | // updating the statistics |
1299 | m_dumpStats.m_cbStack = m_cbMemoryReported - cbMemoryReported; |
1300 | |
1301 | return status; |
1302 | } |
1303 | |
1304 | |
1305 | #ifdef FEATURE_COMINTEROP |
1306 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1307 | // |
1308 | // WinRT stowed exception holds the (CCW)pointer to a managed exception object. |
1309 | // We should check for the presence of a such an exception object and dump it if available. |
1310 | // This can also drag in memory implicitly. |
1311 | // So do call |
1312 | // m_instances.DumpAllInstances(m_enumMemCb); |
1313 | // when function is done. |
1314 | // |
1315 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1316 | HRESULT ClrDataAccess::EnumMemStowedException(CLRDataEnumMemoryFlags flags) |
1317 | { |
1318 | SUPPORTS_DAC; |
1319 | |
1320 | ICLRDataTarget3 *pTarget3 = GetLegacyTarget3(); |
1321 | if (pTarget3 == NULL) |
1322 | return S_OK; |
1323 | |
1324 | // get the thread that raised the exception |
1325 | ULONG32 exThreadID = 0; |
1326 | if (FAILED(pTarget3->GetExceptionThreadID(&exThreadID)) || exThreadID == 0) |
1327 | return S_OK; |
1328 | |
1329 | // |
1330 | // check that the thread is one of the known managed threads |
1331 | // |
1332 | BOOL foundThread = FALSE; |
1333 | CLRDATA_ENUM handle; |
1334 | ReleaseHolder<IXCLRDataTask> pIXCLRDataTask(NULL); |
1335 | |
1336 | // enumerate through each thread |
1337 | StartEnumTasks(&handle); |
1338 | HRESULT status = EnumTask(&handle, &pIXCLRDataTask); |
1339 | for (unsigned nbThreads = 0; status == S_OK && pIXCLRDataTask != NULL; ++nbThreads) |
1340 | { |
1341 | // Avoid infinite loop if target process is corrupted. |
1342 | if (nbThreads > 100000) |
1343 | { |
1344 | break; |
1345 | } |
1346 | EX_TRY |
1347 | { |
1348 | if (((ClrDataTask *)pIXCLRDataTask.GetValue())->GetThread()->GetOSThreadId() == exThreadID) |
1349 | { |
1350 | // found the thread |
1351 | foundThread = TRUE; |
1352 | break; |
1353 | } |
1354 | } |
1355 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1356 | |
1357 | // get next thread |
1358 | pIXCLRDataTask.Clear(); |
1359 | status = EnumTask(&handle, &pIXCLRDataTask); |
1360 | } |
1361 | EndEnumTasks(handle); |
1362 | |
1363 | if (!foundThread) |
1364 | return S_OK; |
1365 | |
1366 | |
1367 | // |
1368 | // Read the remote stowed exceptions. |
1369 | // |
1370 | // EXCEPTION_RECORD.ExceptionCode: STATUS_STOWED_EXCEPTION. |
1371 | // EXCEPTION_RECORD.NumberParameters: 2. |
1372 | // EXCEPTION_RECORD.ExceptionInformation[0]: pointer to an array of pointers |
1373 | // to STOWED_EXCEPTION_INFORMATION structures. |
1374 | // EXCEPTION_RECORD.ExceptionInformation[1]: count of elements in the array. |
1375 | // |
1376 | ULONG32 bytesRead = 0; |
1377 | MINIDUMP_EXCEPTION minidumpException = { 0 }; |
1378 | if (FAILED(pTarget3->GetExceptionRecord(sizeof(MINIDUMP_EXCEPTION), &bytesRead, (PBYTE)&minidumpException))) |
1379 | return S_OK; |
1380 | |
1381 | TADDR remoteStowedExceptionArray = (TADDR)minidumpException.ExceptionInformation[0]; |
1382 | ULONG stowedExceptionCount = (ULONG)minidumpException.ExceptionInformation[1]; |
1383 | if (bytesRead != sizeof(MINIDUMP_EXCEPTION) |
1384 | || minidumpException.ExceptionCode != STATUS_STOWED_EXCEPTION |
1385 | || minidumpException.NumberParameters != 2 |
1386 | || stowedExceptionCount < 1 // there must atleast be 1 stowed exception |
1387 | || stowedExceptionCount > 256 // upper bound: 256 |
1388 | || remoteStowedExceptionArray == NULL) |
1389 | { |
1390 | return S_OK; |
1391 | } |
1392 | |
1393 | for (ULONG i = 0; i < stowedExceptionCount; ++i) |
1394 | { |
1395 | // Read the i-th stowed exception |
1396 | TADDR remoteStowedException = NULL; |
1397 | if (FAILED(m_pTarget->ReadVirtual(TO_CDADDR(remoteStowedExceptionArray + (i * sizeof(TADDR))), |
1398 | (PBYTE)&remoteStowedException, sizeof(TADDR), &bytesRead)) |
1399 | || bytesRead != sizeof(TADDR) |
1400 | || remoteStowedException == NULL) |
1401 | { |
1402 | continue; |
1403 | } |
1404 | |
1405 | // check if this is a v2 stowed exception |
1406 | STOWED_EXCEPTION_INFORMATION_V2 stowedException = { 0 }; |
1407 | if (FAILED(m_pTarget->ReadVirtual(TO_CDADDR(remoteStowedException), |
1408 | (PBYTE)&stowedException, sizeof(STOWED_EXCEPTION_INFORMATION_HEADER), &bytesRead)) |
1409 | || bytesRead != sizeof(STOWED_EXCEPTION_INFORMATION_HEADER) |
1410 | || stowedException.Header.Signature != STOWED_EXCEPTION_INFORMATION_V2_SIGNATURE) |
1411 | { |
1412 | continue; |
1413 | } |
1414 | |
1415 | // Read the full v2 stowed exception and get the CCW pointer out of it |
1416 | if (FAILED(m_pTarget->ReadVirtual(TO_CDADDR(remoteStowedException), |
1417 | (PBYTE)&stowedException, sizeof(STOWED_EXCEPTION_INFORMATION_V2), &bytesRead)) |
1418 | || bytesRead != sizeof(STOWED_EXCEPTION_INFORMATION_V2) |
1419 | || stowedException.NestedExceptionType != STOWED_EXCEPTION_NESTED_TYPE_LEO |
1420 | || stowedException.NestedException == NULL) |
1421 | { |
1422 | continue; |
1423 | } |
1424 | |
1425 | // Find out if NestedException is a pointer to CCW and then dump the exception object in it |
1426 | DumpStowedExceptionObject(flags, TO_CDADDR(stowedException.NestedException)); |
1427 | } |
1428 | |
1429 | return S_OK; |
1430 | } |
1431 | |
1432 | HRESULT ClrDataAccess::DumpStowedExceptionObject(CLRDataEnumMemoryFlags flags, CLRDATA_ADDRESS ccwPtr) |
1433 | { |
1434 | SUPPORTS_DAC; |
1435 | if (ccwPtr == NULL) |
1436 | return S_OK; |
1437 | |
1438 | // dump the managed exception object wrapped in CCW |
1439 | // memory of the CCW object itself is dumped later by DacInstanceManager::DumpAllInstances |
1440 | DacpCCWData ccwData; |
1441 | GetCCWData(ccwPtr, &ccwData); // this call collects some memory implicitly |
1442 | DumpManagedExcepObject(flags, OBJECTREF(TO_TADDR(ccwData.managedObject))); |
1443 | |
1444 | // dump memory of the 2nd slot in the CCW's vtable |
1445 | // this is used in DACGetCCWFromAddress to identify if the passed in pointer is a valid CCW. |
1446 | ULONG32 bytesRead = 0; |
1447 | TADDR vTableAddress = NULL; |
1448 | if (FAILED(m_pTarget->ReadVirtual(ccwPtr, (PBYTE)&vTableAddress, sizeof(TADDR), &bytesRead)) |
1449 | || bytesRead != sizeof (TADDR) |
1450 | || vTableAddress == NULL) |
1451 | { |
1452 | return S_OK; |
1453 | } |
1454 | |
1455 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED |
1456 | ( |
1457 | ReportMem(vTableAddress + sizeof(PBYTE)* TEAR_OFF_SLOT, sizeof(TADDR)); |
1458 | ); |
1459 | |
1460 | return S_OK; |
1461 | } |
1462 | #endif |
1463 | |
1464 | #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory |
1465 | #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory |
1466 | |
1467 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1468 | // |
1469 | // Reports critical data from the CLR main module |
1470 | // that needs to be present in all minidumps. |
1471 | // Implicitly reports memory, so remember to call |
1472 | // m_instances.DumpAllInstances(m_enumMemCb); |
1473 | // after this function completes. |
1474 | // |
1475 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1476 | HRESULT ClrDataAccess::EnumMemCLRMainModuleInfo() |
1477 | { |
1478 | SUPPORTS_DAC; |
1479 | |
1480 | HRESULT status = S_OK; |
1481 | |
1482 | // PEDecoder is DACized, so we just need to touch what we want to |
1483 | // make subsequent lookup work. |
1484 | PEDecoder pe(m_globalBase); |
1485 | |
1486 | // We currently only actually have one debug directory entry. |
1487 | // Post-processing, such as optimization, may add an extra directory. |
1488 | // These directories are of type IMAGE_DEBUG_TYPE_RESERVED10, while our |
1489 | // standard CodeView directory with pdb info is IMAGE_DEBUG_TYPE_CODEVIEW. |
1490 | UINT i; |
1491 | for (i = 0; pe.GetDebugDirectoryEntry(i); i++) |
1492 | { |
1493 | } |
1494 | |
1495 | if (i < 1) |
1496 | { |
1497 | status = E_UNEXPECTED; |
1498 | _ASSERTE(!"Collecting dump of target with no debug directory entries!" ); |
1499 | } |
1500 | |
1501 | // For CLRv4+, the resource directory contains the necessary info |
1502 | // to retrieve the DBI/DAC from a symbol server. |
1503 | // Specifically, in v4 it contains a mscoree!PE_FIXEDFILEINFO. |
1504 | // This is also required since OpenVirtualProcess will check against |
1505 | // this content to determine if a target module is indeed a CLR |
1506 | // main module. |
1507 | |
1508 | // Retrieve all resources in clr.dll. Right now, the entire resource |
1509 | // content is very small (~0x600 bytes of raw data), so getting all is |
1510 | // the easy thing to do. If resources become larger in later |
1511 | // releases, we'll have to specifically get just the debugging-related resources. |
1512 | _ASSERTE(pe.HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)); |
1513 | if (pe.HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE)) |
1514 | { |
1515 | COUNT_T size = 0; |
1516 | TADDR pResourceDirData = pe.GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_RESOURCE, &size); |
1517 | |
1518 | _ASSERTE(size < 0x2000); |
1519 | ReportMem((TADDR)pResourceDirData, size, true); |
1520 | } |
1521 | else |
1522 | { |
1523 | // In later releases, we should log the ERROR_RESOURCE_DATA_NOT_FOUND. |
1524 | status = E_UNEXPECTED; |
1525 | } |
1526 | |
1527 | return status; |
1528 | } |
1529 | |
1530 | |
1531 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1532 | // |
1533 | // Generating skinny mini-dump. Skinny mini-dump will only support stack trace, module list, |
1534 | // and Exception list viewing. |
1535 | // |
1536 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1537 | HRESULT ClrDataAccess::EnumMemoryRegionsWorkerSkinny(IN CLRDataEnumMemoryFlags flags) |
1538 | { |
1539 | SUPPORTS_DAC; |
1540 | |
1541 | HRESULT status = S_OK; |
1542 | |
1543 | // clear all of the previous cached memory |
1544 | Flush(); |
1545 | |
1546 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1547 | // Enable caching enumerated metadata of interest |
1548 | InitStreamsForWriting(flags); |
1549 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1550 | |
1551 | //TODO: actually *do* something with potential failures. It would be relatively easy to |
1552 | // hook up an official dump stream to put info on our failures and other 'metadata' |
1553 | // about dumping into in a generic sort of way. Our code doesn't have access to |
1554 | // MDWD's callbacks, so we can't just do it ourselves. Thus we could have useful info |
1555 | // baked into the dump, like we failed to enumerate mem for certain threads, etc. |
1556 | |
1557 | // Each enumeration function below should be wrapped in a try/catch |
1558 | // so that we have a chance to create a debuggable dump in the face of target problems. |
1559 | |
1560 | // Iterating to all threads' stacks |
1561 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); ) |
1562 | |
1563 | // Iterating to module list. |
1564 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); ) |
1565 | |
1566 | // |
1567 | // iterating through static that we care |
1568 | // |
1569 | // collect CLR static |
1570 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ) |
1571 | |
1572 | // Dump AppDomain-specific info needed for MiniDumpNormal. |
1573 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) |
1574 | |
1575 | // Dump the Debugger object data needed |
1576 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger->EnumMemoryRegions(flags); ) |
1577 | |
1578 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1579 | // Dump the extra data needed for metadata-free debugging |
1580 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumStreams(flags); ) |
1581 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1582 | |
1583 | // now dump the memory get dragged in by using DAC API implicitly. |
1584 | m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); |
1585 | |
1586 | // Do not let any remaining implicitly enumerated memory leak out. |
1587 | Flush(); |
1588 | |
1589 | return S_OK; |
1590 | } |
1591 | |
1592 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1593 | // |
1594 | // Generating triage micro-dump. Triage dumps will only support stack trace |
1595 | // and Exception viewing.More than that triage dumps have to be PII free, |
1596 | // so all exception messages have to be poisoned with 0xcc mask. |
1597 | // |
1598 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1599 | HRESULT ClrDataAccess::EnumMemoryRegionsWorkerMicroTriage(IN CLRDataEnumMemoryFlags flags) |
1600 | { |
1601 | SUPPORTS_DAC; |
1602 | |
1603 | HRESULT status = S_OK; |
1604 | |
1605 | // clear all of the previous cached memory |
1606 | Flush(); |
1607 | |
1608 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1609 | // Enable caching enumerated metadata of interest |
1610 | InitStreamsForWriting(flags); |
1611 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1612 | |
1613 | // Iterating to all threads' stacks |
1614 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(flags); ) |
1615 | |
1616 | // Iterating to module list. |
1617 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(flags); ) |
1618 | |
1619 | // collect CLR static |
1620 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); ) |
1621 | |
1622 | // Dump AppDomain-specific info needed for triage dumps methods enumeration (k command). |
1623 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); ) |
1624 | |
1625 | // Dump the Debugger object data needed |
1626 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( g_pDebugger->EnumMemoryRegions(flags); ) |
1627 | |
1628 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1629 | // Dump the extra data needed for metadata-free debugging |
1630 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( EnumStreams(flags); ) |
1631 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
1632 | |
1633 | // now dump the memory get dragged in by using DAC API implicitly. |
1634 | m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); |
1635 | |
1636 | // Do not let any remaining implicitly enumerated memory leak out. |
1637 | Flush(); |
1638 | |
1639 | return S_OK; |
1640 | } |
1641 | |
1642 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1643 | // |
1644 | // Write out mscorwks's data segment. This will write out the whole |
1645 | // data segment for mscorwks. It is about 200 or 300K. Most of it (90%) are |
1646 | // vtable definition that we don't really care. But we don't have a |
1647 | // good walk to just write out all globals and statics. |
1648 | // |
1649 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1650 | HRESULT ClrDataAccess::EnumMemWriteDataSegment() |
1651 | { |
1652 | SUPPORTS_DAC; |
1653 | |
1654 | NewHolder<PEDecoder> pedecoder(NULL); |
1655 | |
1656 | EX_TRY |
1657 | { |
1658 | // Collecting mscorwks's data segment |
1659 | { |
1660 | // m_globalBase is the base address of target process's mscorwks module |
1661 | pedecoder = new PEDecoder(dac_cast<PTR_VOID>(m_globalBase)); |
1662 | |
1663 | PTR_IMAGE_SECTION_HEADER pSection = (PTR_IMAGE_SECTION_HEADER) pedecoder->FindFirstSection(); |
1664 | PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(pedecoder->GetNumberOfSections()); |
1665 | |
1666 | while (pSection < pSectionEnd) |
1667 | { |
1668 | if (pSection->Name[0] == '.' && |
1669 | pSection->Name[1] == 'd' && |
1670 | pSection->Name[2] == 'a' && |
1671 | pSection->Name[3] == 't' && |
1672 | pSection->Name[4] == 'a') |
1673 | { |
1674 | // This is the .data section of mscorwks |
1675 | ReportMem(m_globalBase + pSection->VirtualAddress, pSection->Misc.VirtualSize); |
1676 | } |
1677 | pSection++; |
1678 | } |
1679 | } |
1680 | } |
1681 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
1682 | |
1683 | return S_OK; |
1684 | } |
1685 | |
1686 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1687 | // |
1688 | // Custom Dump. Depending on the value of g_ECustomDumpFlavor, different dump |
1689 | // will be taken. You can set this global variable using hosting API |
1690 | // ICLRErrorReportingManager::BeginCustomDump. |
1691 | // |
1692 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1693 | HRESULT ClrDataAccess::EnumMemoryRegionsWorkerCustom() |
1694 | { |
1695 | SUPPORTS_DAC; |
1696 | |
1697 | HRESULT status = S_OK; |
1698 | |
1699 | ECustomDumpFlavor eFlavor; |
1700 | |
1701 | eFlavor = DUMP_FLAVOR_Default; |
1702 | |
1703 | m_enumMemFlags = CLRDATA_ENUM_MEM_MINI; |
1704 | |
1705 | // clear all of the previous cached memory |
1706 | Flush(); |
1707 | |
1708 | if (eFlavor == DUMP_FLAVOR_Mini) |
1709 | { |
1710 | // Iterating to all threads' stacks |
1711 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); ) |
1712 | |
1713 | // Iterating to module list. |
1714 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); ) |
1715 | |
1716 | // |
1717 | // iterating through static that we care |
1718 | // |
1719 | // collect CLR static |
1720 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); ) |
1721 | |
1722 | // we are done... |
1723 | |
1724 | // now dump the memory get dragged in implicitly |
1725 | m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); |
1726 | |
1727 | } |
1728 | else if (eFlavor == DUMP_FLAVOR_CriticalCLRState) |
1729 | { |
1730 | // We need to walk Threads stack to view managed frames. |
1731 | // Iterating through module list |
1732 | |
1733 | // Iterating to all threads' stacks |
1734 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAllThreadsStack(m_enumMemFlags); ) |
1735 | |
1736 | // Iterating to module list. |
1737 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpModuleList(m_enumMemFlags); ) |
1738 | |
1739 | // |
1740 | // iterating through static that we care |
1741 | // |
1742 | // collect CLR static |
1743 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); ) |
1744 | |
1745 | // Collecting some CLR secondary critical data |
1746 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(m_enumMemFlags); ) |
1747 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemWriteDataSegment(); ) |
1748 | |
1749 | // we are done... |
1750 | |
1751 | // now dump the memory get dragged in implicitly |
1752 | m_dumpStats.m_cbImplicity = m_instances.DumpAllInstances(m_enumMemCb); |
1753 | |
1754 | } |
1755 | else if (eFlavor == DUMP_FLAVOR_NonHeapCLRState) |
1756 | { |
1757 | // since all CLR hosted heap will be dump by the host, |
1758 | // the EE structures that are not loaded using LoadLibrary will |
1759 | // be included by the host. |
1760 | // |
1761 | // Thus we only need to include mscorwks's critical data and ngen images |
1762 | |
1763 | m_enumMemFlags = CLRDATA_ENUM_MEM_HEAP; |
1764 | |
1765 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(m_enumMemFlags); ) |
1766 | |
1767 | // Collecting some CLR secondary critical data |
1768 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(m_enumMemFlags); ) |
1769 | |
1770 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemWriteDataSegment(); ) |
1771 | CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCollectImages(); ) |
1772 | } |
1773 | else |
1774 | { |
1775 | status = E_INVALIDARG; |
1776 | } |
1777 | |
1778 | return S_OK; |
1779 | } |
1780 | |
1781 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1782 | // |
1783 | // Minidumps traverse a giant static calltree. We already try to catch |
1784 | // exceptions at various lower level places and continue to report memory. |
1785 | // |
1786 | // However, if we'll jump to the top-level catcher and skip the rest of the tree, |
1787 | // that may mean some key data may not get emitted to the minidump. |
1788 | // In the case that a user requests a dump is canceled, we should skip the rest |
1789 | // of the tree. When a COR_E_OPERATIONCANCELED exception is thrown, is allowed to |
1790 | // escape all the way to this function. If any exception makes it here and is not |
1791 | // COR_E_OPERATIONCANCELED that indicates an issue, and the assert is meant to catch that. |
1792 | // Unfortunately the stack unwind will already have happened. |
1793 | // |
1794 | // Internal API to support minidump and heap dump. It just delegate |
1795 | // to proper function but with a top level catch. |
1796 | // |
1797 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1798 | HRESULT ClrDataAccess::EnumMemoryRegionsWrapper(IN CLRDataEnumMemoryFlags flags) |
1799 | { |
1800 | // This is infrastructure code - we don't want DacCop complaining about the calls as a result |
1801 | // of the use of EX_CATCH_HRESULT here. We're careful to mark EnumMemoryRegionsWorkerSkinny |
1802 | // and EnumMemoryRegionsWorkerHeap as just SUPPORTS_DAC so that we still get analysis. |
1803 | SUPPORTS_DAC_HOST_ONLY; |
1804 | |
1805 | HRESULT status = S_OK; |
1806 | m_enumMemFlags = flags; |
1807 | EX_TRY |
1808 | { |
1809 | // The various EnumMemoryRegions() implementations should understand |
1810 | // CLRDATA_ENUM_MEM_MINI to mean that the bare minimimum memory |
1811 | // to make a MiniDumpNormal work should be included. |
1812 | if (flags == CLRDATA_ENUM_MEM_MINI) |
1813 | { |
1814 | // skinny mini-dump |
1815 | status = EnumMemoryRegionsWorkerSkinny(flags); |
1816 | } |
1817 | else if (flags == CLRDATA_ENUM_MEM_TRIAGE) |
1818 | { |
1819 | // triage micro-dump |
1820 | status = EnumMemoryRegionsWorkerMicroTriage(flags); |
1821 | } |
1822 | else if (flags == CLRDATA_ENUM_MEM_HEAP) |
1823 | { |
1824 | status = EnumMemoryRegionsWorkerHeap(flags); |
1825 | } |
1826 | else |
1827 | { |
1828 | _ASSERTE(!"Bad flags passing to EnumMemoryRegionsWrapper!" ); |
1829 | } |
1830 | } |
1831 | EX_CATCH_HRESULT(status); |
1832 | |
1833 | // The only exception that should reach here is the cancel exception |
1834 | _ASSERTE(SUCCEEDED(status) || status == COR_E_OPERATIONCANCELED); |
1835 | |
1836 | return status; |
1837 | } |
1838 | |
1839 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1840 | // |
1841 | // Entry function for generating CLR aware dump. This function is called |
1842 | // for minidump, heap dump, and custom dumps. CLR specific memory will |
1843 | // be reported to outer level dumper (usually dbghelp's MiniDumpWriteDump api) |
1844 | // through the callback. We do not write out to file directly. |
1845 | // |
1846 | // N.B.: The CLR may report duplicate memory chunks and it's up to |
1847 | // the debugger to coalesce memory. *However* the debugger's current |
1848 | // implementation coalesces memory we enumerate and memory that |
1849 | // they enumerate; the two sets of memory are not guaranteed to be |
1850 | // coalesced. The dump produced may thus have memory blocks in the |
1851 | // MemoryListStream that overlap or are totally contained in other blocks. |
1852 | // This issue was resolved by-design by dbgteam. Win7 #407019. |
1853 | // Note also that Memory64ListStream (when passing MiniDumpWithFullMemory) |
1854 | // will have no duplicates, be sorted, etc. In that case, none of |
1855 | // our code is called. |
1856 | // |
1857 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1858 | STDMETHODIMP |
1859 | ClrDataAccess::EnumMemoryRegions(IN ICLRDataEnumMemoryRegionsCallback* callback, |
1860 | IN ULONG32 miniDumpFlags, |
1861 | IN CLRDataEnumMemoryFlags flags) // reserved not used |
1862 | { |
1863 | SUPPORTS_DAC; |
1864 | HRESULT status; |
1865 | |
1866 | #if defined(DAC_MEASURE_PERF) |
1867 | |
1868 | g_nTotalTime = 0; |
1869 | g_nStackTotalTime = 0; |
1870 | g_nReadVirtualTotalTime = 0; |
1871 | g_nFindTotalTime = 0; |
1872 | g_nFindHashTotalTime = 0; |
1873 | g_nFindHits = 0; |
1874 | g_nFindCalls = 0; |
1875 | g_nFindFails = 0; |
1876 | g_nStackWalk = 0; |
1877 | g_nFindStackTotalTime = 0; |
1878 | |
1879 | LARGE_INTEGER nClockFrequency; |
1880 | unsigned __int64 nStart = 0; |
1881 | unsigned __int64 nEnd = 0; |
1882 | |
1883 | QueryPerformanceFrequency(&nClockFrequency); |
1884 | |
1885 | FILE* fp = fopen("c:\\dumpLog.txt" , "a" ); |
1886 | if (fp) |
1887 | { |
1888 | fprintf(fp, "\nMinidumpFlags = %d\n" , miniDumpFlags); |
1889 | fclose(fp); |
1890 | } |
1891 | |
1892 | nStart = GetCycleCount(); |
1893 | |
1894 | #endif // #if defined(DAC_MEASURE_PERF) |
1895 | |
1896 | DAC_ENTER(); |
1897 | |
1898 | // We should not be trying to enumerate while we have an enumeration outstanding |
1899 | _ASSERTE(m_enumMemCb==NULL); |
1900 | m_enumMemCb = callback; |
1901 | |
1902 | // QI for ICLRDataEnumMemoryRegionsCallback2 will succeed only for Win8+. |
1903 | // It is expected to fail on pre Win8 OSes. |
1904 | callback->QueryInterface(IID_ICLRDataEnumMemoryRegionsCallback2, (void **)&m_updateMemCb); |
1905 | |
1906 | EX_TRY |
1907 | { |
1908 | ClearDumpStats(); |
1909 | if (miniDumpFlags & MiniDumpWithPrivateReadWriteMemory) |
1910 | { |
1911 | // heap dump |
1912 | status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_HEAP); |
1913 | } |
1914 | else if (miniDumpFlags & MiniDumpWithFullAuxiliaryState) |
1915 | { |
1916 | // This is the host custom dump. |
1917 | status = EnumMemoryRegionsWorkerCustom(); |
1918 | } |
1919 | else if (miniDumpFlags & MiniDumpFilterTriage) |
1920 | { |
1921 | // triage micro-dump |
1922 | status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_TRIAGE); |
1923 | } |
1924 | else |
1925 | { |
1926 | // minidump |
1927 | status = EnumMemoryRegionsWrapper(CLRDATA_ENUM_MEM_MINI); |
1928 | } |
1929 | |
1930 | #ifndef FEATURE_PAL |
1931 | // For all dump types, we need to capture the chain to the IMAGE_DIRECTORY_ENTRY_DEBUG |
1932 | // contents, so that DAC can validate against the TimeDateStamp even if the |
1933 | // debugger can't find the main CLR module on disk. |
1934 | // If we already failed, don't bother. |
1935 | if (SUCCEEDED(status)) |
1936 | { |
1937 | // In case there's implicitly enumerated memory hanging around |
1938 | // let's not accidentally pick it up. |
1939 | Flush(); |
1940 | if (SUCCEEDED(status = EnumMemCLRMainModuleInfo())) |
1941 | { |
1942 | m_instances.DumpAllInstances(m_enumMemCb); |
1943 | } |
1944 | } |
1945 | #endif |
1946 | Flush(); |
1947 | } |
1948 | EX_CATCH |
1949 | { |
1950 | m_enumMemCb = NULL; |
1951 | |
1952 | // We should never actually be here b/c none of the EMR functions should throw. |
1953 | // They should all either be written robustly w/ ptr.IsValid() and catching their |
1954 | // own exceptions. |
1955 | if (!DacExceptionFilter(GET_EXCEPTION(), this, &status)) |
1956 | { |
1957 | _ASSERTE_MSG(false, "Got unexpected exception in EnumMemoryRegions" ); |
1958 | EX_RETHROW; |
1959 | } |
1960 | } |
1961 | EX_END_CATCH(SwallowAllExceptions) |
1962 | |
1963 | // fix for issue 866100: DAC is too late in releasing ICLRDataEnumMemoryRegionsCallback2* |
1964 | if (m_updateMemCb) |
1965 | { |
1966 | m_updateMemCb->Release(); |
1967 | m_updateMemCb = NULL; |
1968 | } |
1969 | m_enumMemCb = NULL; |
1970 | |
1971 | DAC_LEAVE(); |
1972 | |
1973 | #if defined(DAC_MEASURE_PERF) |
1974 | |
1975 | nEnd = GetCycleCount(); |
1976 | g_nTotalTime= nEnd - nStart; |
1977 | fp = fopen("c:\\dumpLog.txt" , "a" ); |
1978 | fprintf(fp, "Total = %g msec\n" |
1979 | "ReadVirtual = %g msec\n" |
1980 | "StackWalk = %g msec; Find: %g msec\n" |
1981 | "Find = %g msec; Hash = %g msec; Calls = %I64u; Hits = %I64u; Not found = %I64u\n\n=====\n" , |
1982 | (float) (1000*g_nTotalTime/nClockFrequency.QuadPart), |
1983 | (float) (1000*g_nReadVirtualTotalTime/nClockFrequency.QuadPart), |
1984 | (float) (1000*g_nStackTotalTime/nClockFrequency.QuadPart), (float) (1000*g_nFindStackTotalTime/nClockFrequency.QuadPart), |
1985 | (float) (1000*g_nFindTotalTime/nClockFrequency.QuadPart), (float) (1000*g_nFindHashTotalTime/nClockFrequency.QuadPart), |
1986 | g_nFindCalls, g_nFindHits, g_nFindFails |
1987 | ); |
1988 | fclose(fp); |
1989 | |
1990 | #endif // #if defined(DAC_MEASURE_PERF) |
1991 | |
1992 | return status; |
1993 | } |
1994 | |
1995 | |
1996 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1997 | // |
1998 | // Clear the statistics for the dump. For each dump generation, we |
1999 | // clear the dump statistics. At the end of the dump generation, you can |
2000 | // view the statics data member m_dumpStats and see how many bytes that |
2001 | // we have reported to our debugger host. |
2002 | // |
2003 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2004 | void ClrDataAccess::ClearDumpStats() |
2005 | { |
2006 | SUPPORTS_DAC; |
2007 | |
2008 | m_cbMemoryReported = 0; |
2009 | memset(&m_dumpStats, 0, sizeof(DumpMemoryReportStatics)); |
2010 | } |
2011 | |