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.
5 // File: eventtrace.cpp
6 // Abstract: This module implements Event Tracing support
12 // ============================================================================
16 #ifdef FEATURE_REDHAWK
18 #include "commontypes.h"
20 #include "debugmacrosext.h"
21 #include "palredhawkcommon.h"
23 #define Win32EventWrite PalEtwEventWrite
24 #define InterlockedExchange64 PalInterlockedExchange64
26 #else // !FEATURE_REDHAWK
28 #include "eventtrace.h"
32 #include "dbginterface.h"
33 #include "finalizerthread.h"
35 #define Win32EventWrite EventWrite
37 #ifdef FEATURE_COMINTEROP
38 #include "comcallablewrapper.h"
39 #include "runtimecallablewrapper.h"
42 // Flags used to store some runtime information for Event Tracing
43 BOOL g_fEEOtherStartup=FALSE;
44 BOOL g_fEEComActivatedStartup=FALSE;
45 GUID g_EEComObjectGuid=GUID_NULL;
47 BOOL g_fEEHostedStartup = FALSE;
49 #endif // FEATURE_REDHAWK
51 #include "eventtracepriv.h"
53 #ifdef FEATURE_REDHAWK
54 volatile LONGLONG ETW::GCLog::s_l64LastClientSequenceNumber = 0;
55 #else // FEATURE_REDHAWK
56 Volatile<LONGLONG> ETW::GCLog::s_l64LastClientSequenceNumber = 0;
57 #endif // FEATURE_REDHAWK
59 #ifndef FEATURE_REDHAWK
61 //---------------------------------------------------------------------------------------
62 // Helper macros to determine which version of the Method events to use
64 // The V2 versions of these events include the ReJITID, the V1 versions do not.
65 // Historically, when we version events, we'd just stop sending the old version and only
66 // send the new one. However, now that we have xperf in heavy use internally and soon to be
67 // used externally, we need to be a bit careful. In particular, we'd like to allow
68 // current xperf to continue working without knowledge of ReJITIDs, and allow future
69 // xperf to decode symbols in ReJITted functions. Thus,
70 // * During a first-JIT, only issue the existing V1 MethodLoad, etc. events (NOT v0,
71 // NOT v2). This event does not include a ReJITID, and can thus continue to be
72 // parsed by older decoders.
73 // * During a rejit, only issue the new V2 events (NOT v0 or v1), which will include a
74 // nonzero ReJITID. Thus, your unique key for a method extent would be MethodID +
75 // ReJITID + extent (hot/cold). These events will be ignored by older decoders
76 // (including current xperf) because of the version number, but xperf will be
77 // updated to decode these in the future.
79 #define FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
82 { FireEtwMethodLoadVerbose_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
84 { FireEtwMethodLoadVerbose_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
87 #define FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, rejitID) \
90 { FireEtwMethodLoad_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID); } \
92 { FireEtwMethodLoad_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, rejitID); } \
95 #define FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
98 { FireEtwMethodUnloadVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
100 { FireEtwMethodUnloadVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
103 #define FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \
106 { FireEtwMethodUnload_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
108 { FireEtwMethodUnload_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \
111 #define FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
114 { FireEtwMethodDCStartVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
116 { FireEtwMethodDCStartVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
119 #define FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \
122 { FireEtwMethodDCStart_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
124 { FireEtwMethodDCStart_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \
127 #define FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
130 { FireEtwMethodDCEndVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
132 { FireEtwMethodDCEndVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
135 #define FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \
138 { FireEtwMethodDCEnd_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
140 { FireEtwMethodDCEnd_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \
143 // Module load / unload events:
145 #define FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \
146 FireEtwModuleLoad_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath)
147 #define FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \
148 FireEtwModuleUnload_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath)
149 #define FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \
150 FireEtwModuleDCStart_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath)
151 #define FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath) \
152 FireEtwModuleDCEnd_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, clrInstanceId, ManagedPdbSignature, ManagedPdbAge, ManagedPdbPath, NativePdbSignature, NativePdbAge, NativePdbPath)
156 //---------------------------------------------------------------------------------------
158 // Rather than checking the NGEN keyword on the runtime provider directly, use this
159 // helper that checks that the NGEN runtime provider keyword is enabled AND the
160 // OverrideAndSuppressNGenEvents keyword on the runtime provider is NOT enabled.
162 // OverrideAndSuppressNGenEvents allows controllers to set the expensive NGEN keyword for
163 // older runtimes (< 4.0) where NGEN PDB info is NOT available, while suppressing those
164 // expensive events on newer runtimes (>= 4.5) where NGEN PDB info IS available. Note
165 // that 4.0 has NGEN PDBS but unfortunately not the OverrideAndSuppressNGenEvents
166 // keyword, b/c NGEN PDBs were made publicly only after 4.0 shipped. So tools that need
167 // to consume both <4.0 and 4.0 events would neeed to enable the expensive NGEN events to
168 // deal properly with 3.5, even though those events aren't necessary on 4.0.
170 // On CoreCLR, this keyword is a no-op, because coregen PDBs don't exist (and thus we'll
171 // need the NGEN rundown to still work on Silverligth).
174 // nonzero iff NGenKeyword is enabled on the runtime provider and
175 // OverrideAndSuppressNGenEventsKeyword is not enabled on the runtime provider.
178 BOOL IsRuntimeNgenKeywordEnabledAndNotSuppressed()
180 LIMITED_METHOD_CONTRACT;
184 ETW_TRACING_CATEGORY_ENABLED(
185 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
186 TRACE_LEVEL_INFORMATION,
188 && ! ( ETW_TRACING_CATEGORY_ENABLED(
189 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
190 TRACE_LEVEL_INFORMATION,
191 CLR_OVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD) )
195 // Same as above, but for the rundown provider
196 BOOL IsRundownNgenKeywordEnabledAndNotSuppressed()
198 LIMITED_METHOD_CONTRACT;
201 #ifdef FEATURE_PERFTRACING
202 EventPipeHelper::Enabled() ||
203 #endif // FEATURE_PERFTRACING
205 ETW_TRACING_CATEGORY_ENABLED(
206 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
207 TRACE_LEVEL_INFORMATION,
208 CLR_RUNDOWNNGEN_KEYWORD)
209 && ! ( ETW_TRACING_CATEGORY_ENABLED(
210 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
211 TRACE_LEVEL_INFORMATION,
212 CLR_RUNDOWNOVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD) )
216 /*******************************************************/
217 /* Fast assembly function to get the topmost EBP frame */
218 /*******************************************************/
219 #if defined(_TARGET_X86_)
222 CallStackFrame* GetEbp()
224 CallStackFrame *frame=NULL;
232 #endif //_TARGET_X86_
234 /*************************************/
235 /* Function to append a frame to an existing stack */
236 /*************************************/
237 #if !defined(FEATURE_PAL)
238 void ETW::SamplingLog::Append(SIZE_T currentFrame)
240 LIMITED_METHOD_CONTRACT;
241 if(m_FrameCount < (ETW::SamplingLog::s_MaxStackSize-1) &&
244 m_EBPStack[m_FrameCount] = currentFrame;
249 /********************************************************/
250 /* Function to get the callstack on the current thread */
251 /********************************************************/
252 ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::GetCurrentThreadsCallStack(UINT32 *frameCount, PVOID **Stack)
263 // The stack walk performed below can cause allocations (thus entering the host). But
264 // this is acceptable, since we're not supporting the use of SQL/F1 profiling and
265 // full-blown ETW CLR stacks (which would be redundant).
266 PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
269 ETW::SamplingLog::EtwStackWalkStatus stackwalkStatus = SaveCurrentStack();
271 _ASSERTE(m_FrameCount < ETW::SamplingLog::s_MaxStackSize);
273 // this not really needed, but let's do it
274 // because we use the framecount while dumping the stack event
275 for(int i=m_FrameCount; i<ETW::SamplingLog::s_MaxStackSize; i++)
279 // This is for consumers to work correctly because the number of
280 // frames in the manifest file is specified to be 2
284 *frameCount = m_FrameCount;
285 *Stack = (PVOID *)m_EBPStack;
286 return stackwalkStatus;
289 /*************************************/
290 /* Function to save the stack on the current thread */
291 /*************************************/
292 ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::SaveCurrentStack(int skipTopNFrames)
303 if (!IsGarbageCollectorFullyInitialized())
305 // If the GC isn't ready yet, then there won't be any interesting
306 // managed code on the stack to walk. Plus, the stack walk itself may
307 // hit problems (e.g., when calling into the code manager) if it's run
308 // too early during startup.
309 return ETW::SamplingLog::UnInitialized;
311 #ifndef DACCESS_COMPILE
312 #ifdef _TARGET_AMD64_
313 if (RtlVirtualUnwind_Unsafe == NULL)
315 // We haven't even set up the RtlVirtualUnwind function pointer yet,
316 // so it's too early to try stack walking.
317 return ETW::SamplingLog::UnInitialized;
319 #endif // _TARGET_AMD64_
320 Thread *pThread = GetThread();
323 return ETW::SamplingLog::UnInitialized;
325 // The thread should not have a hijack set up or we can't walk the stack.
326 if (pThread->m_State & Thread::TS_Hijacked) {
327 return ETW::SamplingLog::UnInitialized;
329 if (pThread->IsEtwStackWalkInProgress())
331 return ETW::SamplingLog::InProgress;
333 pThread->MarkEtwStackWalkInProgress();
337 CallStackFrame *currentEBP = GetEbp();
338 CallStackFrame *lastEBP = NULL;
340 // The EBP stack walk below is meant to be extremely fast. It does not attempt to protect
341 // against cases of stack corruption. *BUT* it does need to validate a "sane" EBP chain.
343 // Ensure the EBP in the starting frame is "reasonable" (i.e. above the address of a local)
344 if ((SIZE_T) currentEBP > (SIZE_T)¤tEBP)
348 lastEBP = currentEBP;
349 currentEBP = currentEBP->m_Next;
351 // Check for stack upper limit; we don't check the lower limit on each iteration
352 // (we did it at the top) and each subsequent value in the loop is larger than
353 // the previous (see the check "currentEBP < lastEBP" below)
354 if((SIZE_T)currentEBP > (SIZE_T)Thread::GetStackUpperBound())
359 // If we have a too small address, we are probably bad
360 if((SIZE_T)currentEBP < (SIZE_T)0x10000)
363 if((SIZE_T)currentEBP < (SIZE_T)lastEBP)
368 // Skip the top N frames
374 // Save the Return Address for symbol decoding
375 Append(lastEBP->m_ReturnAddress);
380 ClrCaptureContext(&ctx);
381 UINT_PTR ControlPc = 0;
382 UINT_PTR CurrentSP = 0, PrevSP = 0;
386 // Unwind to the caller
387 ControlPc = Thread::VirtualUnwindCallFrame(&ctx);
389 // This is to take care of recursion
390 CurrentSP = (UINT_PTR)GetSP(&ctx);
392 // when to break from this loop
393 if ( ControlPc == 0 || ( PrevSP == CurrentSP ) )
398 // Skip the top N frames
399 if ( skipTopNFrames ) {
404 // Add the stack frame to the list
409 #endif //_TARGET_X86_
410 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
411 pThread->MarkEtwStackWalkCompleted();
412 #endif //!DACCESS_COMPILE
414 return ETW::SamplingLog::Completed;
417 #endif // !defined(FEATURE_PAL)
418 #endif // !FEATURE_REDHAWK
420 /****************************************************************************/
421 /* Methods that are called from the runtime */
422 /****************************************************************************/
424 /****************************************************************************/
425 /* Methods for rundown events */
426 /****************************************************************************/
428 /***************************************************************************/
429 /* This function should be called from the event tracing callback routine
430 when the private CLR provider is enabled */
431 /***************************************************************************/
433 #ifndef FEATURE_REDHAWK
435 VOID ETW::GCLog::GCSettingsEvent()
437 if (GCHeapUtilities::IsGCHeapInitialized())
439 if (ETW_TRACING_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
442 ETW::GCLog::ETW_GC_INFO Info;
444 Info.GCSettings.ServerGC = GCHeapUtilities::IsServerHeap ();
445 Info.GCSettings.SegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (false);
446 Info.GCSettings.LargeObjectSegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (true);
447 FireEtwGCSettings_V1(Info.GCSettings.SegmentSize, Info.GCSettings.LargeObjectSegmentSize, Info.GCSettings.ServerGC, GetClrInstanceId());
449 GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments();
453 #endif // !FEATURE_REDHAWK
456 //---------------------------------------------------------------------------------------
457 // Code for sending GC heap object events is generally the same for both FEATURE_REDHAWK
458 // and !FEATURE_REDHAWK builds
459 //---------------------------------------------------------------------------------------
461 bool s_forcedGCInProgress = false;
465 ForcedGCHolder() { LIMITED_METHOD_CONTRACT; s_forcedGCInProgress = true; }
466 ~ForcedGCHolder() { LIMITED_METHOD_CONTRACT; s_forcedGCInProgress = false; }
469 BOOL ETW::GCLog::ShouldWalkStaticsAndCOMForEtw()
471 LIMITED_METHOD_CONTRACT;
473 return s_forcedGCInProgress &&
474 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
475 TRACE_LEVEL_INFORMATION,
476 CLR_GCHEAPDUMP_KEYWORD);
479 // Simple helpers called by the GC to decide whether it needs to do a walk of heap
480 // objects and / or roots.
482 BOOL ETW::GCLog::ShouldWalkHeapObjectsForEtw()
484 LIMITED_METHOD_CONTRACT;
485 return s_forcedGCInProgress &&
486 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
487 TRACE_LEVEL_INFORMATION,
488 CLR_GCHEAPDUMP_KEYWORD);
491 BOOL ETW::GCLog::ShouldWalkHeapRootsForEtw()
493 LIMITED_METHOD_CONTRACT;
494 return s_forcedGCInProgress &&
495 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
496 TRACE_LEVEL_INFORMATION,
497 CLR_GCHEAPDUMP_KEYWORD);
500 BOOL ETW::GCLog::ShouldTrackMovementForEtw()
502 LIMITED_METHOD_CONTRACT;
503 return ETW_TRACING_CATEGORY_ENABLED(
504 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
505 TRACE_LEVEL_INFORMATION,
506 CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD);
509 // Batches the list of moved/surviving references for the GCBulkMovedObjectRanges /
510 // GCBulkSurvivingObjectRanges events
511 struct EtwGcMovementContext
514 // An instance of EtwGcMovementContext is dynamically allocated and stored
515 // inside of MovedReferenceContextForEtwAndProfapi, which in turn is dynamically
516 // allocated and pointed to by a profiling_context pointer created by the GC on the stack.
517 // This is used to batch and send GCBulkSurvivingObjectRanges events and
518 // GCBulkMovedObjectRanges events. This method is passed a pointer to
519 // MovedReferenceContextForEtwAndProfapi::pctxEtw; if non-NULL it gets returned;
520 // else, a new EtwGcMovementContext is allocated, stored in that pointer, and
521 // then returned. Callers should test for NULL, which can be returned if out of
523 static EtwGcMovementContext * GetOrCreateInGCContext(EtwGcMovementContext ** ppContext)
525 LIMITED_METHOD_CONTRACT;
527 _ASSERTE(ppContext != NULL);
529 EtwGcMovementContext * pContext = *ppContext;
530 if (pContext == NULL)
532 pContext = new (nothrow) EtwGcMovementContext;
533 *ppContext = pContext;
538 EtwGcMovementContext() :
539 iCurBulkSurvivingObjectRanges(0),
540 iCurBulkMovedObjectRanges(0)
542 LIMITED_METHOD_CONTRACT;
546 // Resets structure for reuse on construction, and after each flush.
547 // (Intentionally leave iCurBulk* as is, since they persist across flushes within a GC.)
550 LIMITED_METHOD_CONTRACT;
551 cBulkSurvivingObjectRanges = 0;
552 cBulkMovedObjectRanges = 0;
553 ZeroMemory(rgGCBulkSurvivingObjectRanges, sizeof(rgGCBulkSurvivingObjectRanges));
554 ZeroMemory(rgGCBulkMovedObjectRanges, sizeof(rgGCBulkMovedObjectRanges));
557 //---------------------------------------------------------------------------------------
558 // GCBulkSurvivingObjectRanges
559 //---------------------------------------------------------------------------------------
561 // Sequence number for each GCBulkSurvivingObjectRanges event
562 UINT iCurBulkSurvivingObjectRanges;
564 // Number of surviving object ranges currently filled out in rgGCBulkSurvivingObjectRanges array
565 UINT cBulkSurvivingObjectRanges;
567 // Struct array containing the primary data for each GCBulkSurvivingObjectRanges
568 // event. Fix the size so the total event stays well below the 64K limit (leaving
569 // lots of room for non-struct fields that come before the values data)
570 EventStructGCBulkSurvivingObjectRangesValue rgGCBulkSurvivingObjectRanges[
571 (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkSurvivingObjectRangesValue)];
573 //---------------------------------------------------------------------------------------
574 // GCBulkMovedObjectRanges
575 //---------------------------------------------------------------------------------------
577 // Sequence number for each GCBulkMovedObjectRanges event
578 UINT iCurBulkMovedObjectRanges;
580 // Number of Moved object ranges currently filled out in rgGCBulkMovedObjectRanges array
581 UINT cBulkMovedObjectRanges;
583 // Struct array containing the primary data for each GCBulkMovedObjectRanges
584 // event. Fix the size so the total event stays well below the 64K limit (leaving
585 // lots of room for non-struct fields that come before the values data)
586 EventStructGCBulkMovedObjectRangesValue rgGCBulkMovedObjectRanges[
587 (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkMovedObjectRangesValue)];
590 // Contains above struct for ETW, plus extra info (opaque to us) used by the profiling
591 // API to track its own information.
592 struct MovedReferenceContextForEtwAndProfapi
594 // An instance of MovedReferenceContextForEtwAndProfapi is dynamically allocated and
595 // pointed to by a profiling_context pointer created by the GC on the stack. This is used to
596 // batch and send GCBulkSurvivingObjectRanges events and GCBulkMovedObjectRanges
597 // events and the corresponding callbacks for profapi profilers. This method is
598 // passed a pointer to a MovedReferenceContextForEtwAndProfapi; if non-NULL it gets
599 // returned; else, a new MovedReferenceContextForEtwAndProfapi is allocated, stored
600 // in that pointer, and then returned. Callers should test for NULL, which can be
601 // returned if out of memory
602 static MovedReferenceContextForEtwAndProfapi * CreateInGCContext(LPVOID pvContext)
604 LIMITED_METHOD_CONTRACT;
606 _ASSERTE(pvContext != NULL);
608 MovedReferenceContextForEtwAndProfapi * pContext = *(MovedReferenceContextForEtwAndProfapi **) pvContext;
610 // Shouldn't be called if the context was already created. Perhaps someone made
611 // one too many BeginMovedReferences calls, or didn't have an EndMovedReferences
613 _ASSERTE(pContext == NULL);
615 pContext = new (nothrow) MovedReferenceContextForEtwAndProfapi;
616 *(MovedReferenceContextForEtwAndProfapi **) pvContext = pContext;
622 MovedReferenceContextForEtwAndProfapi() :
627 LIMITED_METHOD_CONTRACT;
631 EtwGcMovementContext * pctxEtw;
635 //---------------------------------------------------------------------------------------
637 // Called by the GC for each moved or surviving reference that it encounters. This
638 // batches the info into our context's buffer, and flushes that buffer to ETW as it fills
642 // * pbMemBlockStart - Start of moved/surviving block
643 // * pbMemBlockEnd - Next pointer after end of moved/surviving block
644 // * cbRelocDistance - How far did the block move? (0 for non-compacted / surviving
645 // references; negative if moved to earlier addresses)
646 // * profilingContext - Where our context is stored
647 // * fCompacting - Is this a compacting GC? Used to decide whether to send the moved
648 // or surviving event
652 void ETW::GCLog::MovedReference(
653 BYTE * pbMemBlockStart,
654 BYTE * pbMemBlockEnd,
655 ptrdiff_t cbRelocDistance,
656 size_t profilingContext,
658 BOOL fAllowProfApiNotification /* = TRUE */)
665 CAN_TAKE_LOCK; // EEToProfInterfaceImpl::AllocateMovedReferencesData takes lock
669 MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi =
670 (MovedReferenceContextForEtwAndProfapi *) profilingContext;
671 if (pCtxForEtwAndProfapi == NULL)
673 _ASSERTE(!"MovedReference() encountered a NULL profilingContext");
677 #ifdef PROFILING_SUPPORTED
679 if (fAllowProfApiNotification)
681 BEGIN_PIN_PROFILER(CORProfilerTrackGC());
682 g_profControlBlock.pProfInterface->MovedReference(pbMemBlockStart,
685 &(pCtxForEtwAndProfapi->pctxProfAPI),
689 #endif // PROFILING_SUPPORTED
693 if (!ShouldTrackMovementForEtw())
696 EtwGcMovementContext * pContext =
697 EtwGcMovementContext::GetOrCreateInGCContext(&pCtxForEtwAndProfapi->pctxEtw);
698 if (pContext == NULL)
705 _ASSERTE(pContext->cBulkMovedObjectRanges < _countof(pContext->rgGCBulkMovedObjectRanges));
706 EventStructGCBulkMovedObjectRangesValue * pValue =
707 &pContext->rgGCBulkMovedObjectRanges[pContext->cBulkMovedObjectRanges];
708 pValue->OldRangeBase = pbMemBlockStart;
709 pValue->NewRangeBase = pbMemBlockStart + cbRelocDistance;
710 pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart;
711 pContext->cBulkMovedObjectRanges++;
713 // If buffer is now full, empty it into ETW
714 if (pContext->cBulkMovedObjectRanges == _countof(pContext->rgGCBulkMovedObjectRanges))
716 FireEtwGCBulkMovedObjectRanges(
717 pContext->iCurBulkMovedObjectRanges,
718 pContext->cBulkMovedObjectRanges,
720 sizeof(pContext->rgGCBulkMovedObjectRanges[0]),
721 &pContext->rgGCBulkMovedObjectRanges[0]);
723 pContext->iCurBulkMovedObjectRanges++;
729 // Surviving references
731 _ASSERTE(pContext->cBulkSurvivingObjectRanges < _countof(pContext->rgGCBulkSurvivingObjectRanges));
732 EventStructGCBulkSurvivingObjectRangesValue * pValue =
733 &pContext->rgGCBulkSurvivingObjectRanges[pContext->cBulkSurvivingObjectRanges];
734 pValue->RangeBase = pbMemBlockStart;
735 pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart;
736 pContext->cBulkSurvivingObjectRanges++;
738 // If buffer is now full, empty it into ETW
739 if (pContext->cBulkSurvivingObjectRanges == _countof(pContext->rgGCBulkSurvivingObjectRanges))
741 FireEtwGCBulkSurvivingObjectRanges(
742 pContext->iCurBulkSurvivingObjectRanges,
743 pContext->cBulkSurvivingObjectRanges,
745 sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]),
746 &pContext->rgGCBulkSurvivingObjectRanges[0]);
748 pContext->iCurBulkSurvivingObjectRanges++;
755 //---------------------------------------------------------------------------------------
757 // Called by the GC just before it begins enumerating plugs. Gives us a chance to
758 // allocate our context structure, to allow us to batch plugs before firing events
762 // * pProfilingContext - Points to location on stack (in GC function) where we can
763 // store a pointer to the context we allocate
767 VOID ETW::GCLog::BeginMovedReferences(size_t * pProfilingContext)
769 LIMITED_METHOD_CONTRACT;
771 MovedReferenceContextForEtwAndProfapi::CreateInGCContext(LPVOID(pProfilingContext));
775 //---------------------------------------------------------------------------------------
777 // Called by the GC at the end of a heap walk to give us a place to flush any remaining
778 // buffers of data to ETW or the profapi profiler
781 // profilingContext - Our context we built up during the heap walk
785 VOID ETW::GCLog::EndMovedReferences(size_t profilingContext, BOOL fAllowProfApiNotification /* = TRUE */)
796 MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi = (MovedReferenceContextForEtwAndProfapi *) profilingContext;
797 if (pCtxForEtwAndProfapi == NULL)
799 _ASSERTE(!"EndMovedReferences() encountered a NULL profilingContext");
803 #ifdef PROFILING_SUPPORTED
805 if (fAllowProfApiNotification)
807 BEGIN_PIN_PROFILER(CORProfilerTrackGC());
808 g_profControlBlock.pProfInterface->EndMovedReferences(&(pCtxForEtwAndProfapi->pctxProfAPI));
811 #endif //PROFILING_SUPPORTED
815 if (!ShouldTrackMovementForEtw())
818 // If context isn't already set up for us, then we haven't been collecting any data
820 EtwGcMovementContext * pContext = pCtxForEtwAndProfapi->pctxEtw;
821 if (pContext == NULL)
824 // Flush any remaining moved or surviving range data
826 if (pContext->cBulkMovedObjectRanges > 0)
828 FireEtwGCBulkMovedObjectRanges(
829 pContext->iCurBulkMovedObjectRanges,
830 pContext->cBulkMovedObjectRanges,
832 sizeof(pContext->rgGCBulkMovedObjectRanges[0]),
833 &pContext->rgGCBulkMovedObjectRanges[0]);
836 if (pContext->cBulkSurvivingObjectRanges > 0)
838 FireEtwGCBulkSurvivingObjectRanges(
839 pContext->iCurBulkSurvivingObjectRanges,
840 pContext->cBulkSurvivingObjectRanges,
842 sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]),
843 &pContext->rgGCBulkSurvivingObjectRanges[0]);
846 pCtxForEtwAndProfapi->pctxEtw = NULL;
850 /***************************************************************************/
851 /* This implements the public runtime provider's GCHeapCollectKeyword. It
852 performs a full, gen-2, blocking GC. */
853 /***************************************************************************/
854 VOID ETW::GCLog::ForceGC(LONGLONG l64ClientSequenceNumber)
864 #ifndef FEATURE_REDHAWK
865 if (!IsGarbageCollectorFullyInitialized())
867 #endif // FEATURE_REDHAWK
869 InterlockedExchange64(&s_l64LastClientSequenceNumber, l64ClientSequenceNumber);
871 ForceGCForDiagnostics();
874 //---------------------------------------------------------------------------------------
876 // Helper to fire the GCStart event. Figures out which version of GCStart to fire, and
877 // includes the client sequence number, if available.
880 // pGcInfo - ETW_GC_INFO containing details from GC about this collection
884 VOID ETW::GCLog::FireGcStart(ETW_GC_INFO * pGcInfo)
886 LIMITED_METHOD_CONTRACT;
888 if (ETW_TRACING_CATEGORY_ENABLED(
889 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
890 TRACE_LEVEL_INFORMATION,
893 // If the controller specified a client sequence number for us to log with this
894 // GCStart, then retrieve it
895 LONGLONG l64ClientSequenceNumberToLog = 0;
896 if ((s_l64LastClientSequenceNumber != 0) &&
897 (pGcInfo->GCStart.Depth == GCHeapUtilities::GetGCHeap()->GetMaxGeneration()) &&
898 (pGcInfo->GCStart.Reason == ETW_GC_INFO::GC_INDUCED))
900 l64ClientSequenceNumberToLog = InterlockedExchange64(&s_l64LastClientSequenceNumber, 0);
903 FireEtwGCStart_V2(pGcInfo->GCStart.Count, pGcInfo->GCStart.Depth, pGcInfo->GCStart.Reason, pGcInfo->GCStart.Type, GetClrInstanceId(), l64ClientSequenceNumberToLog);
907 //---------------------------------------------------------------------------------------
909 // Contains code common to profapi and ETW scenarios where the profiler wants to force
910 // the CLR to perform a GC. The important work here is to create a managed thread for
911 // the current thread BEFORE the GC begins. On both ETW and profapi threads, there may
912 // not yet be a managed thread object. But some scenarios require a managed thread
913 // object be present (notably if we need to call into Jupiter during the GC).
916 // HRESULT indicating success or failure
919 // Caller should ensure that the EE has fully started up and that the GC heap is
920 // initialized enough to actually perform a GC
924 HRESULT ETW::GCLog::ForceGCForDiagnostics()
936 #ifndef FEATURE_REDHAWK
937 // Caller should ensure we're past startup.
938 _ASSERTE(IsGarbageCollectorFullyInitialized());
940 // In immersive apps the GarbageCollect() call below will call into Jupiter,
941 // which will call back into the runtime to track references. This call
942 // chain would cause a Thread object to be created for this thread while code
943 // higher on the stack owns the ThreadStoreLock. This will lead to asserts
944 // since the ThreadStoreLock is non-reentrant. To avoid this we'll create
945 // the Thread object here instead.
946 if (GetThreadNULLOk() == NULL)
949 SetupThreadNoThrow(&hr);
954 ASSERT_NO_EE_LOCKS_HELD();
958 // Need to switch to cooperative mode as the thread will access managed
959 // references (through Jupiter callbacks).
961 #endif // FEATURE_REDHAWK
963 ForcedGCHolder forcedGCHolder;
965 hr = GCHeapUtilities::GetGCHeap()->GarbageCollect(
966 -1, // all generations should be collected
967 false, // low_memory_p
968 collection_blocking);
970 #ifndef FEATURE_REDHAWK
973 EX_END_CATCH(RethrowCorruptingExceptions);
974 #endif // FEATURE_REDHAWK
984 //---------------------------------------------------------------------------------------
985 // WalkStaticsAndCOMForETW walks both CCW/RCW objects and static variables.
986 //---------------------------------------------------------------------------------------
988 VOID ETW::GCLog::WalkStaticsAndCOMForETW()
999 BulkTypeEventLogger typeLogger;
1002 BulkComLogger comLogger(&typeLogger);
1003 comLogger.LogAllComObjects();
1005 // Walk static variables
1006 BulkStaticsLogger staticLogger(&typeLogger);
1007 staticLogger.LogAllStatics();
1009 // Ensure all loggers have written all events, fire type logger last to batch events
1010 // (FireBulkComEvent or FireBulkStaticsEvent may queue up additional types).
1011 comLogger.FireBulkComEvent();
1012 staticLogger.FireBulkStaticsEvent();
1013 typeLogger.FireBulkTypeEvent();
1018 EX_END_CATCH(SwallowAllExceptions);
1022 //---------------------------------------------------------------------------------------
1023 // BulkStaticsLogger: Batches up and logs static variable roots
1024 //---------------------------------------------------------------------------------------
1026 BulkComLogger::BulkComLogger(BulkTypeEventLogger *typeLogger)
1027 : m_currRcw(0), m_currCcw(0), m_typeLogger(typeLogger), m_etwRcwData(0), m_etwCcwData(0), m_enumResult(0)
1037 m_etwRcwData = new EventRCWEntry[kMaxRcwCount];
1038 m_etwCcwData = new EventCCWEntry[kMaxCcwCount];
1041 BulkComLogger::~BulkComLogger()
1054 delete [] m_etwRcwData;
1057 delete [] m_etwCcwData;
1061 CCWEnumerationEntry *curr = m_enumResult;
1064 CCWEnumerationEntry *next = curr->Next;
1071 void BulkComLogger::FireBulkComEvent()
1073 WRAPPER_NO_CONTRACT;
1079 void BulkComLogger::WriteRcw(RCW *pRcw, Object *obj)
1086 PRECONDITION(pRcw != NULL);
1087 PRECONDITION(obj != NULL);
1091 _ASSERTE(m_currRcw < kMaxRcwCount);
1093 #ifdef FEATURE_COMINTEROP
1094 EventRCWEntry &rcw = m_etwRcwData[m_currRcw];
1095 rcw.ObjectID = (ULONGLONG)obj;
1096 rcw.TypeID = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1097 rcw.IUnk = (ULONGLONG)pRcw->GetIUnknown_NoAddRef();
1098 rcw.VTable = (ULONGLONG)pRcw->GetVTablePtr();
1099 rcw.RefCount = pRcw->GetRefCount();
1102 if (++m_currRcw >= kMaxRcwCount)
1107 void BulkComLogger::FlushRcw()
1117 _ASSERTE(m_currRcw <= kMaxRcwCount);
1124 for (int i = 0; i < m_currRcw; ++i)
1125 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, m_etwRcwData[i].TypeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1128 unsigned short instance = GetClrInstanceId();
1130 #if !defined(FEATURE_PAL)
1131 EVENT_DATA_DESCRIPTOR eventData[3];
1132 EventDataDescCreate(&eventData[0], &m_currRcw, sizeof(const unsigned int));
1133 EventDataDescCreate(&eventData[1], &instance, sizeof(const unsigned short));
1134 EventDataDescCreate(&eventData[2], m_etwRcwData, sizeof(EventRCWEntry) * m_currRcw);
1136 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRCW, _countof(eventData), eventData);
1138 ULONG result = FireEtXplatGCBulkRCW(m_currRcw, instance, sizeof(EventRCWEntry) * m_currRcw, m_etwRcwData);
1139 #endif // !defined(FEATURE_PAL)
1141 _ASSERTE(result == ERROR_SUCCESS);
1146 void BulkComLogger::WriteCcw(ComCallWrapper *pCcw, Object **handle, Object *obj)
1153 PRECONDITION(handle != NULL);
1154 PRECONDITION(obj != NULL);
1158 _ASSERTE(m_currCcw < kMaxCcwCount);
1160 #ifdef FEATURE_COMINTEROP
1161 IUnknown *iUnk = NULL;
1163 ULONG jupiterRefCount = 0;
1168 iUnk = pCcw->GetOuter();
1170 iUnk = pCcw->GetBasicIP(true);
1172 refCount = pCcw->GetRefCount();
1173 jupiterRefCount = pCcw->GetJupiterRefCount();
1175 if (pCcw->IsWrapperActive())
1176 flags |= EventCCWEntry::Strong;
1178 if (pCcw->IsPegged())
1179 flags |= EventCCWEntry::Pegged;
1182 EventCCWEntry &ccw = m_etwCcwData[m_currCcw++];
1183 ccw.RootID = (ULONGLONG)handle;
1184 ccw.ObjectID = (ULONGLONG)obj;
1185 ccw.TypeID = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1186 ccw.IUnk = (ULONGLONG)iUnk;
1187 ccw.RefCount = refCount;
1188 ccw.JupiterRefCount = jupiterRefCount;
1191 if (m_currCcw >= kMaxCcwCount)
1196 void BulkComLogger::FlushCcw()
1206 _ASSERTE(m_currCcw <= kMaxCcwCount);
1213 for (int i = 0; i < m_currCcw; ++i)
1214 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, m_etwCcwData[i].TypeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1217 unsigned short instance = GetClrInstanceId();
1219 #if !defined(FEATURE_PAL)
1220 EVENT_DATA_DESCRIPTOR eventData[3];
1221 EventDataDescCreate(&eventData[0], &m_currCcw, sizeof(const unsigned int));
1222 EventDataDescCreate(&eventData[1], &instance, sizeof(const unsigned short));
1223 EventDataDescCreate(&eventData[2], m_etwCcwData, sizeof(EventCCWEntry) * m_currCcw);
1225 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRootCCW, _countof(eventData), eventData);
1227 ULONG result = FireEtXplatGCBulkRootCCW(m_currCcw, instance, sizeof(EventCCWEntry) * m_currCcw, m_etwCcwData);
1228 #endif //!defined(FEATURE_PAL)
1230 _ASSERTE(result == ERROR_SUCCESS);
1235 void BulkComLogger::LogAllComObjects()
1245 #ifdef FEATURE_COMINTEROP
1246 SyncBlockCache *cache = SyncBlockCache::GetSyncBlockCache();
1250 int count = cache->GetTableEntryCount();
1251 SyncTableEntry *table = SyncTableEntry::GetSyncTableEntry();
1253 for (int i = 0; i < count; ++i)
1255 SyncTableEntry &entry = table[i];
1256 Object *obj = entry.m_Object.Load();
1257 if (obj && entry.m_SyncBlock)
1259 InteropSyncBlockInfo *interop = entry.m_SyncBlock->GetInteropInfoNoCreate();
1262 RCW *rcw = interop->GetRawRCW();
1269 // We need to do work in HandleWalkCallback which may trigger a GC. We cannot do this while
1270 // enumerating the handle table. Instead, we will build a list of RefCount handles we found
1271 // during the handle table enumeration first (m_enumResult) during this enumeration:
1272 GCHandleUtilities::GetGCHandleManager()->TraceRefCountedHandles(BulkComLogger::HandleWalkCallback, uintptr_t(this), 0);
1274 // Now that we have all of the object handles, we will walk all of the handles and write the
1276 for (CCWEnumerationEntry *curr = m_enumResult; curr; curr = curr->Next)
1278 for (int i = 0; i < curr->Count; ++i)
1280 Object **handle = curr->Handles[i];
1283 if (handle == NULL || (obj = *handle) == 0)
1286 ObjHeader *header = obj->GetHeader();
1287 _ASSERTE(header != NULL);
1289 // We can catch the refcount handle too early where we don't have a CCW, WriteCCW
1290 // handles this case. We still report the refcount handle without the CCW data.
1291 ComCallWrapper *ccw = NULL;
1293 // Checking the index ensures that the syncblock is already created. The
1294 // PassiveGetSyncBlock function does not check bounds, so we have to be sure
1295 // the SyncBlock was already created.
1296 int index = header->GetHeaderSyncBlockIndex();
1299 SyncBlock *syncBlk = header->PassiveGetSyncBlock();
1300 InteropSyncBlockInfo *interop = syncBlk->GetInteropInfoNoCreate();
1302 ccw = interop->GetCCW();
1305 WriteCcw(ccw, handle, obj);
1313 void BulkComLogger::HandleWalkCallback(Object **handle, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2)
1320 PRECONDITION(param1 != NULL); // Should be the "this" pointer for BulkComLogger.
1321 PRECONDITION(param2 == 0); // This is set by Ref_TraceRefCountHandles.
1325 // Simple sanity check to ensure the parameters are what we expect them to be.
1326 _ASSERTE(param2 == 0);
1329 ((BulkComLogger*)param1)->AddCcwHandle(handle);
1334 // Used during CCW enumeration to keep track of all object handles which point to a CCW.
1335 void BulkComLogger::AddCcwHandle(Object **handle)
1342 PRECONDITION(handle != NULL);
1346 if (m_enumResult == NULL)
1347 m_enumResult = new CCWEnumerationEntry;
1349 CCWEnumerationEntry *curr = m_enumResult;
1353 if (curr->Count == _countof(curr->Handles))
1355 curr->Next = new CCWEnumerationEntry;
1359 curr->Handles[curr->Count++] = handle;
1365 //---------------------------------------------------------------------------------------
1366 // BulkStaticsLogger: Batches up and logs static variable roots
1367 //---------------------------------------------------------------------------------------
1371 #include "domainfile.h"
1373 BulkStaticsLogger::BulkStaticsLogger(BulkTypeEventLogger *typeLogger)
1374 : m_buffer(0), m_used(0), m_count(0), m_domain(0), m_typeLogger(typeLogger)
1384 m_buffer = new BYTE[kMaxBytesValues];
1387 BulkStaticsLogger::~BulkStaticsLogger()
1398 FireBulkStaticsEvent();
1404 void BulkStaticsLogger::FireBulkStaticsEvent()
1414 if (m_used <= 0 || m_count <= 0)
1417 _ASSERTE(m_domain != NULL);
1419 unsigned short instance = GetClrInstanceId();
1420 unsigned __int64 appDomain = (unsigned __int64)m_domain;
1422 #if !defined(FEATURE_PAL)
1423 EVENT_DATA_DESCRIPTOR eventData[4];
1424 EventDataDescCreate(&eventData[0], &m_count, sizeof(const unsigned int) );
1425 EventDataDescCreate(&eventData[1], &appDomain, sizeof(unsigned __int64) );
1426 EventDataDescCreate(&eventData[2], &instance, sizeof(const unsigned short) );
1427 EventDataDescCreate(&eventData[3], m_buffer, m_used);
1429 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRootStaticVar, _countof(eventData), eventData);
1431 ULONG result = FireEtXplatGCBulkRootStaticVar(m_count, appDomain, instance, m_used, m_buffer);
1432 #endif //!defined(FEATURE_PAL)
1434 _ASSERTE(result == ERROR_SUCCESS);
1440 void BulkStaticsLogger::WriteEntry(AppDomain *domain, Object **address, Object *obj, FieldDesc *fieldDesc)
1447 PRECONDITION(domain != NULL);
1448 PRECONDITION(address != NULL);
1449 PRECONDITION(obj != NULL);
1450 PRECONDITION(fieldDesc != NULL);
1454 // Each bulk statics event is for one AppDomain. If we are now inspecting a new domain,
1455 // we need to flush the built up events now.
1456 if (m_domain != domain)
1458 if (m_domain != NULL)
1459 FireBulkStaticsEvent();
1464 ULONGLONG th = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1465 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, th, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1467 // We should have at least 512 characters remaining in the buffer here.
1468 int remaining = kMaxBytesValues - m_used;
1469 _ASSERTE(kMaxBytesValues - m_used > 512);
1471 int len = EventStaticEntry::WriteEntry(m_buffer + m_used, remaining, (ULONGLONG)address,
1472 (ULONGLONG)obj, th, 0, fieldDesc);
1474 // 512 bytes was not enough buffer? This shouldn't happen, so we'll skip emitting the
1482 // When we are close to running out of buffer, emit the event.
1483 if (kMaxBytesValues - m_used < 512)
1484 FireBulkStaticsEvent();
1487 void BulkStaticsLogger::LogAllStatics()
1497 // Enumerate only active app domains (first parameter). We use the unsafe
1498 // iterator here because this method is called under the threadstore lock
1499 // and it's safe to use while the runtime is suspended.
1500 UnsafeAppDomainIterator appIter(TRUE);
1502 while (appIter.Next())
1504 AppDomain *domain = appIter.GetDomain();
1506 AppDomain::AssemblyIterator assemblyIter = domain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded|kIncludeExecution));
1507 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
1508 while (assemblyIter.Next(pDomainAssembly.This()))
1510 // Make sure the assembly is loaded.
1511 if (!pDomainAssembly->IsLoaded())
1514 CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetAssembly();
1515 DomainModuleIterator modIter = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
1517 while (modIter.Next())
1519 // Get the domain module from the module/appdomain pair.
1520 Module *module = modIter.GetModule();
1524 DomainFile *domainFile = module->FindDomainFile(domain);
1525 if (domainFile == NULL)
1528 // Ensure the module has fully loaded.
1529 if (!domainFile->IsActive())
1532 DomainLocalModule *domainModule = module->GetDomainLocalModule(domain);
1533 if (domainModule == NULL)
1536 // Now iterate all types with
1537 LookupMap<PTR_MethodTable>::Iterator mtIter = module->EnumerateTypeDefs();
1538 while (mtIter.Next())
1540 // I don't think mt can be null here, but the dac does a null check...
1541 // IsFullyLoaded should be equivalent to 'GetLoadLevel() == CLASS_LOADED'
1542 MethodTable *mt = mtIter.GetElement();
1543 if (mt == NULL || !mt->IsFullyLoaded())
1546 EEClass *cls = mt->GetClass();
1547 _ASSERTE(cls != NULL);
1549 if (cls->GetNumStaticFields() <= 0)
1552 ApproxFieldDescIterator fieldIter(mt, ApproxFieldDescIterator::STATIC_FIELDS);
1553 for (FieldDesc *field = fieldIter.Next(); field != NULL; field = fieldIter.Next())
1555 // Don't want thread local
1556 _ASSERTE(field->IsStatic());
1557 if (field->IsSpecialStatic() || field->IsEnCNew())
1560 // Static valuetype values are boxed.
1561 CorElementType fieldType = field->GetFieldType();
1562 if (fieldType != ELEMENT_TYPE_CLASS && fieldType != ELEMENT_TYPE_VALUETYPE)
1565 BYTE *base = field->GetBaseInDomainLocalModule(domainModule);
1569 Object **address = (Object**)field->GetStaticAddressHandle(base);
1571 if (address == NULL || ((obj = *address) == NULL))
1574 WriteEntry(domain, address, *address, field);
1575 } // foreach static field
1577 } // foreach domain module
1578 } // foreach domain assembly
1579 } // foreach AppDomain
1580 } // BulkStaticsLogger::LogAllStatics
1584 //---------------------------------------------------------------------------------------
1585 // BulkTypeValue / BulkTypeEventLogger: These take care of batching up types so they can
1586 // be logged via ETW in bulk
1587 //---------------------------------------------------------------------------------------
1589 BulkTypeValue::BulkTypeValue() : cTypeParameters(0)
1590 #ifdef FEATURE_REDHAWK
1591 , ullSingleTypeParameter(0)
1592 #else // FEATURE_REDHAWK
1594 #endif // FEATURE_REDHAWK
1595 , rgTypeParameters()
1597 LIMITED_METHOD_CONTRACT;
1598 ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
1601 //---------------------------------------------------------------------------------------
1603 // Clears a BulkTypeValue so it can be reused after the buffer is flushed to ETW
1606 void BulkTypeValue::Clear()
1616 ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
1617 cTypeParameters = 0;
1618 #ifdef FEATURE_REDHAWK
1619 ullSingleTypeParameter = 0;
1620 rgTypeParameters.Release();
1621 #else // FEATURE_REDHAWK
1623 rgTypeParameters.Clear();
1624 #endif // FEATURE_REDHAWK
1627 //---------------------------------------------------------------------------------------
1629 // Fire an ETW event for all the types we batched so far, and then reset our state
1630 // so we can start batching new types at the beginning of the array.
1634 void BulkTypeEventLogger::FireBulkTypeEvent()
1636 LIMITED_METHOD_CONTRACT;
1638 if (m_nBulkTypeValueCount == 0)
1640 // No types were batched up, so nothing to send
1643 UINT16 nClrInstanceID = GetClrInstanceId();
1645 if(m_pBulkTypeEventBuffer == NULL)
1647 // The buffer could not be allocated when this object was created, so bail.
1653 for (int iTypeData = 0; iTypeData < m_nBulkTypeValueCount; iTypeData++)
1655 BulkTypeValue& target = m_rgBulkTypeValues[iTypeData];
1657 // Do fixed-size data as one bulk copy
1659 m_pBulkTypeEventBuffer + iSize,
1660 &(target.fixedSizedData),
1661 sizeof(target.fixedSizedData));
1662 iSize += sizeof(target.fixedSizedData);
1664 // Do var-sized data individually per field
1666 LPCWSTR wszName = target.sName.GetUnicode();
1667 if (wszName == NULL)
1669 m_pBulkTypeEventBuffer[iSize++] = 0;
1670 m_pBulkTypeEventBuffer[iSize++] = 0;
1674 UINT nameSize = (target.sName.GetCount() + 1) * sizeof(WCHAR);
1675 memcpy(m_pBulkTypeEventBuffer + iSize, wszName, nameSize);
1679 // Type parameter count
1680 ULONG params = target.rgTypeParameters.GetCount();
1682 ULONG *ptrInt = (ULONG*)(m_pBulkTypeEventBuffer + iSize);
1686 target.cTypeParameters = params;
1688 // Type parameter array
1689 if (target.cTypeParameters > 0)
1691 memcpy(m_pBulkTypeEventBuffer + iSize, target.rgTypeParameters.GetElements(), sizeof(ULONGLONG) * target.cTypeParameters);
1692 iSize += sizeof(ULONGLONG) * target.cTypeParameters;
1696 FireEtwBulkType(m_nBulkTypeValueCount, GetClrInstanceId(), iSize, m_pBulkTypeEventBuffer);
1699 m_nBulkTypeValueCount = 0;
1700 m_nBulkTypeValueByteCount = 0;
1703 #ifndef FEATURE_REDHAWK
1705 //---------------------------------------------------------------------------------------
1707 // Batches a single type into the array, flushing the array to ETW if it fills up. Most
1708 // interaction with the type system (to analyze the type) is done here. This does not
1709 // recursively batch up any parameter types (for arrays or generics), but does add their
1710 // TypeHandles to the rgTypeParameters array. LogTypeAndParameters is responsible for
1711 // initiating any recursive calls to deal with type parameters.
1714 // th - TypeHandle to batch
1717 // Index into array of where this type got batched. -1 if there was a failure.
1720 int BulkTypeEventLogger::LogSingleType(TypeHandle th)
1727 CAN_TAKE_LOCK; // some of the type system stuff can take locks
1731 // If there's no room for another type, flush what we've got
1732 if (m_nBulkTypeValueCount == _countof(m_rgBulkTypeValues))
1734 FireBulkTypeEvent();
1737 _ASSERTE(m_nBulkTypeValueCount < _countof(m_rgBulkTypeValues));
1739 if (!th.IsTypeDesc() && th.GetMethodTable()->IsArray())
1741 _ASSERTE(!"BulkTypeEventLogger::LogSingleType called with MethodTable array");
1745 BulkTypeValue * pVal = &m_rgBulkTypeValues[m_nBulkTypeValueCount];
1747 // Clear out pVal before filling it out (array elements can get reused if there
1748 // are enough types that we need to flush to multiple events). Clearing the
1749 // contained SBuffer can throw, so deal with exceptions
1750 BOOL fSucceeded = FALSE;
1760 EX_END_CATCH(RethrowCorruptingExceptions);
1764 pVal->fixedSizedData.TypeID = (ULONGLONG) th.AsTAddr();
1765 pVal->fixedSizedData.ModuleID = (ULONGLONG) (TADDR) th.GetModule();
1766 pVal->fixedSizedData.TypeNameID = (th.GetMethodTable() == NULL) ? 0 : th.GetCl();
1767 pVal->fixedSizedData.Flags = 0;
1768 pVal->fixedSizedData.CorElementType = (BYTE) th.GetInternalCorElementType();
1772 // Normal typedesc array
1773 pVal->fixedSizedData.Flags |= kEtwTypeFlagsArray;
1775 // Fetch TypeHandle of array elements
1779 pVal->rgTypeParameters.Append((ULONGLONG) th.AsArray()->GetArrayElementTypeHandle().AsTAddr());
1786 EX_END_CATCH(RethrowCorruptingExceptions);
1790 else if (th.IsTypeDesc())
1792 // Non-array Typedescs
1793 PTR_TypeDesc pTypeDesc = th.AsTypeDesc();
1794 if (pTypeDesc->HasTypeParam())
1799 pVal->rgTypeParameters.Append((ULONGLONG) pTypeDesc->GetTypeParam().AsTAddr());
1806 EX_END_CATCH(RethrowCorruptingExceptions);
1813 // Non-array MethodTable
1815 PTR_MethodTable pMT = th.AsMethodTable();
1817 // Make CorElementType more specific if this is a string MT
1818 if (pMT->IsString())
1820 pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_STRING;
1822 else if (pMT->IsObjectClass())
1824 pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_OBJECT;
1827 // Generic arguments
1828 DWORD cTypeParameters = pMT->GetNumGenericArgs();
1829 if (cTypeParameters > 0)
1831 Instantiation inst = pMT->GetInstantiation();
1835 for (DWORD i=0; i < cTypeParameters; i++)
1837 pVal->rgTypeParameters.Append((ULONGLONG) inst[i].AsTAddr());
1845 EX_END_CATCH(RethrowCorruptingExceptions);
1850 if (pMT->HasFinalizer())
1852 pVal->fixedSizedData.Flags |= kEtwTypeFlagsFinalizable;
1854 if (pMT->IsDelegate())
1856 pVal->fixedSizedData.Flags |= kEtwTypeFlagsDelegate;
1858 if (pMT->IsComObjectType())
1860 pVal->fixedSizedData.Flags |= kEtwTypeFlagsExternallyImplementedCOMObject;
1864 // If the profiler wants it, construct a name. Always normalize the string (even if
1865 // type names are not requested) so that calls to sName.GetCount() can't throw
1868 if (ETW_TRACING_CATEGORY_ENABLED(
1869 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
1870 TRACE_LEVEL_INFORMATION,
1871 CLR_GCHEAPANDTYPENAMES_KEYWORD))
1873 th.GetName(pVal->sName);
1875 pVal->sName.Normalize();
1879 // If this failed, the name remains empty, which is ok; the event just
1880 // won't have a name in it.
1881 pVal->sName.Clear();
1883 EX_END_CATCH(RethrowCorruptingExceptions);
1885 // Now that we know the full size of this type's data, see if it fits in our
1886 // batch or whether we need to flush
1888 int cbVal = pVal->GetByteCountInEvent();
1889 if (cbVal > kMaxBytesTypeValues)
1891 // This type is apparently so huge, it's too big to squeeze into an event, even
1892 // if it were the only type batched in the whole event. Bail
1893 _ASSERTE(!"Type too big to log via ETW");
1897 if (m_nBulkTypeValueByteCount + cbVal > kMaxBytesTypeValues)
1899 // Although this type fits into the array, its size is so big that the entire
1900 // array can't be logged via ETW. So flush the array, and start over by
1901 // calling ourselves--this refetches the type info and puts it at the
1902 // beginning of the array. Since we know this type is small enough to be
1903 // batched into an event on its own, this recursive call will not try to
1904 // call itself again.
1905 FireBulkTypeEvent();
1906 return LogSingleType(th);
1909 // The type fits into the batch, so update our state
1910 m_nBulkTypeValueCount++;
1911 m_nBulkTypeValueByteCount += cbVal;
1912 return m_nBulkTypeValueCount - 1; // Index of type we just added
1915 //---------------------------------------------------------------------------------------
1917 // High-level method to batch a type and (recursively) its type parameters, flushing to
1918 // ETW as needed. This is called by (static)
1919 // ETW::TypeSystemLog::LogTypeAndParametersIfNecessary, which is what clients use to log
1923 // * thAsAddr - Type to batch
1924 // * typeLogBehavior - Reminder of whether the type system log lock is held
1925 // (useful if we need to recurively call back into TypeSystemLog), and whether
1926 // we even care to check if the type was already logged
1929 void BulkTypeEventLogger::LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior)
1936 CAN_TAKE_LOCK; // LogSingleType can take locks
1940 TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr);
1942 // Batch up this type. This grabs useful info about the type, including any
1943 // type parameters it may have, and sticks it in m_rgBulkTypeValues
1944 int iBulkTypeEventData = LogSingleType(th);
1945 if (iBulkTypeEventData == -1)
1947 // There was a failure trying to log the type, so don't bother with its type
1952 // Look at the type info we just batched, so we can get the type parameters
1953 BulkTypeValue * pVal = &m_rgBulkTypeValues[iBulkTypeEventData];
1955 // We're about to recursively call ourselves for the type parameters, so make a
1956 // local copy of their type handles first (else, as we log them we could flush
1957 // and clear out m_rgBulkTypeValues, thus trashing pVal)
1959 StackSArray<ULONGLONG> rgTypeParameters;
1960 DWORD cParams = pVal->rgTypeParameters.GetCount();
1962 BOOL fSucceeded = FALSE;
1965 for (COUNT_T i = 0; i < cParams; i++)
1967 rgTypeParameters.Append(pVal->rgTypeParameters[i]);
1975 EX_END_CATCH(RethrowCorruptingExceptions);
1979 // Before we recurse, adjust the special-cased type-log behavior that allows a
1980 // top-level type to be logged without lookup, but still requires lookups to avoid
1981 // dupes of type parameters
1982 if (typeLogBehavior == ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLogTopLevelType)
1983 typeLogBehavior = ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime;
1985 // Recursively log any referenced parameter types
1986 for (COUNT_T i=0; i < cParams; i++)
1988 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(this, rgTypeParameters[i], typeLogBehavior);
1992 #endif // FEATURE_REDHAWK
1994 // Holds state that batches of roots, nodes, edges, and types as the GC walks the heap
1995 // at the end of a collection.
1996 class EtwGcHeapDumpContext
1999 // An instance of EtwGcHeapDumpContext is dynamically allocated and stored inside of
2000 // ProfilingScanContext and ProfilerWalkHeapContext, which are context structures
2001 // that the GC heap walker sends back to the callbacks. This method is passed a
2002 // pointer to ProfilingScanContext::pvEtwContext or
2003 // ProfilerWalkHeapContext::pvEtwContext; if non-NULL it gets returned; else, a new
2004 // EtwGcHeapDumpContext is allocated, stored in that pointer, and then returned.
2005 // Callers should test for NULL, which can be returned if out of memory
2006 static EtwGcHeapDumpContext * GetOrCreateInGCContext(LPVOID * ppvEtwContext)
2008 LIMITED_METHOD_CONTRACT;
2010 _ASSERTE(ppvEtwContext != NULL);
2012 EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) *ppvEtwContext;
2013 if (pContext == NULL)
2015 pContext = new (nothrow) EtwGcHeapDumpContext;
2016 *ppvEtwContext = pContext;
2021 EtwGcHeapDumpContext() :
2022 iCurBulkRootEdge(0),
2023 iCurBulkRootConditionalWeakTableElementEdge(0),
2024 iCurBulkNodeEvent(0),
2025 iCurBulkEdgeEvent(0),
2026 bulkTypeEventLogger()
2028 LIMITED_METHOD_CONTRACT;
2030 ClearRootConditionalWeakTableElementEdges();
2035 // These helpers clear the individual buffers, for use after a flush and on
2036 // construction. They intentionally leave the indices (iCur*) alone, since they
2037 // persist across flushes within a GC
2039 void ClearRootEdges()
2041 LIMITED_METHOD_CONTRACT;
2042 cGcBulkRootEdges = 0;
2043 ZeroMemory(rgGcBulkRootEdges, sizeof(rgGcBulkRootEdges));
2046 void ClearRootConditionalWeakTableElementEdges()
2048 LIMITED_METHOD_CONTRACT;
2049 cGCBulkRootConditionalWeakTableElementEdges = 0;
2050 ZeroMemory(rgGCBulkRootConditionalWeakTableElementEdges, sizeof(rgGCBulkRootConditionalWeakTableElementEdges));
2055 LIMITED_METHOD_CONTRACT;
2056 cGcBulkNodeValues = 0;
2057 ZeroMemory(rgGcBulkNodeValues, sizeof(rgGcBulkNodeValues));
2062 LIMITED_METHOD_CONTRACT;
2063 cGcBulkEdgeValues = 0;
2064 ZeroMemory(rgGcBulkEdgeValues, sizeof(rgGcBulkEdgeValues));
2067 //---------------------------------------------------------------------------------------
2070 // A "root edge" is the relationship between a source "GCRootID" (i.e., stack
2071 // variable, handle, static, etc.) and the target "RootedNodeAddress" (the managed
2072 // object that gets rooted).
2074 //---------------------------------------------------------------------------------------
2076 // Sequence number for each GCBulkRootEdge event
2077 UINT iCurBulkRootEdge;
2079 // Number of root edges currently filled out in rgGcBulkRootEdges array
2080 UINT cGcBulkRootEdges;
2082 // Struct array containing the primary data for each GCBulkRootEdge event. Fix the size so
2083 // the total event stays well below the 64K
2084 // limit (leaving lots of room for non-struct fields that come before the root edge data)
2085 EventStructGCBulkRootEdgeValue rgGcBulkRootEdges[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootEdgeValue)];
2088 //---------------------------------------------------------------------------------------
2089 // GCBulkRootConditionalWeakTableElementEdge
2091 // These describe dependent handles, which simulate an edge connecting a key NodeID
2092 // to a value NodeID.
2094 //---------------------------------------------------------------------------------------
2096 // Sequence number for each GCBulkRootConditionalWeakTableElementEdge event
2097 UINT iCurBulkRootConditionalWeakTableElementEdge;
2099 // Number of root edges currently filled out in rgGCBulkRootConditionalWeakTableElementEdges array
2100 UINT cGCBulkRootConditionalWeakTableElementEdges;
2102 // Struct array containing the primary data for each GCBulkRootConditionalWeakTableElementEdge event. Fix the size so
2103 // the total event stays well below the 64K
2104 // limit (leaving lots of room for non-struct fields that come before the root edge data)
2105 EventStructGCBulkRootConditionalWeakTableElementEdgeValue rgGCBulkRootConditionalWeakTableElementEdges
2106 [(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootConditionalWeakTableElementEdgeValue)];
2108 //---------------------------------------------------------------------------------------
2111 // A "node" is ANY managed object sitting on the heap, including RootedNodeAddresses
2112 // as well as leaf nodes.
2114 //---------------------------------------------------------------------------------------
2116 // Sequence number for each GCBulkNode event
2117 UINT iCurBulkNodeEvent;
2119 // Number of nodes currently filled out in rgGcBulkNodeValues array
2120 UINT cGcBulkNodeValues;
2122 // Struct array containing the primary data for each GCBulkNode event. Fix the size so
2123 // the total event stays well below the 64K
2124 // limit (leaving lots of room for non-struct fields that come before the node data)
2125 EventStructGCBulkNodeValue rgGcBulkNodeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkNodeValue)];
2127 //---------------------------------------------------------------------------------------
2130 // An "edge" is the relationship between a source node and its referenced target
2131 // node. Edges are reported in bulk, separately from Nodes, but it is expected that
2132 // the consumer read the Node and Edge streams together. One takes the first node
2133 // from the Node stream, and then reads EdgeCount entries in the Edge stream, telling
2134 // you all of that Node's targets. Then, one takes the next node in the Node stream,
2135 // and reads the next entries in the Edge stream (using this Node's EdgeCount to
2136 // determine how many) to find all of its targets. This continues on until the Node
2137 // and Edge streams have been fully read.
2139 // GCBulkRootEdges are not duplicated in the GCBulkEdge events. GCBulkEdge events
2140 // begin at the GCBulkRootEdge.RootedNodeAddress and move forward.
2142 //---------------------------------------------------------------------------------------
2144 // Sequence number for each GCBulkEdge event
2145 UINT iCurBulkEdgeEvent;
2147 // Number of nodes currently filled out in rgGcBulkEdgeValues array
2148 UINT cGcBulkEdgeValues;
2150 // Struct array containing the primary data for each GCBulkEdge event. Fix the size so
2151 // the total event stays well below the 64K
2152 // limit (leaving lots of room for non-struct fields that come before the edge data)
2153 EventStructGCBulkEdgeValue rgGcBulkEdgeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkEdgeValue)];
2156 //---------------------------------------------------------------------------------------
2159 // Types are a bit more complicated to batch up, since their data is of varying
2160 // size. BulkTypeEventLogger takes care of the pesky details for us
2161 //---------------------------------------------------------------------------------------
2163 BulkTypeEventLogger bulkTypeEventLogger;
2168 //---------------------------------------------------------------------------------------
2170 // Called during a heap walk for each root reference encountered. Batches up the root in
2174 // * pvHandle - If the root is a handle, this points to the handle
2175 // * pRootedNode - Points to object that is rooted
2176 // * pSecondaryNodeForDependentHandle - For dependent handles, this is the
2178 // * fDependentHandle - nonzero iff this is for a dependent handle
2179 // * profilingScanContext - The shared profapi/etw context built up during the heap walk.
2180 // * dwGCFlags - Bitmask of "GC_"-style flags set by GC
2181 // * rootFlags - Bitmask of EtwGCRootFlags describing the root
2185 VOID ETW::GCLog::RootReference(
2187 Object * pRootedNode,
2188 Object * pSecondaryNodeForDependentHandle,
2189 BOOL fDependentHandle,
2190 ProfilingScanContext * profilingScanContext,
2194 LIMITED_METHOD_CONTRACT;
2196 EtwGcHeapDumpContext * pContext =
2197 EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilingScanContext->pvEtwContext);
2198 if (pContext == NULL)
2201 // Determine root kind, root ID, and handle-specific flags
2202 LPVOID pvRootID = NULL;
2203 BYTE nRootKind = (BYTE) profilingScanContext->dwEtwRootKind;
2206 case kEtwGCRootKindStack:
2207 #if !defined (FEATURE_REDHAWK) && (defined(GC_PROFILING) || defined (DACCESS_COMPILE))
2208 pvRootID = profilingScanContext->pMD;
2209 #endif // !defined (FEATURE_REDHAWK) && (defined(GC_PROFILING) || defined (DACCESS_COMPILE))
2212 case kEtwGCRootKindHandle:
2213 pvRootID = pvHandle;
2216 case kEtwGCRootKindFinalizer:
2217 _ASSERTE(pvRootID == NULL);
2220 case kEtwGCRootKindOther:
2222 _ASSERTE(nRootKind == kEtwGCRootKindOther);
2223 _ASSERTE(pvRootID == NULL);
2227 // Convert GC root flags to ETW root flags
2228 if (dwGCFlags & GC_CALL_INTERIOR)
2229 rootFlags |= kEtwGCRootFlagsInterior;
2230 if (dwGCFlags & GC_CALL_PINNED)
2231 rootFlags |= kEtwGCRootFlagsPinning;
2233 // Add root edge to appropriate buffer
2234 if (fDependentHandle)
2236 _ASSERTE(pContext->cGCBulkRootConditionalWeakTableElementEdges <
2237 _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges));
2238 EventStructGCBulkRootConditionalWeakTableElementEdgeValue * pRCWTEEdgeValue =
2239 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[pContext->cGCBulkRootConditionalWeakTableElementEdges];
2240 pRCWTEEdgeValue->GCKeyNodeID = pRootedNode;
2241 pRCWTEEdgeValue->GCValueNodeID = pSecondaryNodeForDependentHandle;
2242 pRCWTEEdgeValue->GCRootID = pvRootID;
2243 pContext->cGCBulkRootConditionalWeakTableElementEdges++;
2245 // If RCWTE edge buffer is now full, empty it into ETW
2246 if (pContext->cGCBulkRootConditionalWeakTableElementEdges ==
2247 _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges))
2249 FireEtwGCBulkRootConditionalWeakTableElementEdge(
2250 pContext->iCurBulkRootConditionalWeakTableElementEdge,
2251 pContext->cGCBulkRootConditionalWeakTableElementEdges,
2253 sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]),
2254 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]);
2256 pContext->iCurBulkRootConditionalWeakTableElementEdge++;
2257 pContext->ClearRootConditionalWeakTableElementEdges();
2262 _ASSERTE(pContext->cGcBulkRootEdges < _countof(pContext->rgGcBulkRootEdges));
2263 EventStructGCBulkRootEdgeValue * pBulkRootEdgeValue = &pContext->rgGcBulkRootEdges[pContext->cGcBulkRootEdges];
2264 pBulkRootEdgeValue->RootedNodeAddress = pRootedNode;
2265 pBulkRootEdgeValue->GCRootKind = nRootKind;
2266 pBulkRootEdgeValue->GCRootFlag = rootFlags;
2267 pBulkRootEdgeValue->GCRootID = pvRootID;
2268 pContext->cGcBulkRootEdges++;
2270 // If root edge buffer is now full, empty it into ETW
2271 if (pContext->cGcBulkRootEdges == _countof(pContext->rgGcBulkRootEdges))
2273 FireEtwGCBulkRootEdge(
2274 pContext->iCurBulkRootEdge,
2275 pContext->cGcBulkRootEdges,
2277 sizeof(pContext->rgGcBulkRootEdges[0]),
2278 &pContext->rgGcBulkRootEdges[0]);
2280 pContext->iCurBulkRootEdge++;
2281 pContext->ClearRootEdges();
2286 //---------------------------------------------------------------------------------------
2288 // Called during a heap walk for each object reference encountered. Batches up the
2289 // corresponding node, edges, and type data for the ETW events.
2292 // * profilerWalkHeapContext - The shared profapi/etw context built up during the heap walk.
2293 // * pObjReferenceSource - Object doing the pointing
2294 // * typeID - Type of pObjReferenceSource
2295 // * fDependentHandle - nonzero iff this is for a dependent handle
2296 // * cRefs - Count of objects being pointed to
2297 // * rgObjReferenceTargets - Array of objects being pointed to
2301 VOID ETW::GCLog::ObjectReference(
2302 ProfilerWalkHeapContext * profilerWalkHeapContext,
2303 Object * pObjReferenceSource,
2306 Object ** rgObjReferenceTargets)
2314 // LogTypeAndParametersIfNecessary can take a lock
2319 EtwGcHeapDumpContext * pContext =
2320 EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilerWalkHeapContext->pvEtwContext);
2321 if (pContext == NULL)
2324 //---------------------------------------------------------------------------------------
2325 // GCBulkNode events
2326 //---------------------------------------------------------------------------------------
2328 // Add Node (pObjReferenceSource) to buffer
2329 _ASSERTE(pContext->cGcBulkNodeValues < _countof(pContext->rgGcBulkNodeValues));
2330 EventStructGCBulkNodeValue * pBulkNodeValue = &pContext->rgGcBulkNodeValues[pContext->cGcBulkNodeValues];
2331 pBulkNodeValue->Address = pObjReferenceSource;
2332 pBulkNodeValue->Size = pObjReferenceSource->GetSize();
2333 pBulkNodeValue->TypeID = typeID;
2334 pBulkNodeValue->EdgeCount = cRefs;
2335 pContext->cGcBulkNodeValues++;
2337 // If Node buffer is now full, empty it into ETW
2338 if (pContext->cGcBulkNodeValues == _countof(pContext->rgGcBulkNodeValues))
2341 pContext->iCurBulkNodeEvent,
2342 pContext->cGcBulkNodeValues,
2344 sizeof(pContext->rgGcBulkNodeValues[0]),
2345 &pContext->rgGcBulkNodeValues[0]);
2347 pContext->iCurBulkNodeEvent++;
2348 pContext->ClearNodes();
2351 //---------------------------------------------------------------------------------------
2353 //---------------------------------------------------------------------------------------
2355 // We send type information as necessary--only for nodes, and only for nodes that we
2356 // haven't already sent type info for
2359 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(
2360 &pContext->bulkTypeEventLogger, // Batch up this type with others to minimize events
2363 // During heap walk, GC holds the lock for us, so we can directly enter the
2364 // hash to see if the type has already been logged
2365 ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime
2369 //---------------------------------------------------------------------------------------
2370 // GCBulkEdge events
2371 //---------------------------------------------------------------------------------------
2373 // Add Edges (rgObjReferenceTargets) to buffer. Buffer could fill up before all edges
2374 // are added (it could even fill up multiple times during this one call if there are
2375 // a lot of edges), so empty Edge buffer into ETW as we go along, as many times as we
2378 for (ULONGLONG i=0; i < cRefs; i++)
2380 _ASSERTE(pContext->cGcBulkEdgeValues < _countof(pContext->rgGcBulkEdgeValues));
2381 EventStructGCBulkEdgeValue * pBulkEdgeValue = &pContext->rgGcBulkEdgeValues[pContext->cGcBulkEdgeValues];
2382 pBulkEdgeValue->Value = rgObjReferenceTargets[i];
2383 // FUTURE: ReferencingFieldID
2384 pBulkEdgeValue->ReferencingFieldID = 0;
2385 pContext->cGcBulkEdgeValues++;
2387 // If Edge buffer is now full, empty it into ETW
2388 if (pContext->cGcBulkEdgeValues == _countof(pContext->rgGcBulkEdgeValues))
2391 pContext->iCurBulkEdgeEvent,
2392 pContext->cGcBulkEdgeValues,
2394 sizeof(pContext->rgGcBulkEdgeValues[0]),
2395 &pContext->rgGcBulkEdgeValues[0]);
2397 pContext->iCurBulkEdgeEvent++;
2398 pContext->ClearEdges();
2403 //---------------------------------------------------------------------------------------
2405 // Called by GC at end of heap dump to give us a convenient time to flush any remaining
2406 // buffers of data to ETW
2409 // profilerWalkHeapContext - Context containing data we've batched up
2413 VOID ETW::GCLog::EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext)
2415 LIMITED_METHOD_CONTRACT;
2417 // If context isn't already set up for us, then we haven't been collecting any data
2419 EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) profilerWalkHeapContext->pvEtwContext;
2420 if (pContext == NULL)
2423 // If the GC events are enabled, flush any remaining root, node, and / or edge data
2424 if (s_forcedGCInProgress &&
2425 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
2426 TRACE_LEVEL_INFORMATION,
2427 CLR_GCHEAPDUMP_KEYWORD))
2429 if (pContext->cGcBulkRootEdges > 0)
2431 FireEtwGCBulkRootEdge(
2432 pContext->iCurBulkRootEdge,
2433 pContext->cGcBulkRootEdges,
2435 sizeof(pContext->rgGcBulkRootEdges[0]),
2436 &pContext->rgGcBulkRootEdges[0]);
2439 if (pContext->cGCBulkRootConditionalWeakTableElementEdges > 0)
2441 FireEtwGCBulkRootConditionalWeakTableElementEdge(
2442 pContext->iCurBulkRootConditionalWeakTableElementEdge,
2443 pContext->cGCBulkRootConditionalWeakTableElementEdges,
2445 sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]),
2446 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]);
2449 if (pContext->cGcBulkNodeValues > 0)
2452 pContext->iCurBulkNodeEvent,
2453 pContext->cGcBulkNodeValues,
2455 sizeof(pContext->rgGcBulkNodeValues[0]),
2456 &pContext->rgGcBulkNodeValues[0]);
2459 if (pContext->cGcBulkEdgeValues > 0)
2462 pContext->iCurBulkEdgeEvent,
2463 pContext->cGcBulkEdgeValues,
2465 sizeof(pContext->rgGcBulkEdgeValues[0]),
2466 &pContext->rgGcBulkEdgeValues[0]);
2470 // Ditto for type events
2471 if (ETW_TRACING_CATEGORY_ENABLED(
2472 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
2473 TRACE_LEVEL_INFORMATION,
2476 pContext->bulkTypeEventLogger.FireBulkTypeEvent();
2479 // Delete any GC state built up in the context
2480 profilerWalkHeapContext->pvEtwContext = NULL;
2485 //---------------------------------------------------------------------------------------
2487 // Helper to send public finalize object & type events, and private finalize object
2488 // event. If Type events are enabled, this will send the Type event for the finalized
2489 // objects. It will not be batched with other types (except type parameters, if any),
2490 // and will not check if the Type has already been logged (may thus result in dupe
2491 // logging of the Type).
2494 // pMT - MT of object getting finalized
2495 // pObj - object getting finalized
2499 VOID ETW::GCLog::SendFinalizeObjectEvent(MethodTable * pMT, Object * pObj)
2507 // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to
2512 // Send public finalize object event, if it's enabled
2513 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, FinalizeObject))
2515 FireEtwFinalizeObject(pMT, pObj, GetClrInstanceId());
2517 // This function checks if type events are enabled; if so, it sends event for
2518 // finalized object's type (and parameter types, if any)
2519 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(
2520 NULL, // Not batching this type with others
2523 // Don't spend the time entering the lock and checking the hash table to see
2524 // if we've already logged the type; just log it (if type events are enabled).
2525 ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog
2529 // Send private finalize object event, if it's enabled
2530 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PrvFinalizeObject))
2534 DefineFullyQualifiedNameForClassWOnStack();
2535 FireEtwPrvFinalizeObject(pMT, pObj, GetClrInstanceId(), GetFullyQualifiedNameForClassNestedAwareW(pMT));
2540 EX_END_CATCH(RethrowCorruptingExceptions);
2545 DWORD ETW::ThreadLog::GetEtwThreadFlags(Thread * pThread)
2547 LIMITED_METHOD_CONTRACT;
2549 DWORD dwEtwThreadFlags = 0;
2551 if (pThread->IsThreadPoolThread())
2553 dwEtwThreadFlags |= kEtwThreadFlagThreadPoolWorker;
2555 if (pThread->IsGCSpecial())
2557 dwEtwThreadFlags |= kEtwThreadFlagGCSpecial;
2559 if (IsGarbageCollectorFullyInitialized() &&
2560 (pThread == FinalizerThread::GetFinalizerThread()))
2562 dwEtwThreadFlags |= kEtwThreadFlagFinalizer;
2565 return dwEtwThreadFlags;
2568 VOID ETW::ThreadLog::FireThreadCreated(Thread * pThread)
2570 LIMITED_METHOD_CONTRACT;
2572 FireEtwThreadCreated(
2574 (ULONGLONG)pThread->GetDomain(),
2575 GetEtwThreadFlags(pThread),
2576 pThread->GetThreadId(),
2577 pThread->GetOSThreadId(),
2578 GetClrInstanceId());
2581 VOID ETW::ThreadLog::FireThreadDC(Thread * pThread)
2583 LIMITED_METHOD_CONTRACT;
2587 (ULONGLONG)pThread->GetDomain(),
2588 GetEtwThreadFlags(pThread),
2589 pThread->GetThreadId(),
2590 pThread->GetOSThreadId(),
2591 GetClrInstanceId());
2596 #ifndef FEATURE_REDHAWK
2598 // TypeSystemLog implementation
2600 // We keep track of which TypeHandles have been logged, and stats on instances of these
2601 // TypeHandles that have been allocated, by a hash table of hash tables. The outer hash
2602 // table maps Module*'s to an inner hash table that contains all the TypeLoggingInfos for that
2603 // Module*. Arranging things this way makes it easy to deal with Module unloads, as we
2604 // can simply remove the corresponding inner hash table from the outer hash table.
2606 // The following help define the "inner" hash table: a hash table of TypeLoggingInfos
2607 // from a particular Module (key = TypeHandle, value = TypeLoggingInfo.
2609 class LoggedTypesFromModuleTraits : public NoRemoveSHashTraits< DefaultSHashTraits<ETW::TypeLoggingInfo> >
2613 // explicitly declare local typedefs for these traits types, otherwise
2614 // the compiler may get confused
2615 typedef NoRemoveSHashTraits< DefaultSHashTraits<ETW::TypeLoggingInfo> > PARENT;
2616 typedef PARENT::element_t element_t;
2617 typedef PARENT::count_t count_t;
2619 typedef TypeHandle key_t;
2621 static key_t GetKey(const element_t &e)
2623 LIMITED_METHOD_CONTRACT;
2627 static BOOL Equals(key_t k1, key_t k2)
2629 LIMITED_METHOD_CONTRACT;
2633 static count_t Hash(key_t k)
2635 LIMITED_METHOD_CONTRACT;
2636 return (count_t) k.AsTAddr();
2639 static bool IsNull(const element_t &e)
2641 LIMITED_METHOD_CONTRACT;
2642 return (e.th.AsTAddr() == NULL);
2645 static const element_t Null()
2647 LIMITED_METHOD_CONTRACT;
2648 return ETW::TypeLoggingInfo(NULL);
2651 typedef SHash<LoggedTypesFromModuleTraits> LoggedTypesFromModuleHash;
2653 // The inner hash table is housed inside this class, which acts as an entry in the outer
2655 class ETW::LoggedTypesFromModule
2659 LoggedTypesFromModuleHash loggedTypesFromModuleHash;
2661 // These are used by the outer hash table (mapping Module*'s to instances of
2662 // LoggedTypesFromModule).
2663 static COUNT_T Hash(Module * pModule)
2665 LIMITED_METHOD_CONTRACT;
2666 return (COUNT_T) (SIZE_T) pModule;
2670 LIMITED_METHOD_CONTRACT;
2674 LoggedTypesFromModule(Module * pModuleParam) : loggedTypesFromModuleHash()
2676 LIMITED_METHOD_CONTRACT;
2677 pModule = pModuleParam;
2680 ~LoggedTypesFromModule()
2682 LIMITED_METHOD_CONTRACT;
2686 // The following define the outer hash table (mapping Module*'s to instances of
2687 // LoggedTypesFromModule).
2689 class AllLoggedTypesTraits : public DefaultSHashTraits<ETW::LoggedTypesFromModule *>
2693 // explicitly declare local typedefs for these traits types, otherwise
2694 // the compiler may get confused
2695 typedef DefaultSHashTraits<ETW::LoggedTypesFromModule *> PARENT;
2696 typedef PARENT::element_t element_t;
2697 typedef PARENT::count_t count_t;
2699 typedef Module * key_t;
2701 static key_t GetKey(const element_t &e)
2703 LIMITED_METHOD_CONTRACT;
2707 static BOOL Equals(key_t k1, key_t k2)
2709 LIMITED_METHOD_CONTRACT;
2713 static count_t Hash(key_t k)
2715 LIMITED_METHOD_CONTRACT;
2716 return (count_t) (size_t) k;
2719 static bool IsNull(const element_t &e)
2721 LIMITED_METHOD_CONTRACT;
2725 static const element_t Null()
2727 LIMITED_METHOD_CONTRACT;
2732 typedef SHash<AllLoggedTypesTraits> AllLoggedTypesHash;
2734 // The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule) is
2735 // housed in this struct, which is dynamically allocated the first time we decide we need
2737 struct AllLoggedTypes
2740 // This Crst protects the entire outer & inner hash tables. On a GC heap walk, it
2741 // is entered once for the duration of the walk, so that we can freely access the
2742 // hash tables during the walk. On each object allocation, this Crst must be
2743 // entered individually each time.
2744 static CrstStatic s_cs;
2746 // A thread local copy of the global epoch.
2747 // This value is used by each thread to ensure that the thread local data structures
2748 // are in sync with the global state.
2749 unsigned int nEpoch;
2751 // The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule)
2752 AllLoggedTypesHash allLoggedTypesHash;
2756 CrstStatic AllLoggedTypes::s_cs;
2757 AllLoggedTypes * ETW::TypeSystemLog::s_pAllLoggedTypes = NULL;
2758 unsigned int ETW::TypeSystemLog::s_nEpoch = 0;
2759 BOOL ETW::TypeSystemLog::s_fHeapAllocEventEnabledOnStartup = FALSE;
2760 BOOL ETW::TypeSystemLog::s_fHeapAllocHighEventEnabledNow = FALSE;
2761 BOOL ETW::TypeSystemLog::s_fHeapAllocLowEventEnabledNow = FALSE;
2762 int ETW::TypeSystemLog::s_nCustomMsBetweenEvents = 0;
2765 //---------------------------------------------------------------------------------------
2767 // Initializes TypeSystemLog (specifically its crst). Called just before ETW providers
2768 // are registered with the OS
2771 // HRESULT indicating success or failure
2775 HRESULT ETW::TypeSystemLog::PreRegistrationInit()
2777 LIMITED_METHOD_CONTRACT;
2779 if (!AllLoggedTypes::s_cs.InitNoThrow(
2781 CRST_UNSAFE_ANYMODE)) // This lock is taken during a GC while walking the heap
2789 //---------------------------------------------------------------------------------------
2791 // Initializes TypeSystemLog (specifically its crst). Called just after ETW providers
2792 // are registered with the OS
2795 // HRESULT indicating success or failure
2799 void ETW::TypeSystemLog::PostRegistrationInit()
2801 LIMITED_METHOD_CONTRACT;
2803 // Initialize our "current state" BOOLs that remember if low or high allocation
2804 // sampling is turned on
2805 s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD);
2806 s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD);
2808 // Snapshot the current state of the object allocated keyword (on startup), and rely
2809 // on this snapshot for the rest of the process run. Since these events require the
2810 // slow alloc JIT helper to be enabled, and that can only be done on startup, we
2811 // remember in this BOOL that we did so, so that we can prevent the object allocated
2812 // event from being fired if the fast allocation helper were enabled but had to
2813 // degrade down to the slow helper (e.g., thread ran over its allocation limit). This
2814 // keeps things consistent.
2815 s_fHeapAllocEventEnabledOnStartup = (s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow);
2817 if (s_fHeapAllocEventEnabledOnStartup)
2819 // Determine if a COMPLUS env var is overriding the frequency for the sampled
2820 // object allocated events
2822 // Config value intentionally typed as string, b/c DWORD intepretation is hard-coded
2823 // to hex, which is not what the user would expect. This way I can force the
2824 // conversion to use decimal.
2825 NewArrayHolder<WCHAR> wszCustomObjectAllocationEventsPerTypePerSec(NULL);
2826 if (FAILED(CLRConfig::GetConfigValue(
2827 CLRConfig::UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec,
2828 &wszCustomObjectAllocationEventsPerTypePerSec)) ||
2829 (wszCustomObjectAllocationEventsPerTypePerSec == NULL))
2834 DWORD dwCustomObjectAllocationEventsPerTypePerSec = wcstoul(
2835 wszCustomObjectAllocationEventsPerTypePerSec,
2837 10 // Base 10 conversion
2840 if (dwCustomObjectAllocationEventsPerTypePerSec == ULONG_MAX)
2841 dwCustomObjectAllocationEventsPerTypePerSec = 0;
2842 if (dwCustomObjectAllocationEventsPerTypePerSec != 0)
2844 // MsBetweenEvents = (1000 ms/sec) / (custom desired events/sec)
2845 s_nCustomMsBetweenEvents = 1000 / dwCustomObjectAllocationEventsPerTypePerSec;
2851 //---------------------------------------------------------------------------------------
2853 // Update object allocation sampling frequency and / or Type hash table contents based
2854 // on what keywords were changed.
2858 void ETW::TypeSystemLog::OnKeywordsChanged()
2860 LIMITED_METHOD_CONTRACT;
2862 // If the desired frequencey for the GCSampledObjectAllocation events has changed,
2863 // update our state.
2864 s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD);
2865 s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD);
2867 // FUTURE: Would be nice here to log an error event if (s_fHeapAllocLowEventEnabledNow ||
2868 // s_fHeapAllocHighEventEnabledNow), but !s_fHeapAllocEventEnabledOnStartup
2870 // If the type events should be turned off, eliminate the hash tables that tracked
2871 // which types were logged. (If type events are turned back on later, we'll re-log
2872 // them all as we encounter them.) Note that all we can really test for is that the
2873 // Types keyword on the runtime provider is off. Not necessarily that it was on and
2874 // was just turned off with this request. But either way, TypeSystemLog can handle it
2875 // because it is extremely smart.
2876 if (!ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_TYPE_KEYWORD))
2877 OnTypesKeywordTurnedOff();
2881 //---------------------------------------------------------------------------------------
2883 // Based on keywords alone, determine the what the default sampling rate should be for
2884 // object allocation events. (This function does not consider any COMPLUS overrides for
2885 // the sampling rate.)
2889 int ETW::TypeSystemLog::GetDefaultMsBetweenEvents()
2891 LIMITED_METHOD_CONTRACT;
2893 // We should only get here if the allocation event is enabled. In spirit, this assert
2894 // is correct, but a race could cause the assert to fire (if someone toggled the
2895 // event off after we decided that the event was on and we started down the path of
2896 // calculating statistics to fire the event). In such a case we'll end up returning
2897 // k_nDefaultMsBetweenEventsLow below, but next time we won't get here as we'll know
2898 // early enough not to fire the event.
2899 //_ASSERTE(IsHeapAllocEventEnabled());
2901 // MsBetweenEvents = (1000 ms/sec) / (desired events/sec)
2902 const int k_nDefaultMsBetweenEventsHigh = 1000 / 100; // 100 events per type per sec
2903 const int k_nDefaultMsBetweenEventsLow = 1000 / 5; // 5 events per type per sec
2905 // If both are set, High takes precedence
2906 if (s_fHeapAllocHighEventEnabledNow)
2908 return k_nDefaultMsBetweenEventsHigh;
2910 return k_nDefaultMsBetweenEventsLow;
2913 //---------------------------------------------------------------------------------------
2915 // Use this to decide whether to fire the object allocation event
2918 // nonzero iff we should fire the event.
2922 BOOL ETW::TypeSystemLog::IsHeapAllocEventEnabled()
2924 LIMITED_METHOD_CONTRACT;
2927 // Only fire the event if it was enabled at startup (and thus the slow-JIT new
2928 // helper is used in all cases)
2929 s_fHeapAllocEventEnabledOnStartup &&
2931 // AND a keyword is still enabled. (Thus people can turn off the event
2932 // whenever they want; but they cannot turn it on unless it was also on at startup.)
2933 (s_fHeapAllocHighEventEnabledNow || s_fHeapAllocLowEventEnabledNow);
2936 //---------------------------------------------------------------------------------------
2938 // Helper that adds (or updates) the TypeLoggingInfo inside the inner hash table passed
2942 // * pLoggedTypesFromModule - Inner hash table to update
2943 // * pTypeLoggingInfo - TypeLoggingInfo to store
2946 // nonzero iff the add/replace was successful.
2950 BOOL ETW::TypeSystemLog::AddOrReplaceTypeLoggingInfo(ETW::LoggedTypesFromModule * pLoggedTypesFromModule, const ETW::TypeLoggingInfo * pTypeLoggingInfo)
2952 LIMITED_METHOD_CONTRACT;
2954 _ASSERTE(pLoggedTypesFromModule != NULL);
2956 BOOL fSucceeded = FALSE;
2959 pLoggedTypesFromModule->loggedTypesFromModuleHash.AddOrReplace(*pTypeLoggingInfo);
2966 EX_END_CATCH(RethrowCorruptingExceptions);
2971 //---------------------------------------------------------------------------------------
2973 // Records stats about the object's allocation, and determines based on those stats whether
2974 // to fires the high / low frequency GCSampledObjectAllocation ETW event
2977 // * pObject - Allocated object to log
2978 // * th - TypeHandle for the object
2982 void ETW::TypeSystemLog::SendObjectAllocatedEvent(Object * pObject)
2992 // No-op if the appropriate keywords were not enabled on startup (or we're not yet
2994 if (!s_fHeapAllocEventEnabledOnStartup || !g_fEEStarted)
2997 TypeHandle th = pObject->GetTypeHandle();
2999 SIZE_T size = pObject->GetSize();
3000 if (size < MIN_OBJECT_SIZE)
3002 size = PtrAlign(size);
3005 SIZE_T nTotalSizeForTypeSample = size;
3006 DWORD dwTickNow = GetTickCount();
3007 DWORD dwObjectCountForTypeSample = 0;
3009 // Get stats for type
3010 TypeLoggingInfo typeLoggingInfo(NULL);
3011 LoggedTypesFromModule * pLoggedTypesFromModule = NULL;
3012 BOOL fCreatedNew = FALSE;
3013 typeLoggingInfo = LookupOrCreateTypeLoggingInfo(th, &fCreatedNew, &pLoggedTypesFromModule);
3014 if (typeLoggingInfo.th.IsNull())
3017 // Update stats with current allocation
3018 typeLoggingInfo.dwAllocsSkippedForSample++;
3019 typeLoggingInfo.cbIgnoredSizeForSample += size;
3021 // If both the high and low verbosity keywords are enabled, log all allocations.
3022 if (!(s_fHeapAllocHighEventEnabledNow && s_fHeapAllocLowEventEnabledNow))
3024 // Get the number of threads so that we can scale the per-thread sampling data.
3025 // NOTE: We don't do this while holding the thread store lock, so this may not be perfect,
3026 // but it will be close enough.
3027 LONG numThreads = ThreadStore::s_pThreadStore->ThreadCountInEE();
3029 // This is our filter. If we should ignore this alloc, then record our updated
3030 // our stats, and bail without sending the event. Note that we always log objects
3031 // over 10K in size.
3032 if (size < 10000 && typeLoggingInfo.dwAllocsSkippedForSample < (typeLoggingInfo.dwAllocsToSkipPerSample * numThreads))
3034 // Update hash table's copy of type logging info with these values. It is not optimal that
3035 // we're doing another hash table lookup here. Could instead have used LookupPtr()
3036 // if it gave us back a non-const pointer, and then we could have updated in-place
3037 AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo);
3040 // Although we're skipping logging the allocation, we still need to log
3041 // the type (so it's available for resolving future allocation events to
3044 // (See other call to LogTypeAndParametersIfNecessary further down for
3046 LogTypeAndParametersIfNecessary(
3049 kTypeLogBehaviorAlwaysLogTopLevelType);
3054 // Based on observed allocation stats, adjust our sampling rate for this type
3056 typeLoggingInfo.dwAllocCountInCurrentBucket += typeLoggingInfo.dwAllocsSkippedForSample;
3057 int delta = (dwTickNow - typeLoggingInfo.dwTickOfCurrentTimeBucket) & 0x7FFFFFFF; // make wrap around work.
3059 int nMinAllocPerMSec = typeLoggingInfo.dwAllocCountInCurrentBucket / 16 / numThreads; // This is an underestimation of the true rate.
3060 if (delta >= 16 || (nMinAllocPerMSec > 2 && nMinAllocPerMSec > typeLoggingInfo.flAllocPerMSec * 1.5F))
3062 float flNewAllocPerMSec = 0;
3065 // This is the normal case, our allocation rate is under control with the current throttling.
3066 flNewAllocPerMSec = ((float) typeLoggingInfo.dwAllocCountInCurrentBucket) / delta;
3067 // Do a exponential decay window that is 5 * max(16, AllocationInterval)
3068 typeLoggingInfo.flAllocPerMSec = 0.8F * typeLoggingInfo.flAllocPerMSec + 0.2F * flNewAllocPerMSec;
3069 typeLoggingInfo.dwTickOfCurrentTimeBucket = dwTickNow;
3070 typeLoggingInfo.dwAllocCountInCurrentBucket = 0;
3074 flNewAllocPerMSec = (float) nMinAllocPerMSec;
3075 // This means the second clause above is true, which means our sampling rate is too low
3076 // so we need to throttle quickly.
3077 typeLoggingInfo.flAllocPerMSec = flNewAllocPerMSec;
3081 // Obey the desired sampling rate, but don't ignore > 1000 allocations per second
3083 int nDesiredMsBetweenEvents = (s_nCustomMsBetweenEvents == 0) ? GetDefaultMsBetweenEvents() : s_nCustomMsBetweenEvents;
3084 typeLoggingInfo.dwAllocsToSkipPerSample = min((int) (typeLoggingInfo.flAllocPerMSec * nDesiredMsBetweenEvents), 1000);
3085 if (typeLoggingInfo.dwAllocsToSkipPerSample == 1)
3086 typeLoggingInfo.dwAllocsToSkipPerSample = 0;
3090 // We're logging this sample, so save the values we need into locals, and reset
3091 // our counts for the next sample.
3092 nTotalSizeForTypeSample = typeLoggingInfo.cbIgnoredSizeForSample;
3093 dwObjectCountForTypeSample = typeLoggingInfo.dwAllocsSkippedForSample;
3094 typeLoggingInfo.cbIgnoredSizeForSample = 0;
3095 typeLoggingInfo.dwAllocsSkippedForSample = 0;
3097 // Save updated stats into hash table
3098 if (!AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo))
3103 // While we're still holding the crst, optionally log any relevant Types now (we may need
3104 // to reconsult the hash in here if there are any type parameters, though we can
3105 // optimize and NOT consult the hash for th itself).
3108 // We were the ones to add the Type to the hash. So it wasn't there before,
3109 // which means it hasn't been logged yet.
3110 LogTypeAndParametersIfNecessary(
3112 // No BulkTypeEventLogger, as we're not batching during a GC heap walk
3117 // We've determined the type is not yet logged, so no need to check
3118 kTypeLogBehaviorAlwaysLogTopLevelType);
3121 // Now log the allocation
3122 if (s_fHeapAllocHighEventEnabledNow)
3124 FireEtwGCSampledObjectAllocationHigh(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId());
3128 FireEtwGCSampledObjectAllocationLow(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId());
3132 //---------------------------------------------------------------------------------------
3134 // Accessor for global hash table crst
3137 // global hash table crst
3141 CrstBase * ETW::TypeSystemLog::GetHashCrst()
3143 LIMITED_METHOD_CONTRACT;
3144 return &AllLoggedTypes::s_cs;
3147 //---------------------------------------------------------------------------------------
3149 // Outermost level of ETW-type-logging. Clients outside eventtrace.cpp call this to log
3150 // a TypeHandle and (recursively) its type parameters when present. This guy then calls
3151 // into the appropriate BulkTypeEventLogger to do the batching and logging
3154 // * pBulkTypeEventLogger - If our caller is keeping track of batched types, it
3155 // passes this to us so we can use it to batch the current type (GC heap walk
3156 // does this). If this is NULL, no batching is going on (e.g., we're called on
3157 // object allocation, not a GC heal walk), in which case we create our own
3158 // temporary BulkTypeEventLogger.
3159 // * thAsAddr - TypeHandle to batch
3160 // * typeLogBehavior - Optimization to tell us we don't need to enter the
3161 // TypeSystemLog's crst, as the TypeSystemLog's hash table is already protected
3162 // by a prior acquisition of the crst by our caller. (Or that we don't even
3163 // need to check the hash in the first place.)
3167 VOID ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pLogger, ULONGLONG thAsAddr, TypeLogBehavior typeLogBehavior)
3175 // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to
3180 if (!ETW_TRACING_CATEGORY_ENABLED(
3181 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3182 TRACE_LEVEL_INFORMATION,
3188 TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr);
3189 if (!th.IsRestored())
3194 // Check to see if we've already logged this type. If so, bail immediately.
3195 // Otherwise, mark that it's getting logged (by adding it to the hash), and fall
3196 // through to the logging code below. If caller doesn't care, then don't even
3197 // check; just log the type
3198 BOOL fShouldLogType = ((typeLogBehavior == kTypeLogBehaviorAlwaysLog) ||
3199 (typeLogBehavior == kTypeLogBehaviorAlwaysLogTopLevelType)) ?
3202 if (!fShouldLogType)
3205 if (pLogger == NULL)
3207 // We're not batching this type against previous types (e.g., we're being called
3208 // on object allocate instead of a GC heap walk). So create a temporary logger
3209 // on the stack. If there are generic parameters that need to be logged, then
3210 // at least they'll get batched together with the type
3211 BulkTypeEventLogger logger;
3212 logger.LogTypeAndParameters(thAsAddr, typeLogBehavior);
3214 // Since this logger isn't being used to batch anything else, flush what we have
3215 logger.FireBulkTypeEvent();
3219 // We are batching this type with others (e.g., we're being called at the end of
3220 // a GC on a heap walk). So use the logger our caller set up for us.
3221 pLogger->LogTypeAndParameters(thAsAddr, typeLogBehavior);
3226 //---------------------------------------------------------------------------------------
3228 // Ask hash table if we've already logged the type, without first acquiring the lock
3229 // (our caller already did this). As a side-effect, a TypeLoggingInfo will be created
3230 // for this type (so future calls to this function will return FALSE to avoid dupe type
3234 // pth - TypeHandle to query
3237 // nonzero iff type should be logged (i.e., not previously logged)
3241 BOOL ETW::TypeSystemLog::ShouldLogType(TypeHandle th)
3253 // Check to see if TypeLoggingInfo exists yet for th. If not, creates one and
3254 // adds it to the hash.
3255 BOOL fCreatedNew = FALSE;
3257 // When we have a thread context, default to calling the API that requires one which
3258 // reduces the cost of locking.
3259 if (GetThread() != NULL)
3261 LookupOrCreateTypeLoggingInfo(th, &fCreatedNew);
3265 AddTypeToGlobalCacheIfNotExists(th, &fCreatedNew);
3268 // Return whether we had to create the TypeLoggingInfo (indicating it was not yet in
3269 // the hash, and thus that we hadn't yet logged the type).
3274 //---------------------------------------------------------------------------------------
3276 // Helper that returns (creating if necessary) the TypeLoggingInfo in the hash table
3277 // corresponding with the specified TypeHandle
3280 // * th - Key to lookup the TypeLoggingInfo
3281 // * pfCreatedNew - [out] Points to nonzero iff a new TypeLoggingInfo was created
3282 // (i.e., none existed yet in the hash for th).
3283 // * ppLoggedTypesFromModule - [out] Points to the inner hash that was used to do
3284 // the lookup. (An otpimization so the caller doesn't have to find this again,
3285 // if it needs to do further operations on it.)
3288 // TypeLoggingInfo found or created.
3293 ETW::TypeLoggingInfo ETW::TypeSystemLog::LookupOrCreateTypeLoggingInfo(TypeHandle th, BOOL * pfCreatedNew, LoggedTypesFromModule ** ppLoggedTypesFromModule /* = NULL */)
3295 //LIMITED_METHOD_CONTRACT;
3304 _ASSERTE(pfCreatedNew != NULL);
3306 if (ppLoggedTypesFromModule != NULL)
3308 *ppLoggedTypesFromModule = NULL;
3311 BOOL fSucceeded = FALSE;
3313 Thread *pThread = GetThread();
3315 // Compare the thread local epoch value against the global epoch.
3316 // If the epoch has changed, dump the thread local state and start over.
3317 AllLoggedTypes * pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3318 if((pThreadAllLoggedTypes != NULL) && (pThreadAllLoggedTypes->nEpoch != s_nEpoch))
3320 // Set the type hash pointer on the thread to NULL.
3321 pThread->SetAllocationSamplingTable(NULL);
3323 // DeleteTypeHashNoLock will set pThreadAllLoggedTypes to NULL
3324 DeleteTypeHashNoLock(&pThreadAllLoggedTypes);
3327 // Create the thread local state if it doesn't exist.
3328 if (pThreadAllLoggedTypes == NULL)
3330 pThreadAllLoggedTypes = new (nothrow) AllLoggedTypes;
3331 if (pThreadAllLoggedTypes == NULL)
3333 // out of memory. Bail on ETW stuff
3334 *pfCreatedNew = FALSE;
3335 return TypeLoggingInfo(NULL);
3338 // Set the epoch so we know we can track when changes to global state occur.
3339 pThreadAllLoggedTypes->nEpoch = s_nEpoch;
3341 // Save the thread local state to the thread.
3342 pThread->SetAllocationSamplingTable(pThreadAllLoggedTypes);
3345 BOOL addTypeToGlobalList = FALSE;
3347 // Step 1: go from LoaderModule to hash of types.
3349 Module * pLoaderModule = th.GetLoaderModule();
3350 _ASSERTE(pLoaderModule != NULL);
3351 LoggedTypesFromModule * pLoggedTypesFromModule = pThreadAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule);
3352 if (pLoggedTypesFromModule == NULL)
3354 addTypeToGlobalList = TRUE;
3355 pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule);
3356 if (pLoggedTypesFromModule == NULL)
3358 // out of memory. Bail on ETW stuff
3359 *pfCreatedNew = FALSE;
3360 return TypeLoggingInfo(NULL);
3366 pThreadAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule);
3373 EX_END_CATCH(RethrowCorruptingExceptions);
3376 *pfCreatedNew = FALSE;
3377 return TypeLoggingInfo(NULL);
3381 if (ppLoggedTypesFromModule != NULL)
3383 *ppLoggedTypesFromModule = pLoggedTypesFromModule;
3386 // Step 2: From hash of types, see if our TypeHandle is there already
3387 TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th);
3388 if (!typeLoggingInfoPreexisting.th.IsNull())
3390 // Type is already hashed, so it's already logged, so we don't need to
3392 *pfCreatedNew = FALSE;
3393 return typeLoggingInfoPreexisting;
3396 // We haven't logged this type, so we need to continue with this function to
3397 // log it below. Add it to the hash table first so any recursive calls will
3398 // see that this type is already being taken care of
3399 addTypeToGlobalList = TRUE;
3401 TypeLoggingInfo typeLoggingInfoNew(th);
3404 pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew);
3411 EX_END_CATCH(RethrowCorruptingExceptions);
3414 *pfCreatedNew = FALSE;
3415 return TypeLoggingInfo(NULL);
3418 // This is the first time that we've seen this type on this thread, so we should attempt to
3419 // add it to the global list.
3420 if(!AddTypeToGlobalCacheIfNotExists(th, pfCreatedNew))
3422 // out of memory or ETW has been disabled. Bail on ETW stuff
3423 *pfCreatedNew = FALSE;
3424 return TypeLoggingInfo(NULL);
3427 return typeLoggingInfoNew;
3430 //---------------------------------------------------------------------------------------
3432 // Helper that creates a Type entry in the global type logging cache if one doesn't
3436 // * th - Key to lookup or create
3439 // TRUE if the type needed to be added to the cache.
3444 BOOL ETW::TypeSystemLog::AddTypeToGlobalCacheIfNotExists(TypeHandle th, BOOL * pfCreatedNew)
3454 BOOL fSucceeded = FALSE;
3457 CrstHolder _crst(GetHashCrst());
3459 // Check if ETW is enabled, and if not, bail here.
3460 // We do this inside of the lock to ensure that we don't immediately
3461 // re-allocate the global type hash after it has been cleaned up.
3462 if (!ETW_TRACING_CATEGORY_ENABLED(
3463 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3464 TRACE_LEVEL_INFORMATION,
3467 *pfCreatedNew = FALSE;
3471 if (s_pAllLoggedTypes == NULL)
3473 s_pAllLoggedTypes = new (nothrow) AllLoggedTypes;
3474 if (s_pAllLoggedTypes == NULL)
3476 // out of memory. Bail on ETW stuff
3477 *pfCreatedNew = FALSE;
3482 // Step 1: go from LoaderModule to hash of types.
3484 Module * pLoaderModule = th.GetLoaderModule();
3485 _ASSERTE(pLoaderModule != NULL);
3486 LoggedTypesFromModule * pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule);
3487 if (pLoggedTypesFromModule == NULL)
3489 pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule);
3490 if (pLoggedTypesFromModule == NULL)
3492 // out of memory. Bail on ETW stuff
3493 *pfCreatedNew = FALSE;
3500 s_pAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule);
3507 EX_END_CATCH(RethrowCorruptingExceptions);
3510 *pfCreatedNew = FALSE;
3515 // Step 2: From hash of types, see if our TypeHandle is there already
3516 TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th);
3517 if (!typeLoggingInfoPreexisting.th.IsNull())
3519 // Type is already hashed, so it's already logged, so we don't need to
3521 *pfCreatedNew = FALSE;
3525 // We haven't logged this type, so we need to continue with this function to
3526 // log it below. Add it to the hash table first so any recursive calls will
3527 // see that this type is already being taken care of
3529 TypeLoggingInfo typeLoggingInfoNew(th);
3532 pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew);
3539 EX_END_CATCH(RethrowCorruptingExceptions);
3542 *pfCreatedNew = FALSE;
3545 } // RELEASE: CrstHolder _crst(GetHashCrst());
3547 *pfCreatedNew = TRUE;
3552 //---------------------------------------------------------------------------------------
3554 // Called when we determine if a module was unloaded, so we can clear out that module's
3555 // set of types from our hash table
3558 // pModule - Module getting unloaded
3562 VOID ETW::TypeSystemLog::OnModuleUnload(Module * pModule)
3573 // We don't need to do anything if allocation sampling is disabled.
3574 if (!ETW_TRACING_CATEGORY_ENABLED(
3575 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3576 TRACE_LEVEL_INFORMATION,
3582 LoggedTypesFromModule * pLoggedTypesFromModule = NULL;
3585 CrstHolder _crst(GetHashCrst());
3587 // We don't need to do anything if the global type hash doesn't contain any data.
3588 if (s_pAllLoggedTypes == NULL)
3591 // Is there a TypesHash for this module?
3592 pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pModule);
3593 if (pLoggedTypesFromModule == NULL)
3596 // Remove TypesHash from master hash mapping modules to their TypesHash
3597 s_pAllLoggedTypes->allLoggedTypesHash.Remove(pModule);
3599 // Increment the epoch to signal the change to all threads.
3603 // Destruct this TypesHash we just removed
3604 delete pLoggedTypesFromModule;
3605 pLoggedTypesFromModule = NULL;
3609 //---------------------------------------------------------------------------------------
3611 // Same semantics as DeleteTypeHash but assumes that the appropriate lock
3612 // has already been acquired.
3616 VOID ETW::TypeSystemLog::DeleteTypeHashNoLock(AllLoggedTypes **ppAllLoggedTypes)
3618 LIMITED_METHOD_CONTRACT;
3620 if(ppAllLoggedTypes == NULL)
3625 AllLoggedTypes *pAllLoggedTypes = *ppAllLoggedTypes;
3627 if(pAllLoggedTypes == NULL)
3632 // Destruct each of the per-module TypesHashes
3633 AllLoggedTypesHash * pLoggedTypesHash = &pAllLoggedTypes->allLoggedTypesHash;
3634 for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin();
3635 iter != pLoggedTypesHash->End();
3638 LoggedTypesFromModule * pLoggedTypesFromModule = *iter;
3639 delete pLoggedTypesFromModule;
3642 // This causes the default ~AllLoggedTypes() to be called, and thus
3643 // ~AllLoggedTypesHash() to be called
3644 delete pAllLoggedTypes;
3645 *ppAllLoggedTypes = NULL;
3648 //---------------------------------------------------------------------------------------
3650 // Called from shutdown to give us the opportunity to dump any sampled object allocation
3651 // information before the process shuts down.
3655 VOID ETW::TypeSystemLog::FlushObjectAllocationEvents()
3666 // If logging is not enabled, then we don't need to do any work.
3667 if (!(s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow))
3672 AllLoggedTypes * pThreadAllLoggedTypes = NULL;
3673 Thread * pThread = NULL;
3675 // Get the thread store lock.
3676 ThreadStoreLockHolder tsl;
3678 // Iterate over each thread and log any un-logged allocations.
3679 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
3681 pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3682 if (pThreadAllLoggedTypes == NULL)
3687 DWORD dwAllocsSkippedForSample;
3688 SIZE_T cbIgnoredSizeForSample;
3690 // Iterate over each module.
3691 AllLoggedTypesHash * pLoggedTypesHash = &pThreadAllLoggedTypes->allLoggedTypesHash;
3692 for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin();
3693 iter != pLoggedTypesHash->End();
3696 // Iterate over each type in the module.
3697 LoggedTypesFromModule * pLoggedTypesFromModule = *iter;
3698 LoggedTypesFromModuleHash * pLoggedTypesFromModuleHash = &pLoggedTypesFromModule->loggedTypesFromModuleHash;
3699 for (LoggedTypesFromModuleHash::Iterator typeIter = pLoggedTypesFromModuleHash->Begin();
3700 typeIter != pLoggedTypesFromModuleHash->End();
3703 dwAllocsSkippedForSample = typeIter->dwAllocsSkippedForSample;
3704 cbIgnoredSizeForSample = typeIter->cbIgnoredSizeForSample;
3706 // Only write the event if there were allocations that have not been logged.
3707 if (dwAllocsSkippedForSample > 0 || cbIgnoredSizeForSample > 0)
3709 // Write the event based on which keyword was specified when ETW was configured.
3710 if (s_fHeapAllocHighEventEnabledNow)
3712 FireEtwGCSampledObjectAllocationHigh(NULL, (LPVOID) typeIter->th.AsTAddr(), dwAllocsSkippedForSample, cbIgnoredSizeForSample, GetClrInstanceId());
3716 FireEtwGCSampledObjectAllocationLow(NULL, (LPVOID) typeIter->th.AsTAddr(), dwAllocsSkippedForSample, cbIgnoredSizeForSample, GetClrInstanceId());
3724 //---------------------------------------------------------------------------------------
3726 // Whenever we detect that the Types keyword is off, this gets called. This eliminates the
3727 // global hash tables that tracked which types were logged (if the hash tables had been created
3728 // previously). If type events are turned back on later, we'll re-log them all as we
3729 // encounter them. Thread local hash tables are destroyed in the Cleanup method, which is
3730 // called during GC to ensure that there aren't any races.
3734 VOID ETW::TypeSystemLog::OnTypesKeywordTurnedOff()
3745 // Take the global cache lock.
3746 CrstHolder _crst(GetHashCrst());
3748 // Clean-up the global TypeHash if necessary.
3749 if (s_pAllLoggedTypes == NULL)
3751 // Even if we don't increment the epoch, but we get into a situation where
3752 // some per thread data has been allocated, it will be cleaned up during the
3753 // next GC because we are guaranteed that s_nEpoch has been incremented at
3754 // least once (to shutdown allocation sampling).
3758 // Destruct the global TypeHash
3759 DeleteTypeHashNoLock(&s_pAllLoggedTypes);
3761 // Increment the epoch to signal the change to all threads.
3765 //---------------------------------------------------------------------------------------
3767 // Clean-up thread local type hashes. This is called from within the GC to ensure that
3768 // there are no races. All threads are suspended when this is called.
3772 VOID ETW::TypeSystemLog::Cleanup()
3782 // If allocation sampling is enabled, bail here so that we don't delete
3783 // any of the thread local state.
3784 if (ETW_TRACING_CATEGORY_ENABLED(
3785 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3786 TRACE_LEVEL_INFORMATION,
3792 // If logging is disabled but the epoch has not been incremented,
3793 // we haven't ever turned on allocation sampling, so there is nothing
3800 // Iterate over each thread and destruct the per thread caches
3801 AllLoggedTypes * pThreadAllLoggedTypes = NULL;
3802 Thread * pThread = NULL;
3803 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
3805 pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3806 if(pThreadAllLoggedTypes == NULL)
3811 // Destruct each of the thread local TypesHashes
3812 DeleteTypeHashNoLock(&pThreadAllLoggedTypes);
3814 // Set the thread type hash pointer to NULL
3815 pThread->SetAllocationSamplingTable(NULL);
3820 /****************************************************************************/
3821 /* Called when ETW is turned ON on an existing process and ModuleRange events are to
3823 /****************************************************************************/
3824 VOID ETW::EnumerationLog::ModuleRangeRundown()
3833 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
3834 TRACE_LEVEL_INFORMATION,
3835 CLR_PERFTRACK_PRIVATE_KEYWORD))
3837 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate);
3839 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
3843 /****************************************************************************/
3844 /* Called when ETW is turned ON on an existing process */
3845 /****************************************************************************/
3846 VOID ETW::EnumerationLog::StartRundown()
3855 BOOL bIsArmRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3856 TRACE_LEVEL_INFORMATION,
3857 CLR_RUNDOWNAPPDOMAINRESOURCEMANAGEMENT_KEYWORD);
3858 BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3859 TRACE_LEVEL_INFORMATION,
3860 CLR_RUNDOWNPERFTRACK_KEYWORD);
3861 BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(
3862 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3863 TRACE_LEVEL_INFORMATION,
3864 CLR_RUNDOWNTHREADING_KEYWORD);
3866 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3867 TRACE_LEVEL_INFORMATION,
3868 CLR_RUNDOWNJIT_KEYWORD)
3870 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3871 TRACE_LEVEL_INFORMATION,
3872 CLR_RUNDOWNLOADER_KEYWORD)
3874 IsRundownNgenKeywordEnabledAndNotSuppressed()
3876 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3877 TRACE_LEVEL_INFORMATION,
3878 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)
3880 bIsArmRundownEnabled
3882 bIsPerfTrackRundownEnabled
3884 bIsThreadingRundownEnabled)
3886 // begin marker event will go to the rundown provider
3887 FireEtwDCStartInit_V1(GetClrInstanceId());
3889 // The rundown flag is expected to be checked in the caller, so no need to check here again
3890 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
3891 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3892 TRACE_LEVEL_INFORMATION,
3893 CLR_RUNDOWNLOADER_KEYWORD))
3895 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart;
3897 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3898 TRACE_LEVEL_INFORMATION,
3899 CLR_RUNDOWNJIT_KEYWORD))
3901 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart;
3903 if(IsRundownNgenKeywordEnabledAndNotSuppressed())
3905 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart;
3907 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3908 TRACE_LEVEL_INFORMATION,
3909 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD))
3911 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap;
3913 if(bIsPerfTrackRundownEnabled)
3915 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart;
3918 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions);
3920 if (bIsArmRundownEnabled)
3922 // When an ETW event consumer asks for ARM rundown, that not only enables
3923 // the ETW events, but also causes some minor behavioral changes in the
3924 // CLR, such as gathering CPU usage baselines for each thread right now,
3925 // and also gathering resource usage information later on (keyed off of
3926 // g_fEnableARM, which we'll set right now).
3930 if (bIsArmRundownEnabled || bIsThreadingRundownEnabled)
3932 SendThreadRundownEvent();
3935 // end marker event will go to the rundown provider
3936 FireEtwDCStartComplete_V1(GetClrInstanceId());
3938 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
3941 //---------------------------------------------------------------------------------------
3943 // Simple helper to convert the currently active keywords on the runtime provider into a
3944 // bitmask of enumeration options as defined in ETW::EnumerationLog::EnumerationStructs
3947 // ETW::EnumerationLog::EnumerationStructs bitmask corresponding to the currently
3948 // active keywords on the runtime provider
3952 DWORD ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords()
3954 LIMITED_METHOD_CONTRACT;
3956 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
3957 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3958 TRACE_LEVEL_INFORMATION,
3959 CLR_LOADER_KEYWORD))
3961 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload;
3963 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3964 TRACE_LEVEL_INFORMATION,
3966 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3967 TRACE_LEVEL_INFORMATION,
3968 CLR_ENDENUMERATION_KEYWORD))
3970 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodUnload;
3972 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed() &&
3973 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3974 TRACE_LEVEL_INFORMATION,
3975 CLR_ENDENUMERATION_KEYWORD))
3977 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload;
3980 return enumerationOptions;
3983 //---------------------------------------------------------------------------------------
3985 // Executes a flavor of rundown initiated by a CAPTURE_STATE request to
3986 // code:#EtwCallback. CAPTURE_STATE is the "ETW-sanctioned" way of performing a
3987 // rundown, whereas the CLR's rundown provider was *our* version of this, implemented
3988 // before CAPTURE_STATE was standardized.
3990 // When doing a CAPTURE_STATE, the CLR rundown provider is completely unused. Instead,
3991 // we pay attention to the runtime keywords active at the time the CAPTURE_STATE was
3992 // requested, and enumerate through the appropriate objects (AppDomains, assemblies,
3993 // modules, types, methods, threads) and send runtime events for each of them.
3995 // CAPTURE_STATE is intended to be used primarily by PerfTrack. Implementing this form
3996 // of rundown allows PerfTrack to be blissfully unaware of the CLR's rundown provider.
4000 VOID ETW::EnumerationLog::EnumerateForCaptureState()
4011 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO))
4013 DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords();
4015 // Send unload events for all remaining domains, including shared domain and
4017 ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions);
4019 // Send thread created events for all currently active threads, if requested
4020 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4021 TRACE_LEVEL_INFORMATION,
4022 CLR_THREADING_KEYWORD))
4024 SendThreadRundownEvent();
4027 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4030 /**************************************************************************************/
4031 /* Called when ETW is turned OFF on an existing process .Will be used by the controller for end rundown*/
4032 /**************************************************************************************/
4033 VOID ETW::EnumerationLog::EndRundown()
4042 BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4043 TRACE_LEVEL_INFORMATION,
4044 CLR_RUNDOWNPERFTRACK_KEYWORD);
4045 BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(
4046 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4047 TRACE_LEVEL_INFORMATION,
4048 CLR_RUNDOWNTHREADING_KEYWORD);
4049 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4050 TRACE_LEVEL_INFORMATION,
4051 CLR_RUNDOWNJIT_KEYWORD)
4053 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4054 TRACE_LEVEL_INFORMATION,
4055 CLR_RUNDOWNLOADER_KEYWORD)
4057 IsRundownNgenKeywordEnabledAndNotSuppressed()
4059 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4060 TRACE_LEVEL_INFORMATION,
4061 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)
4063 bIsPerfTrackRundownEnabled
4065 bIsThreadingRundownEnabled
4068 // begin marker event will go to the rundown provider
4069 FireEtwDCEndInit_V1(GetClrInstanceId());
4071 // The rundown flag is expected to be checked in the caller, so no need to check here again
4072 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
4073 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4074 TRACE_LEVEL_INFORMATION,
4075 CLR_RUNDOWNLOADER_KEYWORD))
4077 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd;
4079 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4080 TRACE_LEVEL_INFORMATION,
4081 CLR_RUNDOWNJIT_KEYWORD))
4083 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd;
4085 if(IsRundownNgenKeywordEnabledAndNotSuppressed())
4087 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd;
4089 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4090 TRACE_LEVEL_INFORMATION,
4091 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD))
4093 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap;
4095 if(bIsPerfTrackRundownEnabled)
4097 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd;
4100 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions);
4102 if (bIsThreadingRundownEnabled)
4104 SendThreadRundownEvent();
4107 // end marker event will go to the rundown provider
4108 FireEtwDCEndComplete_V1(GetClrInstanceId());
4111 STRESS_LOG1(LF_ALWAYS, LL_ERROR, "Exception during Rundown Enumeration, EIP of last AV = %p", g_LastAccessViolationEIP);
4112 } EX_END_CATCH(SwallowAllExceptions);
4118 Routine Description:
4120 Registers provider with ETW tracing framework.
4121 This function should not be called more than once, on
4122 Dll Process attach only.
4129 Returns the return value from RegisterTraceGuids or EventRegister.
4133 void InitializeEventTracing()
4143 // Do startup-only initialization of any state required by the ETW classes before
4144 // events can be fired
4145 HRESULT hr = ETW::TypeSystemLog::PreRegistrationInit();
4149 #if !defined(FEATURE_PAL)
4150 // Register CLR providers with the OS
4151 if (g_pEtwTracer == NULL)
4153 NewHolder <ETW::CEtwTracer> tempEtwTracer (new (nothrow) ETW::CEtwTracer());
4154 if (tempEtwTracer != NULL && tempEtwTracer->Register () == ERROR_SUCCESS)
4155 g_pEtwTracer = tempEtwTracer.Extract ();
4159 g_nClrInstanceId = GetRuntimeId() & 0x0000FFFF; // This will give us duplicate ClrInstanceId after UINT16_MAX
4161 // Any classes that need some initialization to happen after we've registered the
4162 // providers can do so now
4163 ETW::TypeSystemLog::PostRegistrationInit();
4166 // Plumbing to funnel event pipe callbacks and ETW callbacks together into a single common
4167 // handler, for the purposes of informing the GC of changes to the event state.
4169 // There is one callback for every EventPipe provider and one for all of ETW. The reason
4170 // for this is that ETW passes the registration handle of the provider that was enabled
4171 // as a field on the "CallbackContext" field of the callback, while EventPipe passes null
4172 // unless another token is given to it when the provider is constructed. In the absence of
4173 // a suitable token, this implementation has a different callback for every EventPipe provider
4174 // that ultimately funnels them all into a common handler.
4176 #if defined(FEATURE_PAL)
4177 // CLR_GCHEAPCOLLECT_KEYWORD is defined by the generated ETW manifest on Windows.
4178 // On non-Windows, we need to make sure that this is defined. Given that we can't change
4179 // the value due to compatibility, we specify it here rather than generating defines based on the manifest.
4180 #define CLR_GCHEAPCOLLECT_KEYWORD 0x800000
4181 #endif // defined(FEATURE_PAL)
4183 // CallbackProviderIndex provides a quick identification of which provider triggered the
4185 enum CallbackProviderIndex
4188 DotNETRuntimeRundown = 1,
4189 DotNETRuntimeStress = 2,
4190 DotNETRuntimePrivate = 3
4193 // Common handler for all ETW or EventPipe event notifications. Based on the provider that
4194 // was enabled/disabled, this implementation forwards the event state change onto GCHeapUtilities
4195 // which will inform the GC to update its local state about what events are enabled.
4196 VOID EtwCallbackCommon(
4197 CallbackProviderIndex ProviderIndex,
4200 ULONGLONG MatchAnyKeyword,
4203 LIMITED_METHOD_CONTRACT;
4205 bool bIsPublicTraceHandle = ProviderIndex == DotNETRuntime;
4206 #if !defined(FEATURE_PAL)
4207 static_assert(GCEventLevel_None == TRACE_LEVEL_NONE, "GCEventLevel_None value mismatch");
4208 static_assert(GCEventLevel_Fatal == TRACE_LEVEL_FATAL, "GCEventLevel_Fatal value mismatch");
4209 static_assert(GCEventLevel_Error == TRACE_LEVEL_ERROR, "GCEventLevel_Error value mismatch");
4210 static_assert(GCEventLevel_Warning == TRACE_LEVEL_WARNING, "GCEventLevel_Warning mismatch");
4211 static_assert(GCEventLevel_Information == TRACE_LEVEL_INFORMATION, "GCEventLevel_Information mismatch");
4212 static_assert(GCEventLevel_Verbose == TRACE_LEVEL_VERBOSE, "GCEventLevel_Verbose mismatch");
4213 #endif // !defined(FEATURE_PAL)
4214 GCEventKeyword keywords = static_cast<GCEventKeyword>(MatchAnyKeyword);
4215 GCEventLevel level = static_cast<GCEventLevel>(Level);
4216 GCHeapUtilities::RecordEventStateChange(bIsPublicTraceHandle, keywords, level);
4218 // Special check for the runtime provider's GCHeapCollectKeyword. Profilers
4219 // flick this to force a full GC.
4220 if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle &&
4221 ((MatchAnyKeyword & CLR_GCHEAPCOLLECT_KEYWORD) != 0))
4223 // Profilers may (optionally) specify extra data in the filter parameter
4224 // to log with the GCStart event.
4225 LONGLONG l64ClientSequenceNumber = 0;
4226 #if !defined(FEATURE_PAL)
4227 PEVENT_FILTER_DESCRIPTOR FilterData = (PEVENT_FILTER_DESCRIPTOR)pFilterData;
4228 if ((FilterData != NULL) &&
4229 (FilterData->Type == 1) &&
4230 (FilterData->Size == sizeof(l64ClientSequenceNumber)))
4232 l64ClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr);
4234 #endif // !defined(FEATURE_PAL)
4235 ETW::GCLog::ForceGC(l64ClientSequenceNumber);
4239 // Individual callbacks for each EventPipe provider.
4241 VOID EventPipeEtwCallbackDotNETRuntimeStress(
4242 _In_ LPCGUID SourceId,
4243 _In_ ULONG ControlCode,
4245 _In_ ULONGLONG MatchAnyKeyword,
4246 _In_ ULONGLONG MatchAllKeyword,
4247 _In_opt_ PVOID FilterData,
4248 _Inout_opt_ PVOID CallbackContext)
4250 LIMITED_METHOD_CONTRACT;
4252 EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData);
4255 VOID EventPipeEtwCallbackDotNETRuntime(
4256 _In_ LPCGUID SourceId,
4257 _In_ ULONG ControlCode,
4259 _In_ ULONGLONG MatchAnyKeyword,
4260 _In_ ULONGLONG MatchAllKeyword,
4261 _In_opt_ PVOID FilterData,
4262 _Inout_opt_ PVOID CallbackContext)
4264 LIMITED_METHOD_CONTRACT;
4266 EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData);
4269 VOID EventPipeEtwCallbackDotNETRuntimeRundown(
4270 _In_ LPCGUID SourceId,
4271 _In_ ULONG ControlCode,
4273 _In_ ULONGLONG MatchAnyKeyword,
4274 _In_ ULONGLONG MatchAllKeyword,
4275 _In_opt_ PVOID FilterData,
4276 _Inout_opt_ PVOID CallbackContext)
4278 LIMITED_METHOD_CONTRACT;
4280 EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData);
4283 VOID EventPipeEtwCallbackDotNETRuntimePrivate(
4284 _In_ LPCGUID SourceId,
4285 _In_ ULONG ControlCode,
4287 _In_ ULONGLONG MatchAnyKeyword,
4288 _In_ ULONGLONG MatchAllKeyword,
4289 _In_opt_ PVOID FilterData,
4290 _Inout_opt_ PVOID CallbackContext)
4292 WRAPPER_NO_CONTRACT;
4294 EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData);
4298 #if !defined(FEATURE_PAL)
4299 HRESULT ETW::CEtwTracer::Register()
4301 WRAPPER_NO_CONTRACT;
4303 EventRegisterMicrosoft_Windows_DotNETRuntime();
4304 EventRegisterMicrosoft_Windows_DotNETRuntimePrivate();
4305 EventRegisterMicrosoft_Windows_DotNETRuntimeRundown();
4307 // Stress Log ETW events are available only on the desktop version of the runtime
4309 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeHandle;
4310 MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimePrivateHandle;
4311 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeRundownHandle;
4319 Routine Description:
4320 Unregisters the provider from ETW. This function
4321 should only be called once from DllMain Detach process.
4328 Returns ERROR_SUCCESS
4331 HRESULT ETW::CEtwTracer::UnRegister()
4333 LIMITED_METHOD_CONTRACT;
4334 EventUnregisterMicrosoft_Windows_DotNETRuntime();
4335 EventUnregisterMicrosoft_Windows_DotNETRuntimePrivate();
4336 EventUnregisterMicrosoft_Windows_DotNETRuntimeRundown();
4343 VOID EtwCallout(REGHANDLE RegHandle,
4344 PCEVENT_DESCRIPTOR Descriptor,
4345 ULONG ArgumentCount,
4346 PEVENT_DATA_DESCRIPTOR EventData)
4348 WRAPPER_NO_CONTRACT;
4349 UINT8 providerIndex = 0;
4350 if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4352 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4354 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4356 } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4359 _ASSERTE(!"Provider not one of Runtime, Rundown, Private and Stress");
4363 // stacks are supposed to be fired for only the events with a bit set in the etwStackSupportedEvents bitmap
4364 if(((etwStackSupportedEvents[providerIndex][Descriptor->Id/8]) &
4365 (1<<(Descriptor->Id%8))) != 0)
4367 if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4368 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, &CLRStackWalk, &CLRStackId);
4369 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4370 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, &CLRStackWalkDCStart, &CLRStackRundownId);
4371 } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4372 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, &CLRStackWalkPrivate, &CLRStackPrivateId);
4373 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4374 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context, &CLRStackWalkStress, &CLRStackStressId);
4383 // During the build, MC generates the code to register our provider, and to register
4384 // our ETW callback. (This is buried under Intermediates, in a path like
4385 // Intermediate\clr\corguids.nativeproj_1723354836\obj1c\x86\ClrEtwAll.h.) The ETW
4386 // callback is also generated for us by MC. But we can hook into this generated
4387 // callback by #defining MCGEN_PRIVATE_ENABLE_CALLBACK_V2 to be a call to this
4388 // function (EtwCallback), thus causing EtwCallback to get called after the
4389 // MC-generated code executes.
4391 // This callback function is called whenever an ETW session is enabled or disabled. A
4392 // callback function needs to be specified when the provider is registered. C style
4393 // callback wrappers are needed during event registration. To handle the callback
4394 // action in this class, we pass "this" during provider registration and modify the
4395 // context to the relevant context in the C callback later.
4398 _In_ LPCGUID SourceId,
4399 _In_ ULONG ControlCode,
4401 _In_ ULONGLONG MatchAnyKeyword,
4402 _In_ ULONGLONG MatchAllKeyword,
4403 _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
4404 _Inout_opt_ PVOID CallbackContext)
4408 if(g_fEEStarted) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);};
4411 STATIC_CONTRACT_FAULT;
4415 // Mark that we are the special ETWRundown thread. Currently all this does
4416 // is insure that AVs thrown in this thread are treated as normal exceptions.
4417 // This allows us to catch and swallow them. We can do this because we have
4418 // a reasonably strong belief that doing ETW Rundown does not change runtime state
4419 // and thus if an AV happens it is better to simply give up logging ETW and
4420 // instead of terminating the process (which is what we would do normally)
4421 ClrFlsThreadTypeSwitch etwRundownThreadHolder(ThreadType_ETWRundownThread);
4422 PMCGEN_TRACE_CONTEXT context = (PMCGEN_TRACE_CONTEXT)CallbackContext;
4424 BOOLEAN bIsPublicTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeHandle);
4426 BOOLEAN bIsPrivateTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimePrivateHandle);
4428 BOOLEAN bIsRundownTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeRundownHandle);
4430 // EventPipeEtwCallback contains some GC eventing functionality shared between EventPipe and ETW.
4431 // Eventually, we'll want to merge these two codepaths whenever we can.
4432 CallbackProviderIndex providerIndex = DotNETRuntime;
4433 if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4434 providerIndex = DotNETRuntime;
4435 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4436 providerIndex = DotNETRuntimeRundown;
4437 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4438 providerIndex = DotNETRuntimeStress;
4439 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4440 providerIndex = DotNETRuntimePrivate;
4442 assert(!"unknown registration handle");
4446 EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData);
4448 // TypeSystemLog needs a notification when certain keywords are modified, so
4449 // give it a hook here.
4450 if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle)
4452 ETW::TypeSystemLog::OnKeywordsChanged();
4455 // A manifest based provider can be enabled to multiple event tracing sessions
4456 // As long as there is atleast 1 enabled session, IsEnabled will be TRUE
4457 // Since classic providers can be enabled to only a single session,
4458 // IsEnabled will be TRUE when it is enabled and FALSE when disabled
4460 ((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
4461 (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE));
4464 if (bIsPrivateTraceHandle)
4466 ETW::GCLog::GCSettingsEvent();
4467 if(g_fEEStarted && !g_fEEShutDown)
4469 ETW::EnumerationLog::ModuleRangeRundown();
4473 #ifdef _TARGET_AMD64_
4474 // We only do this on amd64 (NOT ARM, because ARM uses frame based stack crawling)
4475 // If we have turned on the JIT keyword to the INFORMATION setting (needed to get JIT names) then
4476 // we assume that we also want good stack traces so we need to publish unwind information so
4477 // ETW can get at it
4478 if(bIsPublicTraceHandle && ETW_CATEGORY_ENABLED((*context), TRACE_LEVEL_INFORMATION, CLR_RUNDOWNJIT_KEYWORD))
4479 UnwindInfoTable::PublishUnwindInfo(g_fEEStarted != FALSE);
4482 if(g_fEEStarted && !g_fEEShutDown && bIsRundownTraceHandle)
4484 // Fire the runtime information event
4485 ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Callback);
4487 // Start and End Method/Module Rundowns
4488 // Used to fire events that we missed since we started the controller after the process started
4489 // flags for immediate start rundown
4490 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4491 TRACE_LEVEL_INFORMATION,
4492 CLR_RUNDOWNSTART_KEYWORD))
4493 ETW::EnumerationLog::StartRundown();
4495 // flags delayed end rundown
4496 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4497 TRACE_LEVEL_INFORMATION,
4498 CLR_RUNDOWNEND_KEYWORD))
4499 ETW::EnumerationLog::EndRundown();
4502 if (g_fEEStarted && !g_fEEShutDown && (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE))
4504 ETW::EnumerationLog::EnumerateForCaptureState();
4507 #ifdef FEATURE_COMINTEROP
4508 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, CCWRefCountChange))
4509 g_pConfig->SetLogCCWRefCountChangeEnabled(bEnabled != 0);
4510 #endif // FEATURE_COMINTEROP
4514 #endif // FEATURE_REDHAWK
4516 #endif // FEATURE_PAL
4517 #ifndef FEATURE_REDHAWK
4519 /****************************************************************************/
4520 /* This is called by the runtime when an exception is thrown */
4521 /****************************************************************************/
4522 VOID ETW::ExceptionLog::ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownException, BOOL bIsNewException)
4527 PRECONDITION(GetThread() != NULL);
4528 PRECONDITION(GetThread()->GetThrowable() != NULL);
4531 if(!(bIsReThrownException || bIsNewException))
4535 if(!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionThrown_V1))
4541 SString exceptionType(W(""));
4542 LPWSTR exceptionMessage = NULL;
4543 BOOL bIsCLSCompliant=FALSE, bIsCSE=FALSE, bIsNestedException=FALSE, bHasInnerException=FALSE;
4544 UINT16 exceptionFlags=0;
4545 PVOID exceptionEIP=0;
4547 Thread *pThread = GetThread();
4551 OBJECTREF exceptionObj;
4552 OBJECTREF innerExceptionObj;
4553 STRINGREF exceptionMessageRef;
4555 ZeroMemory(&gc, sizeof(gc));
4556 GCPROTECT_BEGIN(gc);
4558 gc.exceptionObj = pThread->GetThrowable();
4559 gc.innerExceptionObj = ((EXCEPTIONREF)gc.exceptionObj)->GetInnerException();
4561 ThreadExceptionState *pExState = pThread->GetExceptionState();
4562 #ifndef WIN64EXCEPTIONS
4563 PTR_ExInfo pExInfo = NULL;
4565 PTR_ExceptionTracker pExInfo = NULL;
4566 #endif //!WIN64EXCEPTIONS
4567 pExInfo = pExState->GetCurrentExceptionTracker();
4568 _ASSERTE(pExInfo != NULL);
4569 bIsNestedException = (pExInfo->GetPreviousExceptionTracker() != NULL);
4570 bIsCSE = (pExInfo->GetCorruptionSeverity() == ProcessCorrupting);
4571 bIsCLSCompliant = IsException((gc.exceptionObj)->GetMethodTable()) &&
4572 ((gc.exceptionObj)->GetMethodTable() != MscorlibBinder::GetException(kRuntimeWrappedException));
4574 // A rethrown exception is also a nested exception
4575 // but since we have a separate flag for it, lets unset the nested flag
4576 if(bIsReThrownException)
4578 bIsNestedException = FALSE;
4580 bHasInnerException = (gc.innerExceptionObj) != NULL;
4582 exceptionFlags = ((bHasInnerException ? ETW::ExceptionLog::ExceptionStructs::HasInnerException : 0) |
4583 (bIsNestedException ? ETW::ExceptionLog::ExceptionStructs::IsNestedException : 0) |
4584 (bIsReThrownException ? ETW::ExceptionLog::ExceptionStructs::IsReThrownException : 0) |
4585 (bIsCSE ? ETW::ExceptionLog::ExceptionStructs::IsCSE : 0) |
4586 (bIsCLSCompliant ? ETW::ExceptionLog::ExceptionStructs::IsCLSCompliant : 0));
4588 if (pCf->IsFrameless())
4591 exceptionEIP = (PVOID)pCf->GetRegisterSet()->ControlPC;
4593 exceptionEIP = (PVOID)GetIP(pCf->GetRegisterSet()->pContext);
4598 exceptionEIP = (PVOID)(pCf->GetFrame()->GetIP());
4601 // On platforms other than IA64, we are at the instruction after the faulting instruction
4602 // This check has been copied from StackTraceInfo::AppendElement
4603 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && exceptionEIP != 0)
4605 exceptionEIP = (PVOID)((UINT_PTR)exceptionEIP - 1);
4608 gc.exceptionMessageRef = ((EXCEPTIONREF)gc.exceptionObj)->GetMessage();
4609 TypeHandle exceptionTypeHandle = (gc.exceptionObj)->GetTypeHandle();
4610 exceptionTypeHandle.GetName(exceptionType);
4611 WCHAR *exceptionTypeName = (WCHAR *)exceptionType.GetUnicode();
4613 if(gc.exceptionMessageRef != NULL)
4615 exceptionMessage = (gc.exceptionMessageRef)->GetBuffer();
4618 HRESULT exceptionHRESULT = ((EXCEPTIONREF)gc.exceptionObj)->GetHResult();
4620 FireEtwExceptionThrown_V1(exceptionTypeName,
4625 GetClrInstanceId());
4627 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4631 VOID ETW::ExceptionLog::ExceptionThrownEnd()
4638 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionThrownStop))
4643 FireEtwExceptionThrownStop();
4646 /****************************************************************************/
4647 /* This is called by the runtime when an exception is handled by the runtime */
4648 /****************************************************************************/
4649 VOID ETW::ExceptionLog::ExceptionCatchBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4656 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionCatchStart))
4664 pMethodDesc->GetFullMethodInfo(methodName);
4666 FireEtwExceptionCatchStart((uint64_t)pEntryEIP,
4667 (uint64_t)pMethodDesc,
4668 methodName.GetUnicode(),
4669 GetClrInstanceId());
4671 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4674 VOID ETW::ExceptionLog::ExceptionCatchEnd()
4681 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionCatchStop))
4686 FireEtwExceptionCatchStop();
4689 VOID ETW::ExceptionLog::ExceptionFinallyBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4696 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFinallyStart))
4704 pMethodDesc->GetFullMethodInfo(methodName);
4706 FireEtwExceptionFinallyStart((uint64_t)pEntryEIP,
4707 (uint64_t)pMethodDesc,
4708 methodName.GetUnicode(),
4709 GetClrInstanceId());
4711 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4714 VOID ETW::ExceptionLog::ExceptionFinallyEnd()
4721 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFinallyStop))
4726 FireEtwExceptionFinallyStop();
4729 VOID ETW::ExceptionLog::ExceptionFilterBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4736 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFilterStart))
4744 pMethodDesc->GetFullMethodInfo(methodName);
4746 FireEtwExceptionFilterStart((uint64_t)pEntryEIP,
4747 (uint64_t)pMethodDesc,
4748 methodName.GetUnicode(),
4749 GetClrInstanceId());
4751 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4754 VOID ETW::ExceptionLog::ExceptionFilterEnd()
4761 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFilterStop))
4766 FireEtwExceptionFilterStop();
4769 /****************************************************************************/
4770 /* This is called by the runtime when a domain is loaded */
4771 /****************************************************************************/
4772 VOID ETW::LoaderLog::DomainLoadReal(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName)
4781 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4782 TRACE_LEVEL_INFORMATION,
4783 CLR_LOADER_KEYWORD))
4785 DWORD dwEventOptions = ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad;
4786 ETW::LoaderLog::SendDomainEvent(pDomain, dwEventOptions, wszFriendlyName);
4788 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4791 /****************************************************************************/
4792 /* This is called by the runtime when an AppDomain is unloaded */
4793 /****************************************************************************/
4794 VOID ETW::LoaderLog::DomainUnload(AppDomain *pDomain)
4803 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4804 TRACE_LEVEL_INFORMATION,
4807 DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
4809 // Domain unload also causes type unload events
4810 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4811 TRACE_LEVEL_INFORMATION,
4814 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
4817 ETW::EnumerationLog::EnumerationHelper(NULL, pDomain, enumerationOptions);
4819 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4822 /****************************************************************************/
4823 /* This is called by the runtime when a LoaderAllocator is unloaded */
4824 /****************************************************************************/
4825 VOID ETW::LoaderLog::CollectibleLoaderAllocatorUnload(AssemblyLoaderAllocator *pLoaderAllocator)
4834 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4835 TRACE_LEVEL_INFORMATION,
4838 DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
4840 // Collectible Loader Allocator unload also causes type unload events
4841 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4842 TRACE_LEVEL_INFORMATION,
4845 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
4848 ETW::EnumerationLog::IterateCollectibleLoaderAllocator(pLoaderAllocator, enumerationOptions);
4850 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4853 /****************************************************************************/
4854 /* This is called by the runtime when the runtime is loaded
4855 Function gets called by both the Callback mechanism and regular ETW events.
4856 Type is used to differentiate whether its a callback or a normal call*/
4857 /****************************************************************************/
4858 VOID ETW::InfoLog::RuntimeInformation(INT32 type)
4866 if((type == ETW::InfoLog::InfoStructs::Normal && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, RuntimeInformationStart))
4868 (type == ETW::InfoLog::InfoStructs::Callback && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, RuntimeInformationDCStart))
4871 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W("");
4872 UINT8 startupMode = 0;
4873 UINT startupFlags = 0;
4876 _ASSERTE(CLRHosted() || g_fEEHostedStartup || // CLR started through one of the Hosting API CLRHosted() returns true if CLR started through the V2 Interface while
4877 // g_fEEHostedStartup is true if CLR is hosted through the V1 API.
4878 g_fEEComActivatedStartup || //CLR started as a COM object
4879 g_fEEOtherStartup ); //In case none of the 4 above mentioned cases are true for example ngen, ildasm then we asssume its a "other" startup
4881 Sku = ETW::InfoLog::InfoStructs::CoreCLR;
4883 //version info for clr.dll
4884 USHORT vmMajorVersion = CLR_MAJOR_VERSION;
4885 USHORT vmMinorVersion = CLR_MINOR_VERSION;
4886 USHORT vmBuildVersion = CLR_BUILD_VERSION;
4887 USHORT vmQfeVersion = CLR_BUILD_VERSION_QFE;
4889 //version info for mscorlib.dll
4890 USHORT bclMajorVersion = VER_ASSEMBLYMAJORVERSION;
4891 USHORT bclMinorVersion = VER_ASSEMBLYMINORVERSION;
4892 USHORT bclBuildVersion = VER_ASSEMBLYBUILD;
4893 USHORT bclQfeVersion = VER_ASSEMBLYBUILD_QFE;
4895 LPCGUID comGUID=&g_EEComObjectGuid;
4897 PCWSTR lpwszCommandLine = W("");
4901 // Determine the startupmode
4902 if (CLRHosted() || g_fEEHostedStartup)
4905 startupMode = ETW::InfoLog::InfoStructs::HostedCLR;
4907 else if(g_fEEComActivatedStartup)
4910 startupMode = ETW::InfoLog::InfoStructs::COMActivated;
4912 else if(g_fEEOtherStartup)
4914 //startup type is other
4915 startupMode = ETW::InfoLog::InfoStructs::Other;
4919 // if WszGetModuleFileName fails, we return an empty string
4920 if (!WszGetModuleFileName(GetCLRModule(), dllPath)) {
4921 dllPath.Set(W("\0"));
4925 if(type == ETW::InfoLog::InfoStructs::Callback)
4927 FireEtwRuntimeInformationDCStart( GetClrInstanceId(),
4945 FireEtwRuntimeInformationStart( GetClrInstanceId(),
4962 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4965 /* Fires ETW events every time a pdb is dynamically loaded.
4967 * The ETW events correspond to sending parts of the pdb in roughly
4968 * 64K sized chunks in order. Additional information sent is as follows:
4969 * ModuleID, TotalChunks, Size of Current Chunk, Chunk Number, CLRInstanceID
4971 * Note: The current implementation does not support reflection.emit.
4972 * The method will silently return without firing an event.
4975 VOID ETW::CodeSymbolLog::EmitCodeSymbols(Module* pModule)
4977 #if !defined(FEATURE_PAL) //UNIXTODO: Enable EmitCodeSymbols
4988 if (ETW_TRACING_CATEGORY_ENABLED(
4989 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4990 TRACE_LEVEL_VERBOSE,
4991 CLR_CODESYMBOLS_KEYWORD))
4993 if (pModule != NULL)
4995 UINT16 clrInstanceID = GetClrInstanceId();
4996 UINT64 moduleID = (ModuleID)pModule;
4998 // We silently exit if pdb is of length 0 instead of sending an event with no pdb bytes
4999 if (CodeSymbolLog::GetInMemorySymbolsLength(pModule, &length) == S_OK && length > 0)
5001 // The maximum data size allowed is 64K - (Size of the Event_Header)
5002 // Since the actual size of user data can only be determined at runtime
5003 // we simplify the header size value to be 1000 bytes as a conservative
5005 static const DWORD maxDataSize = 63000;
5007 ldiv_t qr = ldiv(length, maxDataSize);
5009 // We do not allow pdbs of size greater than 2GB for now,
5010 // so totalChunks should fit in 16 bits.
5011 if (qr.quot < UINT16_MAX)
5013 // If there are trailing bits in the last chunk, then increment totalChunks by 1
5014 UINT16 totalChunks = (UINT16)(qr.quot + ((qr.rem != 0) ? 1 : 0));
5015 NewArrayHolder<BYTE> chunk(new BYTE[maxDataSize]);
5017 for (UINT16 chunkNum = 0; offset < length; chunkNum++)
5019 DWORD lengthRead = 0;
5020 // We expect ReadInMemorySymbols to always return maxDataSize sized chunks
5021 // Or it is the last chunk and it is less than maxDataSize.
5022 CodeSymbolLog::ReadInMemorySymbols(pModule, offset, chunk, maxDataSize, &lengthRead);
5024 _ASSERTE(lengthRead == maxDataSize || // Either we are in the first to (n-1)th chunk
5025 (lengthRead < maxDataSize && chunkNum + 1 == totalChunks)); // Or we are in the last chunk
5027 FireEtwCodeSymbols(moduleID, totalChunks, chunkNum, lengthRead, chunk, clrInstanceID);
5028 offset += lengthRead;
5034 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
5035 #endif// !defined(FEATURE_PAL)
5038 /* Returns the length of an in-memory symbol stream
5040 * If the module has in-memory symbols the length of the stream will
5041 * be placed in pCountSymbolBytes. If the module doesn't have in-memory
5042 * symbols, *pCountSymbolBytes = 0
5044 * Returns S_OK if the length could be determined (even if it is 0)
5046 * Note: The current implementation does not support reflection.emit.
5047 * CORPROF_E_MODULE_IS_DYNAMIC will be returned in that case.
5049 * //IMPORTANT NOTE: The desktop code outside the Project K branch
5050 * contains copies of this function in the clr\src\vm\proftoeeinterfaceimpl.cpp
5051 * file of the desktop version corresponding to the profiler version
5052 * of this feature. Anytime that feature/code is ported to Project K
5053 * the code below should be appropriately merged so as to avoid
5057 HRESULT ETW::CodeSymbolLog::GetInMemorySymbolsLength(
5059 DWORD* pCountSymbolBytes)
5071 if (pCountSymbolBytes == NULL)
5073 return E_INVALIDARG;
5075 *pCountSymbolBytes = 0;
5077 if (pModule == NULL)
5079 return E_INVALIDARG;
5081 if (pModule->IsBeingUnloaded())
5083 return CORPROF_E_DATAINCOMPLETE;
5086 //This method would work fine on reflection.emit, but there would be no way to know
5087 //if some other thread was changing the size of the symbols before this method returned.
5088 //Adding events or locks to detect/prevent changes would make the scenario workable
5089 if (pModule->IsReflection())
5091 return COR_PRF_MODULE_DYNAMIC;
5094 CGrowableStream* pStream = pModule->GetInMemorySymbolStream();
5095 if (pStream == NULL)
5100 STATSTG SizeData = { 0 };
5101 hr = pStream->Stat(&SizeData, STATFLAG_NONAME);
5106 if (SizeData.cbSize.u.HighPart > 0)
5108 return COR_E_OVERFLOW;
5110 *pCountSymbolBytes = SizeData.cbSize.u.LowPart;
5115 /* Reads bytes from an in-memory symbol stream
5117 * This function attempts to read countSymbolBytes of data starting at offset
5118 * symbolsReadOffset within the in-memory stream. The data will be copied into
5119 * pSymbolBytes which is expected to have countSymbolBytes of space available.
5120 * pCountSymbolsBytesRead contains the actual number of bytes read which
5121 * may be less than countSymbolBytes if the end of the stream is reached.
5123 * Returns S_OK if a non-zero number of bytes were read.
5125 * Note: The current implementation does not support reflection.emit.
5126 * CORPROF_E_MODULE_IS_DYNAMIC will be returned in that case.
5128 * //IMPORTANT NOTE: The desktop code outside the Project K branch
5129 * contains copies of this function in the clr\src\vm\proftoeeinterfaceimpl.cpp
5130 * file of the desktop version corresponding to the profiler version
5131 * of this feature. Anytime that feature/code is ported to Project K
5132 * the code below should be appropriately merged so as to avoid
5137 HRESULT ETW::CodeSymbolLog::ReadInMemorySymbols(
5139 DWORD symbolsReadOffset,
5141 DWORD countSymbolBytes,
5142 DWORD* pCountSymbolBytesRead)
5154 if (pSymbolBytes == NULL)
5156 return E_INVALIDARG;
5158 if (pCountSymbolBytesRead == NULL)
5160 return E_INVALIDARG;
5162 *pCountSymbolBytesRead = 0;
5164 if (pModule == NULL)
5166 return E_INVALIDARG;
5168 if (pModule->IsBeingUnloaded())
5170 return CORPROF_E_DATAINCOMPLETE;
5173 //This method would work fine on reflection.emit, but there would be no way to know
5174 //if some other thread was changing the size of the symbols before this method returned.
5175 //Adding events or locks to detect/prevent changes would make the scenario workable
5176 if (pModule->IsReflection())
5178 return COR_PRF_MODULE_DYNAMIC;
5181 CGrowableStream* pStream = pModule->GetInMemorySymbolStream();
5182 if (pStream == NULL)
5184 return E_INVALIDARG;
5187 STATSTG SizeData = { 0 };
5188 hr = pStream->Stat(&SizeData, STATFLAG_NONAME);
5193 if (SizeData.cbSize.u.HighPart > 0)
5195 return COR_E_OVERFLOW;
5197 DWORD streamSize = SizeData.cbSize.u.LowPart;
5198 if (symbolsReadOffset >= streamSize)
5200 return E_INVALIDARG;
5203 *pCountSymbolBytesRead = min(streamSize - symbolsReadOffset, countSymbolBytes);
5204 memcpy_s(pSymbolBytes, countSymbolBytes, ((BYTE*)pStream->GetRawBuffer().StartAddress()) + symbolsReadOffset, *pCountSymbolBytesRead);
5209 /*******************************************************/
5210 /* This is called by the runtime when a method is jitted completely */
5211 /*******************************************************/
5212 VOID ETW::MethodLog::MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, SIZE_T pCode, ReJITID rejitID, BOOL bProfilerRejectedPrecompiledCode, BOOL bReadyToRunRejectedPrecompiledCode)
5221 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5222 TRACE_LEVEL_INFORMATION,
5225 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodLoad, TRUE, namespaceOrClassName, methodName, methodSignature, pCode, rejitID, bProfilerRejectedPrecompiledCode, bReadyToRunRejectedPrecompiledCode);
5228 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5229 TRACE_LEVEL_INFORMATION,
5230 CLR_JITTEDMETHODILTONATIVEMAP_KEYWORD))
5232 // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy
5233 // data has already been initialized.
5235 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
5236 // or profiler is loaded. So it should always be available.
5237 _ASSERTE(g_pDebugInterface != NULL);
5238 g_pDebugInterface->InitializeLazyDataIfNecessary();
5240 ETW::MethodLog::SendMethodILToNativeMapEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap, pCode, rejitID);
5243 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5246 /*************************************************/
5247 /* This is called by the runtime when method jitting started */
5248 /*************************************************/
5249 VOID ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature)
5254 PRECONDITION(pMethodDesc != NULL);
5259 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5260 TRACE_LEVEL_VERBOSE,
5263 pMethodDesc->GetMethodInfo(*namespaceOrClassName, *methodName, *methodSignature);
5264 ETW::MethodLog::SendMethodJitStartEvent(pMethodDesc, namespaceOrClassName, methodName, methodSignature);
5266 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5269 /**********************************************************************/
5270 /* This is called by the runtime when a single jit helper method with stub is initialized */
5271 /**********************************************************************/
5272 VOID ETW::MethodLog::StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName)
5277 PRECONDITION(ullHelperStartAddress != 0);
5282 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5283 TRACE_LEVEL_INFORMATION,
5286 DWORD dwHelperSize=0;
5287 Stub::RecoverStubAndSize((TADDR)ullHelperStartAddress, &dwHelperSize);
5288 ETW::MethodLog::SendHelperEvent(ullHelperStartAddress, dwHelperSize, pHelperName);
5290 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5293 /**********************************************************/
5294 /* This is called by the runtime when helpers with stubs are initialized */
5295 /**********************************************************/
5296 VOID ETW::MethodLog::StubsInitialized(PVOID *pHelperStartAddress, PVOID *pHelperNames, LONG lNoOfHelpers)
5298 WRAPPER_NO_CONTRACT;
5300 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5301 TRACE_LEVEL_INFORMATION,
5304 for(int i=0; i<lNoOfHelpers; i++)
5306 if(pHelperStartAddress[i])
5308 StubInitialized((ULONGLONG)pHelperStartAddress[i], (LPCWSTR)pHelperNames[i]);
5314 /****************************************************************************/
5315 /* This is called by the runtime when a dynamic method is destroyed */
5316 /****************************************************************************/
5317 VOID ETW::MethodLog::DynamicMethodDestroyed(MethodDesc *pMethodDesc)
5326 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5327 TRACE_LEVEL_INFORMATION,
5329 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodUnload, TRUE);
5330 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5333 /****************************************************************************/
5334 /* This is called by the runtime when a ngen method is restored */
5335 /****************************************************************************/
5336 VOID ETW::MethodLog::MethodRestored(MethodDesc *pMethodDesc)
5345 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed()
5347 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5348 TRACE_LEVEL_INFORMATION,
5349 CLR_STARTENUMERATION_KEYWORD))
5351 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE);
5353 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5356 /****************************************************************************/
5357 /* This is called by the runtime when a method table is restored */
5358 /****************************************************************************/
5359 VOID ETW::MethodLog::MethodTableRestored(MethodTable *pMethodTable)
5367 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed()
5369 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5370 TRACE_LEVEL_INFORMATION,
5371 CLR_STARTENUMERATION_KEYWORD))
5374 MethodTable::MethodIterator iter(pMethodTable);
5375 for (; iter.IsValid(); iter.Next())
5377 MethodDesc *pMD = (MethodDesc *)(iter.GetMethodDesc());
5378 if(pMD && pMD->IsRestored() && pMD->GetMethodTable_NoLogging() == pMethodTable)
5379 ETW::MethodLog::SendMethodEvent(pMD, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE);
5383 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5387 /****************************************************************************/
5388 /* This is called by the runtime when a Strong Name Verification Starts */
5389 /****************************************************************************/
5390 VOID ETW::SecurityLog::StrongNameVerificationStart(DWORD dwInFlags, __in LPWSTR strFullyQualifiedAssemblyName)
5392 WRAPPER_NO_CONTRACT;
5396 /****************************************************************************/
5397 /* This is called by the runtime when a Strong Name Verification Ends */
5398 /****************************************************************************/
5399 VOID ETW::SecurityLog::StrongNameVerificationStop(DWORD dwInFlags,ULONG result, __in LPWSTR strFullyQualifiedAssemblyName)
5401 WRAPPER_NO_CONTRACT;
5404 /****************************************************************************/
5405 /* This is called by the runtime when field transparency calculations begin */
5406 /****************************************************************************/
5407 void ETW::SecurityLog::FireFieldTransparencyComputationStart(LPCWSTR wszFieldName,
5408 LPCWSTR wszModuleName,
5411 WRAPPER_NO_CONTRACT;
5412 FireEtwFieldTransparencyComputationStart(wszFieldName, wszModuleName, dwAppDomain, GetClrInstanceId());
5415 /****************************************************************************/
5416 /* This is called by the runtime when field transparency calculations end */
5417 /****************************************************************************/
5418 void ETW::SecurityLog::FireFieldTransparencyComputationEnd(LPCWSTR wszFieldName,
5419 LPCWSTR wszModuleName,
5422 BOOL fIsTreatAsSafe)
5424 WRAPPER_NO_CONTRACT;
5425 FireEtwFieldTransparencyComputationEnd(wszFieldName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5428 /*****************************************************************************/
5429 /* This is called by the runtime when method transparency calculations begin */
5430 /*****************************************************************************/
5431 void ETW::SecurityLog::FireMethodTransparencyComputationStart(LPCWSTR wszMethodName,
5432 LPCWSTR wszModuleName,
5435 WRAPPER_NO_CONTRACT;
5436 FireEtwMethodTransparencyComputationStart(wszMethodName, wszModuleName, dwAppDomain, GetClrInstanceId());
5439 /*****************************************************************************/
5440 /* This is called by the runtime when method transparency calculations end */
5441 /********************************************(********************************/
5442 void ETW::SecurityLog::FireMethodTransparencyComputationEnd(LPCWSTR wszMethodName,
5443 LPCWSTR wszModuleName,
5446 BOOL fIsTreatAsSafe)
5448 WRAPPER_NO_CONTRACT;
5449 FireEtwMethodTransparencyComputationEnd(wszMethodName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5452 /*****************************************************************************/
5453 /* This is called by the runtime when module transparency calculations begin */
5454 /*****************************************************************************/
5455 void ETW::SecurityLog::FireModuleTransparencyComputationStart(LPCWSTR wszModuleName,
5458 WRAPPER_NO_CONTRACT;
5459 FireEtwModuleTransparencyComputationStart(wszModuleName, dwAppDomain, GetClrInstanceId());
5462 /****************************************************************************/
5463 /* This is called by the runtime when module transparency calculations end */
5464 /****************************************************************************/
5465 void ETW::SecurityLog::FireModuleTransparencyComputationEnd(LPCWSTR wszModuleName,
5467 BOOL fIsAllCritical,
5468 BOOL fIsAllTransparent,
5469 BOOL fIsTreatAsSafe,
5470 BOOL fIsOpportunisticallyCritical,
5471 DWORD dwSecurityRuleSet)
5473 WRAPPER_NO_CONTRACT;
5474 FireEtwModuleTransparencyComputationEnd(wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsTreatAsSafe, fIsOpportunisticallyCritical, dwSecurityRuleSet, GetClrInstanceId());
5477 /****************************************************************************/
5478 /* This is called by the runtime when token transparency calculations begin */
5479 /****************************************************************************/
5480 void ETW::SecurityLog::FireTokenTransparencyComputationStart(DWORD dwToken,
5481 LPCWSTR wszModuleName,
5484 WRAPPER_NO_CONTRACT;
5485 FireEtwTokenTransparencyComputationStart(dwToken, wszModuleName, dwAppDomain, GetClrInstanceId());
5488 /****************************************************************************/
5489 /* This is called by the runtime when token transparency calculations end */
5490 /****************************************************************************/
5491 void ETW::SecurityLog::FireTokenTransparencyComputationEnd(DWORD dwToken,
5492 LPCWSTR wszModuleName,
5495 BOOL fIsTreatAsSafe)
5497 WRAPPER_NO_CONTRACT;
5498 FireEtwTokenTransparencyComputationEnd(dwToken, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5501 /*****************************************************************************/
5502 /* This is called by the runtime when type transparency calculations begin */
5503 /*****************************************************************************/
5504 void ETW::SecurityLog::FireTypeTransparencyComputationStart(LPCWSTR wszTypeName,
5505 LPCWSTR wszModuleName,
5508 WRAPPER_NO_CONTRACT;
5509 FireEtwTypeTransparencyComputationStart(wszTypeName, wszModuleName, dwAppDomain, GetClrInstanceId());
5512 /****************************************************************************/
5513 /* This is called by the runtime when type transparency calculations end */
5514 /****************************************************************************/
5515 void ETW::SecurityLog::FireTypeTransparencyComputationEnd(LPCWSTR wszTypeName,
5516 LPCWSTR wszModuleName,
5518 BOOL fIsAllCritical,
5519 BOOL fIsAllTransparent,
5521 BOOL fIsTreatAsSafe)
5523 WRAPPER_NO_CONTRACT;
5524 FireEtwTypeTransparencyComputationEnd(wszTypeName, wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5527 /**********************************************************************************/
5528 /* This is called by the runtime when a module is loaded */
5529 /* liReportedSharedModule will be 0 when this module is reported for the 1st time */
5530 /**********************************************************************************/
5531 VOID ETW::LoaderLog::ModuleLoad(Module *pModule, LONG liReportedSharedModule)
5540 DWORD enumerationOptions = ETW::EnumerationLog::EnumerationStructs::None;
5541 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5542 TRACE_LEVEL_INFORMATION,
5545 BOOL bTraceFlagLoaderSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5546 TRACE_LEVEL_INFORMATION,
5547 CLR_LOADER_KEYWORD);
5548 BOOL bTraceFlagNgenMethodSet = IsRuntimeNgenKeywordEnabledAndNotSuppressed();
5549 BOOL bTraceFlagStartRundownSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5550 TRACE_LEVEL_INFORMATION,
5551 CLR_STARTENUMERATION_KEYWORD);
5552 BOOL bTraceFlagPerfTrackSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5553 TRACE_LEVEL_INFORMATION,
5554 CLR_PERFTRACK_KEYWORD);
5556 if(liReportedSharedModule == 0)
5559 if(bTraceFlagLoaderSet)
5560 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad;
5561 if (bTraceFlagPerfTrackSet)
5562 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad;
5563 if(bTraceFlagNgenMethodSet && bTraceFlagStartRundownSet)
5564 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad;
5566 if(pModule->IsManifest() && bTraceFlagLoaderSet)
5567 ETW::LoaderLog::SendAssemblyEvent(pModule->GetAssembly(), enumerationOptions);
5569 if(bTraceFlagLoaderSet || bTraceFlagPerfTrackSet)
5570 ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad | ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad);
5572 ETW::EnumerationLog::EnumerationHelper(pModule, NULL, enumerationOptions);
5575 // we want to report domainmodule events whenever they are loaded in any AppDomain
5576 if(bTraceFlagLoaderSet)
5577 ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad, TRUE);
5581 BOOL bTraceFlagPerfTrackPrivateSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
5582 TRACE_LEVEL_INFORMATION,
5583 CLR_PERFTRACK_PRIVATE_KEYWORD);
5584 if (liReportedSharedModule == 0 && bTraceFlagPerfTrackPrivateSet)
5586 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate;
5587 ETW::LoaderLog::SendModuleRange(pModule, enumerationOptions);
5590 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5593 /****************************************************************************/
5594 /* This is called by the runtime when the process is being shutdown */
5595 /****************************************************************************/
5596 VOID ETW::EnumerationLog::ProcessShutdown()
5605 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO))
5607 DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords();
5609 // Send unload events for all remaining domains, including shared domain and
5611 ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions);
5613 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5616 /****************************************************************************/
5617 /****************************************************************************/
5618 /* Begining of helper functions */
5619 /****************************************************************************/
5620 /****************************************************************************/
5622 /****************************************************************************/
5623 /* This routine is used to send a domain load/unload or rundown event */
5624 /****************************************************************************/
5625 VOID ETW::LoaderLog::SendDomainEvent(BaseDomain *pBaseDomain, DWORD dwEventOptions, LPCWSTR wszFriendlyName)
5635 PCWSTR szDtraceOutput1=W("");
5636 BOOL bIsDefaultDomain = pBaseDomain->IsDefaultDomain();
5637 BOOL bIsAppDomain = pBaseDomain->IsAppDomain();
5638 BOOL bIsExecutable = bIsAppDomain ? !(pBaseDomain->AsAppDomain()->IsPassiveDomain()) : FALSE;
5639 BOOL bIsSharedDomain = pBaseDomain->IsSharedDomain();
5640 UINT32 uSharingPolicy = bIsAppDomain?(pBaseDomain->AsAppDomain()->GetSharePolicy()):0;
5642 ULONGLONG ullDomainId = (ULONGLONG)pBaseDomain;
5643 ULONG ulDomainFlags = ((bIsDefaultDomain ? ETW::LoaderLog::LoaderStructs::DefaultDomain : 0) |
5644 (bIsExecutable ? ETW::LoaderLog::LoaderStructs::ExecutableDomain : 0) |
5645 (bIsSharedDomain ? ETW::LoaderLog::LoaderStructs::SharedDomain : 0) |
5646 (uSharingPolicy<<28));
5648 LPCWSTR wsEmptyString = W("");
5649 LPCWSTR wsSharedString = W("SharedDomain");
5651 LPWSTR lpswzDomainName = (LPWSTR)wsEmptyString;
5656 lpswzDomainName = (PWCHAR)wszFriendlyName;
5658 lpswzDomainName = (PWCHAR)pBaseDomain->AsAppDomain()->GetFriendlyName();
5661 lpswzDomainName = (LPWSTR)wsSharedString;
5663 /* prepare events args for ETW and ETM */
5664 szDtraceOutput1 = (PCWSTR)lpswzDomainName;
5666 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
5668 FireEtwAppDomainLoad_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5670 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
5672 FireEtwAppDomainUnload_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5674 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
5676 FireEtwAppDomainDCStart_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5678 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
5680 FireEtwAppDomainDCEnd_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5684 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
5685 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
5686 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
5687 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
5691 /********************************************************/
5692 /* This routine is used to send thread rundown events when ARM is enabled */
5693 /********************************************************/
5694 VOID ETW::EnumerationLog::SendThreadRundownEvent()
5701 #ifndef DACCESS_COMPILE
5702 Thread *pThread = NULL;
5704 // Take the thread store lock while we enumerate threads.
5705 ThreadStoreLockHolder tsl;
5706 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
5708 if (pThread->IsUnstarted() || pThread->IsDead())
5711 // Send thread rundown provider events and thread created runtime provider
5712 // events (depending on which are enabled)
5713 ThreadLog::FireThreadDC(pThread);
5714 ThreadLog::FireThreadCreated(pThread);
5716 #endif // !DACCESS_COMPILE
5719 /****************************************************************************/
5720 /* This routine is used to send an assembly load/unload or rundown event ****/
5721 /****************************************************************************/
5723 VOID ETW::LoaderLog::SendAssemblyEvent(Assembly *pAssembly, DWORD dwEventOptions)
5733 PCWSTR szDtraceOutput1=W("");
5734 BOOL bIsDynamicAssembly = pAssembly->IsDynamic();
5735 BOOL bIsCollectibleAssembly = pAssembly->IsCollectible();
5736 BOOL bIsDomainNeutral = pAssembly->IsDomainNeutral() ;
5737 BOOL bHasNativeImage = pAssembly->GetManifestFile()->HasNativeImage();
5738 BOOL bIsReadyToRun = pAssembly->GetManifestFile()->IsILImageReadyToRun();
5740 ULONGLONG ullAssemblyId = (ULONGLONG)pAssembly;
5741 ULONGLONG ullDomainId = (ULONGLONG)pAssembly->GetDomain();
5742 ULONGLONG ullBindingID = 0;
5743 ULONG ulAssemblyFlags = ((bIsDomainNeutral ? ETW::LoaderLog::LoaderStructs::DomainNeutralAssembly : 0) |
5744 (bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicAssembly : 0) |
5745 (bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeAssembly : 0) |
5746 (bIsCollectibleAssembly ? ETW::LoaderLog::LoaderStructs::CollectibleAssembly : 0) |
5747 (bIsReadyToRun ? ETW::LoaderLog::LoaderStructs::ReadyToRunAssembly : 0));
5749 SString sAssemblyPath;
5750 pAssembly->GetDisplayName(sAssemblyPath);
5751 LPWSTR lpszAssemblyPath = (LPWSTR)sAssemblyPath.GetUnicode();
5753 /* prepare events args for ETW and ETM */
5754 szDtraceOutput1 = (PCWSTR)lpszAssemblyPath;
5756 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
5758 FireEtwAssemblyLoad_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5760 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
5762 FireEtwAssemblyUnload_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5764 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
5766 FireEtwAssemblyDCStart_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5768 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
5770 FireEtwAssemblyDCEnd_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5774 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
5775 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
5776 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
5777 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
5783 ETW::LoaderLog::SendModuleRange(
5784 __in Module *pModule,
5785 __in DWORD dwEventOptions)
5788 ULONG Result = ERROR_SUCCESS;
5791 // do not fire the ETW event when:
5792 // 1. We did not load the native image
5793 // 2. We do not have IBC data for the native image
5794 if( !pModule || !pModule->HasNativeImage() || !pModule->IsIbcOptimized() )
5799 // get information about the hot sections from the native image that has been loaded
5800 COUNT_T cbSizeOfSectionTable;
5801 CORCOMPILE_VIRTUAL_SECTION_INFO* pVirtualSectionsTable = (CORCOMPILE_VIRTUAL_SECTION_INFO* )pModule->GetNativeImage()->GetVirtualSectionsTable(&cbSizeOfSectionTable);
5803 COUNT_T RangeCount = cbSizeOfSectionTable/sizeof(CORCOMPILE_VIRTUAL_SECTION_INFO);
5805 // if we do not have any hot ranges, we do not fire the ETW event
5807 // Figure out the rest of the event data
5808 UINT16 ClrInstanceId = GetClrInstanceId();
5809 UINT64 ModuleID = (ULONGLONG)(TADDR) pModule;
5811 for (COUNT_T i = 0; i < RangeCount; ++i)
5813 DWORD rangeBegin = pVirtualSectionsTable[i].VirtualAddress;
5814 DWORD rangeSize = pVirtualSectionsTable[i].Size;
5815 DWORD sectionType = pVirtualSectionsTable[i].SectionType;
5817 UINT8 ibcType = VirtualSectionData::IBCType(sectionType);
5818 UINT8 rangeType = VirtualSectionData::RangeType(sectionType);
5819 UINT16 virtualSectionType = VirtualSectionData::VirtualSectionType(sectionType);
5820 BOOL isIBCProfiledColdSection = VirtualSectionData::IsIBCProfiledColdSection(sectionType);
5821 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad)
5823 if (isIBCProfiledColdSection)
5824 Result &= FireEtwModuleRangeLoad(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5826 else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart)
5828 if (isIBCProfiledColdSection)
5829 Result &= FireEtwModuleRangeDCStart(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5831 else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd)
5833 if (isIBCProfiledColdSection)
5834 Result &= FireEtwModuleRangeDCEnd(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5836 // Fire private events if they are requested.
5837 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate)
5839 Result &= FireEtwModuleRangeLoadPrivate(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType, ibcType, virtualSectionType);
5845 //---------------------------------------------------------------------------------------
5847 // Helper that takes a module, and returns the managed and native PDB information
5848 // corresponding to that module. Used by the routine that fires the module load / unload
5852 // * pModule - Module to examine
5853 // * pCvInfoIL - [out] CV_INFO_PDB70 corresponding to managed PDB for this module
5854 // (the last debug directory entry in the PE File), if it exists. If it doesn't
5855 // exist, this is zeroed out.
5856 // * pCvInfoNative - [out] CV_INFO_PDB70 corresponding to native NGEN PDB for this
5857 // module (the next-to-last debug directory entry in the PE File), if it exists.
5858 // If it doesn't exist, this is zeroed out.
5861 // * This method only understands the CV_INFO_PDB70 / RSDS format. If the format
5862 // changes, this function will act as if there are no debug directory entries.
5863 // Module load / unload events will still be fired, but all PDB info will be
5865 // * The raw data in the PE file's debug directory entries are assumed to be
5866 // untrusted, and reported sizes of buffers are verified against their data.
5869 static void GetCodeViewInfo(Module * pModule, CV_INFO_PDB70 * pCvInfoIL, CV_INFO_PDB70 * pCvInfoNative)
5871 LIMITED_METHOD_CONTRACT;
5873 _ASSERTE (pModule != NULL);
5874 _ASSERTE (pCvInfoIL != NULL);
5875 _ASSERTE (pCvInfoNative != NULL);
5877 ZeroMemory(pCvInfoIL, sizeof(*pCvInfoIL));
5878 ZeroMemory(pCvInfoNative, sizeof(*pCvInfoNative));
5880 PTR_PEFile pPEFile = pModule->GetFile();
5881 _ASSERTE(pPEFile != NULL);
5883 PTR_PEImageLayout pLayout = NULL;
5884 if (pPEFile->HasNativeImage())
5886 pLayout = pPEFile->GetLoadedNative();
5888 else if (pPEFile->HasOpenedILimage())
5890 pLayout = pPEFile->GetLoadedIL();
5893 if (pLayout == NULL)
5895 // This can happen for reflection-loaded modules
5899 if (!pLayout->HasNTHeaders())
5901 // Without NT headers, we'll have a tough time finding the debug directory
5902 // entries. This can happen for nlp files.
5906 if (!pLayout->HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG))
5909 COUNT_T cbDebugEntries;
5910 IMAGE_DEBUG_DIRECTORY * rgDebugEntries =
5911 (IMAGE_DEBUG_DIRECTORY *) pLayout->GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugEntries);
5913 if (cbDebugEntries < sizeof(IMAGE_DEBUG_DIRECTORY))
5916 // Since rgDebugEntries is an array of IMAGE_DEBUG_DIRECTORYs, cbDebugEntries
5917 // should be a multiple of sizeof(IMAGE_DEBUG_DIRECTORY).
5918 if (cbDebugEntries % sizeof(IMAGE_DEBUG_DIRECTORY) != 0)
5921 // Temporary storage for a CV_INFO_PDB70 and its size (which could be less than
5922 // sizeof(CV_INFO_PDB70); see below).
5925 CV_INFO_PDB70 * m_pPdb70;
5929 // Iterate through all debug directory entries. The very last one will be the
5930 // managed PDB entry. The next to last one (if it exists) will be the (native) NGEN
5931 // PDB entry. Treat raw bytes we read as untrusted.
5932 PdbInfo pdbInfoLast = {0};
5933 PdbInfo pdbInfoNextToLast = {0};
5934 int cEntries = cbDebugEntries / sizeof(IMAGE_DEBUG_DIRECTORY);
5935 for (int i = 0; i < cEntries; i++)
5937 if (rgDebugEntries[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW)
5940 // Get raw data pointed to by this IMAGE_DEBUG_DIRECTORY
5942 // Some compilers set PointerToRawData but not AddressOfRawData as they put the
5943 // data at the end of the file in an unmapped part of the file
5944 RVA rvaOfRawData = (rgDebugEntries[i].AddressOfRawData != NULL) ?
5945 rgDebugEntries[i].AddressOfRawData :
5946 pLayout->OffsetToRva(rgDebugEntries[i].PointerToRawData);
5948 ULONG cbDebugData = rgDebugEntries[i].SizeOfData;
5949 if (cbDebugData < (offsetof(CV_INFO_PDB70, magic) + sizeof(((CV_INFO_PDB70*)0)->magic)))
5951 // raw data too small to contain magic number at expected spot, so its format
5952 // is not recognizeable. Skip
5956 if (!pLayout->CheckRva(rvaOfRawData, cbDebugData))
5958 // Memory claimed to belong to the raw data does not fit.
5959 // IMAGE_DEBUG_DIRECTORY is outright corrupt. Do not include PDB info in
5964 // Verify the magic number is as expected
5965 CV_INFO_PDB70 * pPdb70 = (CV_INFO_PDB70 *) pLayout->GetRvaData(rvaOfRawData);
5966 if (pPdb70->magic != CV_SIGNATURE_RSDS)
5968 // Unrecognized magic number. Skip
5972 // From this point forward, the format should adhere to the expected layout of
5973 // CV_INFO_PDB70. If we find otherwise, then assume the IMAGE_DEBUG_DIRECTORY is
5974 // outright corrupt, and do not include PDB info in event at all. The caller will
5975 // still fire the module event, but have zeroed-out / empty PDB fields.
5977 // Verify sane size of raw data
5978 if (cbDebugData > sizeof(CV_INFO_PDB70))
5981 // cbDebugData actually can be < sizeof(CV_INFO_PDB70), since the "path" field
5982 // can be truncated to its actual data length (i.e., fewer than MAX_LONGPATH chars
5983 // may be present in the PE file). In some cases, though, cbDebugData will
5984 // include all MAX_LONGPATH chars even though path gets null-terminated well before
5985 // the MAX_LONGPATH limit.
5987 // Gotta have at least one byte of the path
5988 if (cbDebugData < offsetof(CV_INFO_PDB70, path) + sizeof(char))
5991 // How much space is available for the path?
5992 size_t cchPathMaxIncludingNullTerminator = (cbDebugData - offsetof(CV_INFO_PDB70, path)) / sizeof(char);
5993 _ASSERTE(cchPathMaxIncludingNullTerminator >= 1); // Guaranteed above
5995 // Verify path string fits inside the declared size
5996 size_t cchPathActualExcludingNullTerminator = strnlen(pPdb70->path, cchPathMaxIncludingNullTerminator);
5997 if (cchPathActualExcludingNullTerminator == cchPathMaxIncludingNullTerminator)
5999 // This is how strnlen indicates failure--it couldn't find the null
6000 // terminator within the buffer size specified
6004 // Looks valid. Remember it.
6005 pdbInfoNextToLast = pdbInfoLast;
6006 pdbInfoLast.m_pPdb70 = pPdb70;
6007 pdbInfoLast.m_cbPdb70 = cbDebugData;
6010 // Return whatever we found
6012 if (pdbInfoLast.m_pPdb70 != NULL)
6014 // The last guy is the IL (managed) PDB info
6015 _ASSERTE(pdbInfoLast.m_cbPdb70 <= sizeof(*pCvInfoIL)); // Guaranteed by checks above
6016 memcpy(pCvInfoIL, pdbInfoLast.m_pPdb70, pdbInfoLast.m_cbPdb70);
6019 if (pdbInfoNextToLast.m_pPdb70 != NULL)
6021 // The next-to-last guy is the NGEN (native) PDB info
6022 _ASSERTE(pdbInfoNextToLast.m_cbPdb70 <= sizeof(*pCvInfoNative)); // Guaranteed by checks above
6023 memcpy(pCvInfoNative, pdbInfoNextToLast.m_pPdb70, pdbInfoNextToLast.m_cbPdb70);
6028 //---------------------------------------------------------------------------------------
6030 // send a module load/unload or rundown event and domainmodule load and rundown event
6033 // * pModule - Module loading or unloading
6034 // * dwEventOptions - Bitmask of which events to fire
6035 // * bFireDomainModuleEvents - nonzero if we are to fire DomainModule events; zero
6036 // if we are to fire Module events
6038 VOID ETW::LoaderLog::SendModuleEvent(Module *pModule, DWORD dwEventOptions, BOOL bFireDomainModuleEvents)
6048 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W("");
6049 BOOL bIsDynamicAssembly = pModule->GetAssembly()->IsDynamic();
6050 BOOL bHasNativeImage = FALSE;
6051 #ifdef FEATURE_PREJIT
6052 bHasNativeImage = pModule->HasNativeImage();
6053 #endif // FEATURE_PREJIT
6054 BOOL bIsManifestModule = pModule->IsManifest();
6055 ULONGLONG ullAppDomainId = 0; // This is used only with DomainModule events
6056 ULONGLONG ullModuleId = (ULONGLONG)(TADDR) pModule;
6057 ULONGLONG ullAssemblyId = (ULONGLONG)pModule->GetAssembly();
6058 BOOL bIsDomainNeutral = pModule->GetAssembly()->IsDomainNeutral();
6059 BOOL bIsIbcOptimized = FALSE;
6062 bIsIbcOptimized = pModule->IsIbcOptimized();
6064 BOOL bIsReadyToRun = pModule->IsReadyToRun();
6065 ULONG ulReservedFlags = 0;
6066 ULONG ulFlags = ((bIsDomainNeutral ? ETW::LoaderLog::LoaderStructs::DomainNeutralModule : 0) |
6067 (bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeModule : 0) |
6068 (bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicModule : 0) |
6069 (bIsManifestModule ? ETW::LoaderLog::LoaderStructs::ManifestModule : 0) |
6070 (bIsIbcOptimized ? ETW::LoaderLog::LoaderStructs::IbcOptimized : 0) |
6071 (bIsReadyToRun ? ETW::LoaderLog::LoaderStructs::ReadyToRunModule : 0));
6073 // Grab PDB path, guid, and age for managed PDB and native (NGEN) PDB when
6074 // available. Any failures are not fatal. The corresponding PDB info will remain
6075 // zeroed out, and that's what we'll include in the event.
6076 CV_INFO_PDB70 cvInfoIL = {0};
6077 CV_INFO_PDB70 cvInfoNative = {0};
6078 GetCodeViewInfo(pModule, &cvInfoIL, &cvInfoNative);
6080 PWCHAR ModuleILPath=(PWCHAR)W(""), ModuleNativePath=(PWCHAR)W("");
6082 if(bFireDomainModuleEvents)
6084 if(pModule->GetDomain()->IsSharedDomain()) // for shared domains, we do not fire domainmodule event
6086 ullAppDomainId = (ULONGLONG)pModule->FindDomainAssembly(pModule->GetDomain()->AsAppDomain())->GetAppDomain();
6089 LPCWSTR pEmptyString = W("");
6090 SString moduleName = W("");
6092 if(!bIsDynamicAssembly)
6094 ModuleILPath = (PWCHAR)pModule->GetAssembly()->GetManifestFile()->GetILimage()->GetPath().GetUnicode();
6095 ModuleNativePath = (PWCHAR)pEmptyString;
6097 #ifdef FEATURE_PREJIT
6099 ModuleNativePath = (PWCHAR)pModule->GetNativeImage()->GetPath().GetUnicode();
6100 #endif // FEATURE_PREJIT
6103 // if we do not have a module path yet, we put the module name
6104 if(bIsDynamicAssembly || ModuleILPath==NULL || wcslen(ModuleILPath) <= 2)
6106 moduleName.SetUTF8(pModule->GetSimpleName());
6107 ModuleILPath = (PWCHAR)moduleName.GetUnicode();
6108 ModuleNativePath = (PWCHAR)pEmptyString;
6111 /* prepare events args for ETW and ETM */
6112 szDtraceOutput1 = (PCWSTR)ModuleILPath;
6113 szDtraceOutput2 = (PCWSTR)ModuleNativePath;
6115 // Convert PDB paths to UNICODE
6116 StackSString managedPdbPath(SString::Utf8, cvInfoIL.path);
6117 StackSString nativePdbPath(SString::Utf8, cvInfoNative.path);
6119 if(bFireDomainModuleEvents)
6121 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
6123 FireEtwDomainModuleLoad_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6125 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
6127 FireEtwDomainModuleDCStart_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6129 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
6131 FireEtwDomainModuleDCEnd_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6135 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
6136 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
6137 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
6142 if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad))
6144 FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6146 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
6148 FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6150 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart))
6152 FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6154 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd))
6156 FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6160 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
6161 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
6162 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
6163 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
6164 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny));
6168 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny)
6170 // Fire ModuleRangeLoad, ModuleRangeDCStart, ModuleRangeDCEnd or ModuleRangeLoadPrivate event for this Module
6171 SendModuleRange(pModule, dwEventOptions);
6176 /*****************************************************************/
6177 /* This routine is used to send an ETW event just before a method starts jitting*/
6178 /*****************************************************************/
6179 VOID ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature)
6186 Module *pModule = NULL;
6187 Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID
6189 ULONGLONG ullMethodIdentifier=0;
6190 ULONGLONG ullModuleID=0;
6191 ULONG ulMethodToken=0;
6192 ULONG ulMethodILSize=0;
6193 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""),szDtraceOutput3=W("");
6196 pModule = pMethodDesc->GetModule_NoLogging();
6198 if(!pMethodDesc->IsRestored()) {
6202 bool bIsDynamicMethod = pMethodDesc->IsDynamicMethod();
6203 BOOL bIsGenericMethod = FALSE;
6204 if(pMethodDesc->GetMethodTable_NoLogging())
6205 bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging();
6207 ullModuleID = (ULONGLONG)(TADDR) pModule;
6208 ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6210 // Use MethodDesc if Dynamic or Generic methods
6211 if( bIsDynamicMethod || bIsGenericMethod)
6213 if(bIsGenericMethod)
6214 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6215 if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0
6216 ulMethodToken = (ULONG)0;
6219 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6221 if(pMethodDesc->IsIL())
6223 COR_ILMETHOD_DECODER::DecoderStatus decoderstatus = COR_ILMETHOD_DECODER::FORMAT_ERROR;
6224 COR_ILMETHOD_DECODER ILHeader(pMethodDesc->GetILHeader(), pMethodDesc->GetMDImport(), &decoderstatus);
6225 ulMethodILSize = (ULONG)ILHeader.GetCodeSize();
6228 SString tNamespace, tMethodName, tMethodSignature;
6229 if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty()))
6231 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
6232 namespaceOrClassName = &tNamespace;
6233 methodName = &tMethodName;
6234 methodSignature = &tMethodSignature;
6237 // fire method information
6238 /* prepare events args for ETW and ETM */
6239 szDtraceOutput1 = (PCWSTR)namespaceOrClassName->GetUnicode();
6240 szDtraceOutput2 = (PCWSTR)methodName->GetUnicode();
6241 szDtraceOutput3 = (PCWSTR)methodSignature->GetUnicode();
6243 FireEtwMethodJittingStarted_V1(ullMethodIdentifier,
6250 GetClrInstanceId());
6254 /****************************************************************************/
6255 /* This routine is used to send a method load/unload or rundown event */
6256 /****************************************************************************/
6257 VOID ETW::MethodLog::SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, SIZE_T pCode, ReJITID rejitID, BOOL bProfilerRejectedPrecompiledCode, BOOL bReadyToRunRejectedPrecompiledCode)
6265 Module *pModule = NULL;
6266 Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID
6267 ULONGLONG ullMethodStartAddress=0, ullColdMethodStartAddress=0, ullModuleID=0, ullMethodIdentifier=0;
6268 ULONG ulMethodSize=0, ulColdMethodSize=0, ulMethodToken=0, ulMethodFlags=0, ulColdMethodFlags=0;
6269 PWCHAR pMethodName=NULL, pNamespaceName=NULL, pMethodSignature=NULL;
6270 BOOL bHasNativeImage = FALSE, bShowVerboseOutput = FALSE, bIsDynamicMethod = FALSE, bHasSharedGenericCode = FALSE, bIsGenericMethod = FALSE;
6271 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""),szDtraceOutput3=W("");
6273 BOOL bIsRundownProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6274 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6275 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
6276 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd));
6278 BOOL bIsRuntimeProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6279 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6280 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
6281 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload));
6283 if (pMethodDesc == NULL)
6286 if(!pMethodDesc->IsRestored())
6288 // Forcibly restoring ngen methods can cause all sorts of deadlocks and contract violations
6289 // These events are therefore put under the private provider
6290 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
6291 TRACE_LEVEL_INFORMATION,
6292 CLR_PRIVATENGENFORCERESTORE_KEYWORD))
6294 PERMANENT_CONTRACT_VIOLATION(GCViolation, ReasonNonShippingCode);
6295 pMethodDesc->CheckRestore();
6304 if(bIsRundownProvider)
6306 bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
6307 TRACE_LEVEL_VERBOSE,
6310 else if(bIsRuntimeProvider)
6312 bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
6313 TRACE_LEVEL_VERBOSE,
6317 pModule = pMethodDesc->GetModule_NoLogging();
6318 #ifdef FEATURE_PREJIT
6319 bHasNativeImage = pModule->HasNativeImage();
6320 #endif // FEATURE_PREJIT
6321 bIsDynamicMethod = (BOOL)pMethodDesc->IsDynamicMethod();
6322 bHasSharedGenericCode = pMethodDesc->IsSharedByGenericInstantiations();
6324 if(pMethodDesc->GetMethodTable_NoLogging())
6325 bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging();
6327 ulMethodFlags = ((ulMethodFlags |
6328 (bHasSharedGenericCode ? ETW::MethodLog::MethodStructs::SharedGenericCode : 0) |
6329 (bIsGenericMethod ? ETW::MethodLog::MethodStructs::GenericMethod : 0) |
6330 (bIsDynamicMethod ? ETW::MethodLog::MethodStructs::DynamicMethod : 0) |
6331 (bIsJit ? ETW::MethodLog::MethodStructs::JittedMethod : 0) |
6332 (bProfilerRejectedPrecompiledCode ? ETW::MethodLog::MethodStructs::ProfilerRejectedPrecompiledCode : 0) |
6333 (bReadyToRunRejectedPrecompiledCode ? ETW::MethodLog::MethodStructs::ReadyToRunRejectedPrecompiledCode : 0)));
6335 // Intentionally set the extent flags (cold vs. hot) only after all the other common
6336 // flags (above) have been set.
6337 ulColdMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::ColdSection; // Method Extent (bits 28, 29, 30, 31)
6338 ulMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::HotSection; // Method Extent (bits 28, 29, 30, 31)
6340 // MethodDesc ==> Code Address ==>JitMananger
6341 TADDR start = pCode ? pCode : PCODEToPINSTR(pMethodDesc->GetNativeCode());
6343 // this method hasn't been jitted
6347 // EECodeInfo is technically initialized by a "PCODE", but it can also be initialized
6348 // by a TADDR (i.e., w/out thumb bit set on ARM)
6349 EECodeInfo codeInfo(start);
6351 // MethodToken ==> MethodRegionInfo
6352 IJitManager::MethodRegionInfo methodRegionInfo;
6353 codeInfo.GetMethodRegionInfo(&methodRegionInfo);
6355 ullMethodStartAddress = (ULONGLONG)methodRegionInfo.hotStartAddress;
6356 ulMethodSize = (ULONG)methodRegionInfo.hotSize;
6358 ullModuleID = (ULONGLONG)(TADDR) pModule;
6359 ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6361 // Use MethodDesc if Dynamic or Generic methods
6362 if( bIsDynamicMethod || bIsGenericMethod)
6364 bShowVerboseOutput = TRUE;
6365 if(bIsGenericMethod)
6366 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6367 if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0
6368 ulMethodToken = (ULONG)0;
6371 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6375 ullColdMethodStartAddress = (ULONGLONG)methodRegionInfo.coldStartAddress;
6376 ulColdMethodSize = (ULONG)methodRegionInfo.coldSize; // methodRegionInfo.coldSize is size_t and info.MethodLoadInfo.MethodSize is 32 bit; will give incorrect values on a 64-bit machine
6379 SString tNamespace, tMethodName, tMethodSignature;
6381 // if verbose method load info needed, only then
6382 // find method name and signature and fire verbose method load info
6383 if(bShowVerboseOutput)
6385 if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty()))
6387 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
6388 namespaceOrClassName = &tNamespace;
6389 methodName = &tMethodName;
6390 methodSignature = &tMethodSignature;
6392 pNamespaceName = (PWCHAR)namespaceOrClassName->GetUnicode();
6393 pMethodName = (PWCHAR)methodName->GetUnicode();
6394 pMethodSignature = (PWCHAR)methodSignature->GetUnicode();
6397 BOOL bFireEventForColdSection = (bHasNativeImage && ullColdMethodStartAddress && ulColdMethodSize);
6399 /* prepare events args for ETW and ETM */
6400 szDtraceOutput1 = (PCWSTR)pNamespaceName;
6401 szDtraceOutput2 = (PCWSTR)pMethodName;
6402 szDtraceOutput3 = (PCWSTR)pMethodSignature;
6404 if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6405 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad))
6407 if(bShowVerboseOutput)
6409 FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier,
6411 ullMethodStartAddress,
6423 FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier,
6425 ullMethodStartAddress,
6432 if(bFireEventForColdSection)
6434 if(bShowVerboseOutput)
6436 FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier,
6438 ullColdMethodStartAddress,
6450 FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier,
6452 ullColdMethodStartAddress,
6461 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6462 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload))
6464 if(bShowVerboseOutput)
6466 FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier,
6468 ullMethodStartAddress,
6480 FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier,
6482 ullMethodStartAddress,
6489 if(bFireEventForColdSection)
6491 if(bShowVerboseOutput)
6493 FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier,
6495 ullColdMethodStartAddress,
6507 FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier,
6509 ullColdMethodStartAddress,
6518 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6519 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart))
6521 if(bShowVerboseOutput)
6523 FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier,
6525 ullMethodStartAddress,
6537 FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier,
6539 ullMethodStartAddress,
6546 if(bFireEventForColdSection)
6548 if(bShowVerboseOutput)
6550 FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier,
6552 ullColdMethodStartAddress,
6564 FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier,
6566 ullColdMethodStartAddress,
6575 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6576 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd))
6578 if(bShowVerboseOutput)
6580 FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier,
6582 ullMethodStartAddress,
6594 FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier,
6596 ullMethodStartAddress,
6603 if(bFireEventForColdSection)
6605 if(bShowVerboseOutput)
6607 FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier,
6609 ullColdMethodStartAddress,
6621 FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier,
6623 ullColdMethodStartAddress,
6634 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6635 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6636 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6637 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6638 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
6639 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) ||
6640 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
6641 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd));
6645 //---------------------------------------------------------------------------------------
6647 // Fires the IL-to-native map event for JITted methods. This is used for the runtime,
6648 // rundown start, and rundown end events that include the il-to-native map information
6651 // pMethodDesc - MethodDesc for which we'll fire the map event
6652 // dwEventOptions - Options that tells us, in the rundown case, whether we're
6653 // supposed to fire the start or end rundown events.
6657 VOID ETW::MethodLog::SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, SIZE_T pCode, ReJITID rejitID)
6667 // This is the limit on how big the il-to-native map can get, as measured by number
6668 // of entries in each parallel array (IL offset array and native offset array).
6669 // This number was chosen to ensure the overall event stays under the Windows limit
6671 const USHORT kMapEntriesMax = 7000;
6673 if (pMethodDesc == NULL)
6676 if (pMethodDesc->HasClassOrMethodInstantiation() && pMethodDesc->IsTypicalMethodDefinition())
6679 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
6680 // or profiler is loaded. So it should always be available.
6681 _ASSERTE(g_pDebugInterface != NULL);
6683 ULONGLONG ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6686 NewArrayHolder<UINT> rguiILOffset;
6687 NewArrayHolder<UINT> rguiNativeOffset;
6689 HRESULT hr = g_pDebugInterface->GetILToNativeMappingIntoArrays(
6699 // Runtime provider.
6701 // This macro already checks for the JittedMethodILToNativeMapKeyword before
6702 // choosing to fire the event
6703 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap) != 0)
6705 FireEtwMethodILToNativeMap(
6706 ullMethodIdentifier,
6708 0, // Extent: This event is only sent for JITted (not NGENd) methods, and
6709 // currently there is only one extent (hot) for JITted methods.
6713 GetClrInstanceId());
6718 // These macros already check for the JittedMethodILToNativeMapRundownKeyword
6719 // before choosing to fire the event--we further check our options to see if we
6720 // should fire the Start and / or End flavor of the event (since the keyword alone
6721 // is insufficient to distinguish these).
6723 // (for an explanation of the parameters see the FireEtwMethodILToNativeMap call above)
6724 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap) != 0)
6725 FireEtwMethodDCStartILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId());
6726 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap) != 0)
6727 FireEtwMethodDCEndILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId());
6731 VOID ETW::MethodLog::SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName)
6733 WRAPPER_NO_CONTRACT;
6736 PCWSTR szDtraceOutput1=W("");
6737 ULONG methodFlags = ETW::MethodLog::MethodStructs::JitHelperMethod; // helper flag set
6738 FireEtwMethodLoadVerbose_V1(ullHelperStartAddress,
6740 ullHelperStartAddress,
6747 GetClrInstanceId());
6752 /****************************************************************************/
6753 /* This routine sends back method events of type 'dwEventOptions', for all
6754 NGEN methods in pModule */
6755 /****************************************************************************/
6756 VOID ETW::MethodLog::SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions)
6763 #ifdef FEATURE_PREJIT
6767 #ifdef FEATURE_READYTORUN
6768 if (pModule->IsReadyToRun())
6770 ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo());
6773 // Call GetMethodDesc_NoRestore instead of GetMethodDesc to avoid restoring methods at shutdown.
6774 MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc_NoRestore();
6775 if (hotDesc != NULL)
6777 ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
6783 #endif // FEATURE_READYTORUN
6784 if (pModule->HasNativeImage())
6786 MethodIterator mi(pModule);
6790 MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc();
6791 ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
6794 #endif // FEATURE_PREJIT
6797 // Called be ETW::MethodLog::SendEventsForJitMethods
6798 // Sends the ETW events once our caller determines whether or not rejit locks can be acquired
6799 VOID ETW::MethodLog::SendEventsForJitMethodsHelper(BaseDomain *pDomainFilter,
6800 LoaderAllocator *pLoaderAllocatorFilter,
6801 DWORD dwEventOptions,
6802 BOOL fLoadOrDCStart,
6803 BOOL fUnloadOrDCEnd,
6804 BOOL fSendMethodEvent,
6805 BOOL fSendILToNativeMapEvent,
6813 EEJitManager::CodeHeapIterator heapIterator(pDomainFilter, pLoaderAllocatorFilter);
6814 while (heapIterator.Next())
6816 MethodDesc * pMD = heapIterator.GetMethod();
6820 TADDR codeStart = heapIterator.GetMethodCode();
6822 // Grab rejitID from the rejit manager. In some cases, such as collectible loader
6823 // allocators, we don't support rejit so we need to short circuit the call.
6824 // This also allows our caller to avoid having to pre-enter the rejit
6826 // see code:#TableLockHolder
6828 fGetReJitIDs ? ReJitManager::GetReJitIdNoLock(pMD, codeStart) : 0;
6830 // There are small windows of time where the heap iterator may come across a
6831 // codeStart that is not yet published to the MethodDesc. This may happen if
6832 // we're JITting the method right now on another thread, and have not completed
6833 // yet. Detect the race, and skip the method if appropriate. (If rejitID is
6834 // nonzero, there is no race, as GetReJitIdNoLock will not return a nonzero
6835 // rejitID if the codeStart has not yet been published for that rejitted version
6836 // of the method.) This check also catches recompilations due to EnC, which we do
6837 // not want to issue events for, in order to ensure xperf's assumption that
6838 // MethodDesc* + ReJITID + extent (hot vs. cold) form a unique key for code
6839 // ranges of methods
6840 if ((rejitID == 0) && (codeStart != PCODEToPINSTR(pMD->GetNativeCode())))
6843 // When we're called to announce loads, then the methodload event itself must
6844 // precede any supplemental events, so that the method load or method jitting
6845 // event is the first event the profiler sees for that MethodID (and not, say,
6846 // the MethodILToNativeMap event.)
6849 if (fSendMethodEvent)
6851 ETW::MethodLog::SendMethodEvent(
6855 NULL, // namespaceOrClassName
6857 NULL, // methodSignature
6863 // Send any supplemental events requested for this MethodID
6864 if (fSendILToNativeMapEvent)
6865 ETW::MethodLog::SendMethodILToNativeMapEvent(pMD, dwEventOptions, codeStart, rejitID);
6867 // When we're called to announce unloads, then the methodunload event itself must
6868 // come after any supplemental events, so that the method unload event is the
6869 // last event the profiler sees for this MethodID
6872 if (fSendMethodEvent)
6874 ETW::MethodLog::SendMethodEvent(
6878 NULL, // namespaceOrClassName
6880 NULL, // methodSignature
6888 /****************************************************************************/
6889 /* This routine sends back method events of type 'dwEventOptions', for all
6890 JITed methods in either a given LoaderAllocator (if pLoaderAllocatorFilter is non NULL)
6891 or in a given Domain (if pDomainFilter is non NULL) or for
6892 all methods (if both filters are null) */
6893 /****************************************************************************/
6894 // Code review indicates this method is never called with both filters NULL. Ideally we would
6895 // assert this and change the comment above, but given I am making a change late in the release I am being cautious
6896 VOID ETW::MethodLog::SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAllocator *pLoaderAllocatorFilter, DWORD dwEventOptions)
6903 #if !defined(DACCESS_COMPILE)
6906 // This is only called for JITted methods loading xor unloading
6907 BOOL fLoadOrDCStart = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny) != 0);
6908 BOOL fUnloadOrDCEnd = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny) != 0);
6909 _ASSERTE((fLoadOrDCStart || fUnloadOrDCEnd) && !(fLoadOrDCStart && fUnloadOrDCEnd));
6911 BOOL fSendMethodEvent =
6913 (ETW::EnumerationLog::EnumerationStructs::JitMethodLoad |
6914 ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart |
6915 ETW::EnumerationLog::EnumerationStructs::JitMethodUnload |
6916 ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd)) != 0;
6918 BOOL fSendILToNativeMapEvent =
6920 (ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap |
6921 ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap)) != 0;
6923 if (fSendILToNativeMapEvent)
6925 // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy
6926 // data has already been initialized, to ensure we don't try to do the lazy init
6927 // while under the implicit, notrigger CodeHeapIterator lock below.
6929 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
6930 // or profiler is loaded. So it should always be available.
6931 _ASSERTE(g_pDebugInterface != NULL);
6932 g_pDebugInterface->InitializeLazyDataIfNecessary();
6935 // #TableLockHolder:
6937 // A word about ReJitManager::TableLockHolder... As we enumerate through the functions,
6938 // we may need to grab their ReJITIDs. The ReJitManager grabs its table Crst in order to
6939 // fetch these. However, several other kinds of locks are being taken during this
6940 // enumeration, such as the SystemDomain lock and the EEJitManager::CodeHeapIterator's
6941 // lock. In order to avoid lock-leveling issues, we grab the appropriate ReJitManager
6942 // table locks after SystemDomain and before CodeHeapIterator. In particular, we need to
6943 // grab the SharedDomain's ReJitManager table lock as well as the specific AppDomain's
6944 // ReJitManager table lock for the current AppDomain we're iterating. Why the SharedDomain's
6945 // ReJitManager lock? For any given AppDomain we're iterating over, the MethodDescs we
6946 // find may be managed by that AppDomain's ReJitManger OR the SharedDomain's ReJitManager.
6947 // (This is due to generics and whether given instantiations may be shared based on their
6948 // arguments.) Therefore, we proactively take the SharedDomain's ReJitManager's table
6949 // lock up front, and then individually take the appropriate AppDomain's ReJitManager's
6950 // table lock that corresponds to the domain or module we're currently iterating over.
6953 // We only support getting rejit IDs when filtering by domain.
6956 CodeVersionManager::TableLockHolder lkRejitMgrSharedDomain(SharedDomain::GetDomain()->GetCodeVersionManager());
6957 CodeVersionManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetCodeVersionManager());
6958 SendEventsForJitMethodsHelper(pDomainFilter,
6959 pLoaderAllocatorFilter,
6964 fSendILToNativeMapEvent,
6969 SendEventsForJitMethodsHelper(pDomainFilter,
6970 pLoaderAllocatorFilter,
6975 fSendILToNativeMapEvent,
6978 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
6979 #endif // !DACCESS_COMPILE
6982 //---------------------------------------------------------------------------------------
6984 // Wrapper around IterateDomain, which locks the AppDomain to be <
6985 // STAGE_FINALIZED until the iteration is complete.
6988 // pAppDomain - AppDomain to iterate
6989 // enumerationOptions - Flags indicating what to enumerate. Just passed
6990 // straight through to IterateDomain
6992 VOID ETW::EnumerationLog::IterateAppDomain(AppDomain * pAppDomain, DWORD enumerationOptions)
6998 PRECONDITION(pAppDomain != NULL);
7002 // Hold the system domain lock during the entire iteration, so we can
7003 // ensure the App Domain does not get finalized until we're all done
7004 SystemDomain::LockHolder lh;
7006 // Now it's safe to do the iteration
7007 IterateDomain(pAppDomain, enumerationOptions);
7010 /********************************************************************************/
7011 /* This routine fires ETW events for
7014 DomainModule's in them,
7016 JIT methods in them,
7017 and the NGEN methods in them
7018 based on enumerationOptions.*/
7019 /********************************************************************************/
7020 VOID ETW::EnumerationLog::IterateDomain(BaseDomain *pDomain, DWORD enumerationOptions)
7025 PRECONDITION(pDomain != NULL);
7028 #if defined(_DEBUG) && !defined(DACCESS_COMPILE)
7029 // Do not call IterateDomain() directly with an AppDomain. Use
7030 // IterateAppDomain(), whch wraps this function with a hold on the
7031 // SystemDomain lock, which ensures pDomain's type data doesn't disappear
7033 if (pDomain->IsAppDomain())
7035 _ASSERTE(SystemDomain::IsUnderDomainLock());
7037 #endif // defined(_DEBUG) && !defined(DACCESS_COMPILE)
7041 // DC Start events for Domain
7042 if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7044 ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions);
7047 // DC End or Unload Jit Method events
7048 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny)
7050 ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions);
7053 if (pDomain->IsAppDomain())
7055 AppDomain::AssemblyIterator assemblyIterator = pDomain->AsAppDomain()->IterateAssembliesEx(
7056 (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
7057 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
7058 while (assemblyIterator.Next(pDomainAssembly.This()))
7060 CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
7061 BOOL bIsDomainNeutral = pAssembly->IsDomainNeutral();
7062 if (bIsDomainNeutral)
7064 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7066 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7069 DomainModuleIterator domainModuleIterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
7070 while (domainModuleIterator.Next())
7072 Module * pModule = domainModuleIterator.GetModule();
7073 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7076 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7077 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7079 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7085 SharedDomain::SharedAssemblyIterator sharedDomainIterator;
7086 while (sharedDomainIterator.Next())
7088 Assembly * pAssembly = sharedDomainIterator.GetAssembly();
7089 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7091 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7094 ModuleIterator domainModuleIterator = pAssembly->IterateModules();
7095 while (domainModuleIterator.Next())
7097 Module * pModule = domainModuleIterator.GetModule();
7098 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7101 if ((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7102 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7104 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7109 // DC Start or Load Jit Method events
7110 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny)
7112 ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions);
7115 // DC End or Unload events for Domain
7116 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7117 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7119 ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions);
7121 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7125 /********************************************************************************/
7126 /* This routine fires ETW events for
7127 Assembly in LoaderAllocator,
7128 DomainModule's in them,
7130 JIT methods in them,
7131 and the NGEN methods in them
7132 based on enumerationOptions.*/
7133 /********************************************************************************/
7134 VOID ETW::EnumerationLog::IterateCollectibleLoaderAllocator(AssemblyLoaderAllocator *pLoaderAllocator, DWORD enumerationOptions)
7139 PRECONDITION(pLoaderAllocator != NULL);
7144 // Unload Jit Method events
7145 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload)
7147 ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions);
7150 // Iterate on all DomainAssembly loaded from the same AssemblyLoaderAllocator
7151 DomainAssemblyIterator domainAssemblyIt = pLoaderAllocator->Id()->GetDomainAssemblyIterator();
7152 while (!domainAssemblyIt.end())
7154 Assembly *pAssembly = domainAssemblyIt->GetAssembly(); // TODO: handle iterator
7155 _ASSERTE(!pAssembly->IsDomainNeutral()); // Collectible Assemblies are not domain neutral.
7157 DomainModuleIterator domainModuleIterator = domainAssemblyIt->IterateModules(kModIterIncludeLoaded);
7158 while (domainModuleIterator.Next())
7160 Module *pModule = domainModuleIterator.GetModule();
7161 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7164 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
7166 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7172 // Load Jit Method events
7173 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad)
7175 ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions);
7177 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7180 /********************************************************************************/
7181 /* This routine fires ETW events for Assembly and the DomainModule's in them
7182 based on enumerationOptions.*/
7183 /********************************************************************************/
7184 VOID ETW::EnumerationLog::IterateAssembly(Assembly *pAssembly, DWORD enumerationOptions)
7189 PRECONDITION(pAssembly != NULL);
7194 // DC Start events for Assembly
7195 if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7197 ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions);
7200 // DC Start, DCEnd, events for DomainModule
7201 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7202 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart))
7204 if(pAssembly->GetDomain()->IsAppDomain())
7206 DomainModuleIterator dmIterator = pAssembly->FindDomainAssembly(pAssembly->GetDomain()->AsAppDomain())->IterateModules(kModIterIncludeLoaded);
7207 while (dmIterator.Next())
7209 ETW::LoaderLog::SendModuleEvent(dmIterator.GetModule(), enumerationOptions, TRUE);
7214 // DC End or Unload events for Assembly
7215 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7216 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7218 ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions);
7220 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7223 /********************************************************************************/
7224 /* This routine fires ETW events for Module, their range information and the NGEN methods in them
7225 based on enumerationOptions.*/
7226 /********************************************************************************/
7227 VOID ETW::EnumerationLog::IterateModule(Module *pModule, DWORD enumerationOptions)
7232 PRECONDITION(pModule != NULL);
7237 // DC Start events for Module
7238 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
7239 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart))
7241 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7244 // DC Start or Load or DC End or Unload Ngen Method events
7245 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
7246 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
7247 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) ||
7248 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd))
7250 ETW::MethodLog::SendEventsForNgenMethods(pModule, enumerationOptions);
7253 // DC End or Unload events for Module
7254 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7255 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
7256 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd))
7258 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7261 // If we're logging types, then update the internal Type hash table to account
7262 // for the module's unloading
7263 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::TypeUnload)
7265 ETW::TypeSystemLog::OnModuleUnload(pModule);
7268 // ModuleRangeLoadPrivate events for module range information from attach/detach scenarios
7269 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
7270 TRACE_LEVEL_INFORMATION,
7271 CLR_PERFTRACK_PRIVATE_KEYWORD) &&
7272 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate))
7274 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7276 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7279 //---------------------------------------------------------------------------------------
7281 // This routine sends back domain, assembly, module and method events based on
7282 // enumerationOptions.
7285 // * moduleFilter - if non-NULL, events from only moduleFilter module are reported
7286 // * domainFilter - if non-NULL, events from only domainFilter domain are reported
7287 // * enumerationOptions - Flags from ETW::EnumerationLog::EnumerationStructs which
7288 // describe which events should be sent.
7291 // * if all filter args are NULL, events from all domains are reported
7296 VOID ETW::EnumerationLog::EnumerationHelper(Module *moduleFilter, BaseDomain *domainFilter, DWORD enumerationOptions)
7303 // Disable IBC logging during ETW enumeration since we call a lot of functionality
7304 // that does logging and causes problems in the shutdown path due to critical
7305 // section access for IBC logging
7306 IBCLoggingDisabler disableLogging;
7310 // Iteratate modules first because their number is ussualy smaller then the number of methods.
7311 // Thus hitting a timeout due to a large number of methods will not affect modules rundown.tf g
7312 ETW::EnumerationLog::IterateModule(moduleFilter, enumerationOptions);
7314 // As best I can tell from code review, these if statements below are never true. There is
7315 // only one caller to this method that specifies a moduleFilter, ETW::LoaderLog::ModuleLoad.
7316 // That method never specifies these flags. Because it is late in a release cycle I am not
7317 // making a change, but if you see this comment early in the next release cycle consider
7318 // deleting this apparently dead code.
7320 // DC End or Unload Jit Method events from all Domains
7321 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny)
7323 ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions);
7326 // DC Start or Load Jit Method events from all Domains
7327 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny)
7329 ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions);
7336 if(domainFilter->IsAppDomain())
7338 ETW::EnumerationLog::IterateAppDomain(domainFilter->AsAppDomain(), enumerationOptions);
7342 ETW::EnumerationLog::IterateDomain(domainFilter, enumerationOptions);
7347 AppDomainIterator appDomainIterator(FALSE);
7348 while(appDomainIterator.Next())
7350 AppDomain *pDomain = appDomainIterator.GetDomain();
7351 if (pDomain != NULL)
7353 ETW::EnumerationLog::IterateAppDomain(pDomain, enumerationOptions);
7357 ETW::EnumerationLog::IterateDomain(SharedDomain::GetDomain(), enumerationOptions);
7362 #endif // !FEATURE_REDHAWK
7364 #ifdef FEATURE_PERFTRACING
7365 #include "eventpipe.h"
7366 bool EventPipeHelper::Enabled()
7368 LIMITED_METHOD_CONTRACT;
7369 return EventPipe::Enabled();
7371 #endif // FEATURE_PERFTRACING