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
34unsigned __int64 g_nTotalTime;
35unsigned __int64 g_nStackTotalTime;
36unsigned __int64 g_nReadVirtualTotalTime;
37unsigned __int64 g_nFindTotalTime;
38unsigned __int64 g_nFindHashTotalTime;
39unsigned __int64 g_nFindHits;
40unsigned __int64 g_nFindCalls;
41unsigned __int64 g_nFindFails;
42unsigned __int64 g_nStackWalk;
43unsigned __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//
64HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
154HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
186HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
312HRESULT 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//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
362HRESULT 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//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
422HRESULT 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//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
574HRESULT 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//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
616HRESULT 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//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
712HRESULT 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//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
742HRESULT 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(&regDisp, &context);
782 addr = GetControlPC(&regDisp);
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(&regDisp);
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.
994class DebuggingExceptionTrackerList
995{
996private:
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
1025public:
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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1068HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1316HRESULT 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
1432HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1476HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1537HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1599HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1650HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1693HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1798HRESULT 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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1858STDMETHODIMP
1859ClrDataAccess::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//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2004void ClrDataAccess::ClearDumpStats()
2005{
2006 SUPPORTS_DAC;
2007
2008 m_cbMemoryReported = 0;
2009 memset(&m_dumpStats, 0, sizeof(DumpMemoryReportStatics));
2010}
2011