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 NativeCodeId, 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 NativeCodeIds, 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 NativeCodeId, 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 NativeCodeId. Thus, your unique key for a method extent would be MethodID +
75 // NativeCodeId + 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, nativeCodeId) \
81 if (nativeCodeId == 0) \
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, nativeCodeId); } \
87 #define FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, nativeCodeId) \
89 if (nativeCodeId == 0) \
90 { FireEtwMethodLoad_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID); } \
92 { FireEtwMethodLoad_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, nativeCodeId); } \
95 #define FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, nativeCodeId) \
97 if (nativeCodeId == 0) \
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, nativeCodeId); } \
103 #define FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, nativeCodeId) \
105 if (nativeCodeId == 0) \
106 { FireEtwMethodUnload_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
108 { FireEtwMethodUnload_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, nativeCodeId); } \
111 #define FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, nativeCodeId) \
113 if (nativeCodeId == 0) \
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, nativeCodeId); } \
119 #define FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, nativeCodeId) \
121 if (nativeCodeId == 0) \
122 { FireEtwMethodDCStart_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
124 { FireEtwMethodDCStart_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, nativeCodeId); } \
127 #define FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, nativeCodeId) \
129 if (nativeCodeId == 0) \
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, nativeCodeId); } \
135 #define FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, nativeCodeId) \
137 if (nativeCodeId == 0) \
138 { FireEtwMethodDCEnd_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
140 { FireEtwMethodDCEnd_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, nativeCodeId); } \
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 need 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_DOTNET_Context,
186 TRACE_LEVEL_INFORMATION,
188 && ! ( ETW_TRACING_CATEGORY_ENABLED(
189 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_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_DOTNET_Context,
207 TRACE_LEVEL_INFORMATION,
208 CLR_RUNDOWNNGEN_KEYWORD)
209 && ! ( ETW_TRACING_CATEGORY_ENABLED(
210 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_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)
262 // The stack walk performed below can cause allocations (thus entering the host). But
263 // this is acceptable, since we're not supporting the use of SQL/F1 profiling and
264 // full-blown ETW CLR stacks (which would be redundant).
265 PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
268 ETW::SamplingLog::EtwStackWalkStatus stackwalkStatus = SaveCurrentStack();
270 _ASSERTE(m_FrameCount < ETW::SamplingLog::s_MaxStackSize);
272 // this not really needed, but let's do it
273 // because we use the framecount while dumping the stack event
274 for(int i=m_FrameCount; i<ETW::SamplingLog::s_MaxStackSize; i++)
278 // This is for consumers to work correctly because the number of
279 // frames in the manifest file is specified to be 2
283 *frameCount = m_FrameCount;
284 *Stack = (PVOID *)m_EBPStack;
285 return stackwalkStatus;
288 /*************************************/
289 /* Function to save the stack on the current thread */
290 /*************************************/
291 ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::SaveCurrentStack(int skipTopNFrames)
301 if (!IsGarbageCollectorFullyInitialized())
303 // If the GC isn't ready yet, then there won't be any interesting
304 // managed code on the stack to walk. Plus, the stack walk itself may
305 // hit problems (e.g., when calling into the code manager) if it's run
306 // too early during startup.
307 return ETW::SamplingLog::UnInitialized;
309 #ifndef DACCESS_COMPILE
310 #ifdef _TARGET_AMD64_
311 if (RtlVirtualUnwind_Unsafe == NULL)
313 // We haven't even set up the RtlVirtualUnwind function pointer yet,
314 // so it's too early to try stack walking.
315 return ETW::SamplingLog::UnInitialized;
317 #endif // _TARGET_AMD64_
318 Thread *pThread = GetThread();
321 return ETW::SamplingLog::UnInitialized;
323 // The thread should not have a hijack set up or we can't walk the stack.
324 if (pThread->m_State & Thread::TS_Hijacked) {
325 return ETW::SamplingLog::UnInitialized;
327 if (pThread->IsEtwStackWalkInProgress())
329 return ETW::SamplingLog::InProgress;
331 pThread->MarkEtwStackWalkInProgress();
335 CallStackFrame *currentEBP = GetEbp();
336 CallStackFrame *lastEBP = NULL;
338 // The EBP stack walk below is meant to be extremely fast. It does not attempt to protect
339 // against cases of stack corruption. *BUT* it does need to validate a "sane" EBP chain.
341 // Ensure the EBP in the starting frame is "reasonable" (i.e. above the address of a local)
342 if ((SIZE_T) currentEBP > (SIZE_T)¤tEBP)
346 lastEBP = currentEBP;
347 currentEBP = currentEBP->m_Next;
349 // Check for stack upper limit; we don't check the lower limit on each iteration
350 // (we did it at the top) and each subsequent value in the loop is larger than
351 // the previous (see the check "currentEBP < lastEBP" below)
352 if((SIZE_T)currentEBP > (SIZE_T)Thread::GetStackUpperBound())
357 // If we have a too small address, we are probably bad
358 if((SIZE_T)currentEBP < (SIZE_T)0x10000)
361 if((SIZE_T)currentEBP < (SIZE_T)lastEBP)
366 // Skip the top N frames
372 // Save the Return Address for symbol decoding
373 Append(lastEBP->m_ReturnAddress);
378 ClrCaptureContext(&ctx);
379 UINT_PTR ControlPc = 0;
380 UINT_PTR CurrentSP = 0, PrevSP = 0;
384 // Unwind to the caller
385 ControlPc = Thread::VirtualUnwindCallFrame(&ctx);
387 // This is to take care of recursion
388 CurrentSP = (UINT_PTR)GetSP(&ctx);
390 // when to break from this loop
391 if ( ControlPc == 0 || ( PrevSP == CurrentSP ) )
396 // Skip the top N frames
397 if ( skipTopNFrames ) {
402 // Add the stack frame to the list
407 #endif //_TARGET_X86_
408 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
409 pThread->MarkEtwStackWalkCompleted();
410 #endif //!DACCESS_COMPILE
412 return ETW::SamplingLog::Completed;
415 #endif // !defined(FEATURE_PAL)
416 #endif // !FEATURE_REDHAWK
418 /****************************************************************************/
419 /* Methods that are called from the runtime */
420 /****************************************************************************/
422 /****************************************************************************/
423 /* Methods for rundown events */
424 /****************************************************************************/
426 /***************************************************************************/
427 /* This function should be called from the event tracing callback routine
428 when the private CLR provider is enabled */
429 /***************************************************************************/
431 #ifndef FEATURE_REDHAWK
433 VOID ETW::GCLog::GCSettingsEvent()
435 if (GCHeapUtilities::IsGCHeapInitialized())
437 if (ETW_TRACING_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context,
440 ETW::GCLog::ETW_GC_INFO Info;
442 Info.GCSettings.ServerGC = GCHeapUtilities::IsServerHeap ();
443 Info.GCSettings.SegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (false);
444 Info.GCSettings.LargeObjectSegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (true);
445 FireEtwGCSettings_V1(Info.GCSettings.SegmentSize, Info.GCSettings.LargeObjectSegmentSize, Info.GCSettings.ServerGC, GetClrInstanceId());
447 GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments();
451 #endif // !FEATURE_REDHAWK
454 //---------------------------------------------------------------------------------------
455 // Code for sending GC heap object events is generally the same for both FEATURE_REDHAWK
456 // and !FEATURE_REDHAWK builds
457 //---------------------------------------------------------------------------------------
459 bool s_forcedGCInProgress = false;
463 ForcedGCHolder() { LIMITED_METHOD_CONTRACT; s_forcedGCInProgress = true; }
464 ~ForcedGCHolder() { LIMITED_METHOD_CONTRACT; s_forcedGCInProgress = false; }
467 BOOL ETW::GCLog::ShouldWalkStaticsAndCOMForEtw()
469 LIMITED_METHOD_CONTRACT;
471 return s_forcedGCInProgress &&
472 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
473 TRACE_LEVEL_INFORMATION,
474 CLR_GCHEAPDUMP_KEYWORD);
477 // Simple helpers called by the GC to decide whether it needs to do a walk of heap
478 // objects and / or roots.
480 BOOL ETW::GCLog::ShouldWalkHeapObjectsForEtw()
482 LIMITED_METHOD_CONTRACT;
483 return s_forcedGCInProgress &&
484 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
485 TRACE_LEVEL_INFORMATION,
486 CLR_GCHEAPDUMP_KEYWORD);
489 BOOL ETW::GCLog::ShouldWalkHeapRootsForEtw()
491 LIMITED_METHOD_CONTRACT;
492 return s_forcedGCInProgress &&
493 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
494 TRACE_LEVEL_INFORMATION,
495 CLR_GCHEAPDUMP_KEYWORD);
498 BOOL ETW::GCLog::ShouldTrackMovementForEtw()
500 LIMITED_METHOD_CONTRACT;
501 return ETW_TRACING_CATEGORY_ENABLED(
502 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
503 TRACE_LEVEL_INFORMATION,
504 CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD);
507 // Batches the list of moved/surviving references for the GCBulkMovedObjectRanges /
508 // GCBulkSurvivingObjectRanges events
509 struct EtwGcMovementContext
512 // An instance of EtwGcMovementContext is dynamically allocated and stored
513 // inside of MovedReferenceContextForEtwAndProfapi, which in turn is dynamically
514 // allocated and pointed to by a profiling_context pointer created by the GC on the stack.
515 // This is used to batch and send GCBulkSurvivingObjectRanges events and
516 // GCBulkMovedObjectRanges events. This method is passed a pointer to
517 // MovedReferenceContextForEtwAndProfapi::pctxEtw; if non-NULL it gets returned;
518 // else, a new EtwGcMovementContext is allocated, stored in that pointer, and
519 // then returned. Callers should test for NULL, which can be returned if out of
521 static EtwGcMovementContext * GetOrCreateInGCContext(EtwGcMovementContext ** ppContext)
523 LIMITED_METHOD_CONTRACT;
525 _ASSERTE(ppContext != NULL);
527 EtwGcMovementContext * pContext = *ppContext;
528 if (pContext == NULL)
530 pContext = new (nothrow) EtwGcMovementContext;
531 *ppContext = pContext;
536 EtwGcMovementContext() :
537 iCurBulkSurvivingObjectRanges(0),
538 iCurBulkMovedObjectRanges(0)
540 LIMITED_METHOD_CONTRACT;
544 // Resets structure for reuse on construction, and after each flush.
545 // (Intentionally leave iCurBulk* as is, since they persist across flushes within a GC.)
548 LIMITED_METHOD_CONTRACT;
549 cBulkSurvivingObjectRanges = 0;
550 cBulkMovedObjectRanges = 0;
551 ZeroMemory(rgGCBulkSurvivingObjectRanges, sizeof(rgGCBulkSurvivingObjectRanges));
552 ZeroMemory(rgGCBulkMovedObjectRanges, sizeof(rgGCBulkMovedObjectRanges));
555 //---------------------------------------------------------------------------------------
556 // GCBulkSurvivingObjectRanges
557 //---------------------------------------------------------------------------------------
559 // Sequence number for each GCBulkSurvivingObjectRanges event
560 UINT iCurBulkSurvivingObjectRanges;
562 // Number of surviving object ranges currently filled out in rgGCBulkSurvivingObjectRanges array
563 UINT cBulkSurvivingObjectRanges;
565 // Struct array containing the primary data for each GCBulkSurvivingObjectRanges
566 // event. Fix the size so the total event stays well below the 64K limit (leaving
567 // lots of room for non-struct fields that come before the values data)
568 EventStructGCBulkSurvivingObjectRangesValue rgGCBulkSurvivingObjectRanges[
569 (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkSurvivingObjectRangesValue)];
571 //---------------------------------------------------------------------------------------
572 // GCBulkMovedObjectRanges
573 //---------------------------------------------------------------------------------------
575 // Sequence number for each GCBulkMovedObjectRanges event
576 UINT iCurBulkMovedObjectRanges;
578 // Number of Moved object ranges currently filled out in rgGCBulkMovedObjectRanges array
579 UINT cBulkMovedObjectRanges;
581 // Struct array containing the primary data for each GCBulkMovedObjectRanges
582 // event. Fix the size so the total event stays well below the 64K limit (leaving
583 // lots of room for non-struct fields that come before the values data)
584 EventStructGCBulkMovedObjectRangesValue rgGCBulkMovedObjectRanges[
585 (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkMovedObjectRangesValue)];
588 // Contains above struct for ETW, plus extra info (opaque to us) used by the profiling
589 // API to track its own information.
590 struct MovedReferenceContextForEtwAndProfapi
592 // An instance of MovedReferenceContextForEtwAndProfapi is dynamically allocated and
593 // pointed to by a profiling_context pointer created by the GC on the stack. This is used to
594 // batch and send GCBulkSurvivingObjectRanges events and GCBulkMovedObjectRanges
595 // events and the corresponding callbacks for profapi profilers. This method is
596 // passed a pointer to a MovedReferenceContextForEtwAndProfapi; if non-NULL it gets
597 // returned; else, a new MovedReferenceContextForEtwAndProfapi is allocated, stored
598 // in that pointer, and then returned. Callers should test for NULL, which can be
599 // returned if out of memory
600 static MovedReferenceContextForEtwAndProfapi * CreateInGCContext(LPVOID pvContext)
602 LIMITED_METHOD_CONTRACT;
604 _ASSERTE(pvContext != NULL);
606 MovedReferenceContextForEtwAndProfapi * pContext = *(MovedReferenceContextForEtwAndProfapi **) pvContext;
608 // Shouldn't be called if the context was already created. Perhaps someone made
609 // one too many BeginMovedReferences calls, or didn't have an EndMovedReferences
611 _ASSERTE(pContext == NULL);
613 pContext = new (nothrow) MovedReferenceContextForEtwAndProfapi;
614 *(MovedReferenceContextForEtwAndProfapi **) pvContext = pContext;
620 MovedReferenceContextForEtwAndProfapi() :
625 LIMITED_METHOD_CONTRACT;
629 EtwGcMovementContext * pctxEtw;
633 //---------------------------------------------------------------------------------------
635 // Called by the GC for each moved or surviving reference that it encounters. This
636 // batches the info into our context's buffer, and flushes that buffer to ETW as it fills
640 // * pbMemBlockStart - Start of moved/surviving block
641 // * pbMemBlockEnd - Next pointer after end of moved/surviving block
642 // * cbRelocDistance - How far did the block move? (0 for non-compacted / surviving
643 // references; negative if moved to earlier addresses)
644 // * profilingContext - Where our context is stored
645 // * fCompacting - Is this a compacting GC? Used to decide whether to send the moved
646 // or surviving event
650 void ETW::GCLog::MovedReference(
651 BYTE * pbMemBlockStart,
652 BYTE * pbMemBlockEnd,
653 ptrdiff_t cbRelocDistance,
654 size_t profilingContext,
656 BOOL fAllowProfApiNotification /* = TRUE */)
663 CAN_TAKE_LOCK; // EEToProfInterfaceImpl::AllocateMovedReferencesData takes lock
667 MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi =
668 (MovedReferenceContextForEtwAndProfapi *) profilingContext;
669 if (pCtxForEtwAndProfapi == NULL)
671 _ASSERTE(!"MovedReference() encountered a NULL profilingContext");
675 #ifdef PROFILING_SUPPORTED
677 if (fAllowProfApiNotification)
679 BEGIN_PIN_PROFILER(CORProfilerTrackGC() || CORProfilerTrackGCMovedObjects());
680 g_profControlBlock.pProfInterface->MovedReference(pbMemBlockStart,
683 &(pCtxForEtwAndProfapi->pctxProfAPI),
687 #endif // PROFILING_SUPPORTED
691 if (!ShouldTrackMovementForEtw())
694 EtwGcMovementContext * pContext =
695 EtwGcMovementContext::GetOrCreateInGCContext(&pCtxForEtwAndProfapi->pctxEtw);
696 if (pContext == NULL)
703 _ASSERTE(pContext->cBulkMovedObjectRanges < _countof(pContext->rgGCBulkMovedObjectRanges));
704 EventStructGCBulkMovedObjectRangesValue * pValue =
705 &pContext->rgGCBulkMovedObjectRanges[pContext->cBulkMovedObjectRanges];
706 pValue->OldRangeBase = pbMemBlockStart;
707 pValue->NewRangeBase = pbMemBlockStart + cbRelocDistance;
708 pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart;
709 pContext->cBulkMovedObjectRanges++;
711 // If buffer is now full, empty it into ETW
712 if (pContext->cBulkMovedObjectRanges == _countof(pContext->rgGCBulkMovedObjectRanges))
714 FireEtwGCBulkMovedObjectRanges(
715 pContext->iCurBulkMovedObjectRanges,
716 pContext->cBulkMovedObjectRanges,
718 sizeof(pContext->rgGCBulkMovedObjectRanges[0]),
719 &pContext->rgGCBulkMovedObjectRanges[0]);
721 pContext->iCurBulkMovedObjectRanges++;
727 // Surviving references
729 _ASSERTE(pContext->cBulkSurvivingObjectRanges < _countof(pContext->rgGCBulkSurvivingObjectRanges));
730 EventStructGCBulkSurvivingObjectRangesValue * pValue =
731 &pContext->rgGCBulkSurvivingObjectRanges[pContext->cBulkSurvivingObjectRanges];
732 pValue->RangeBase = pbMemBlockStart;
733 pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart;
734 pContext->cBulkSurvivingObjectRanges++;
736 // If buffer is now full, empty it into ETW
737 if (pContext->cBulkSurvivingObjectRanges == _countof(pContext->rgGCBulkSurvivingObjectRanges))
739 FireEtwGCBulkSurvivingObjectRanges(
740 pContext->iCurBulkSurvivingObjectRanges,
741 pContext->cBulkSurvivingObjectRanges,
743 sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]),
744 &pContext->rgGCBulkSurvivingObjectRanges[0]);
746 pContext->iCurBulkSurvivingObjectRanges++;
753 //---------------------------------------------------------------------------------------
755 // Called by the GC just before it begins enumerating plugs. Gives us a chance to
756 // allocate our context structure, to allow us to batch plugs before firing events
760 // * pProfilingContext - Points to location on stack (in GC function) where we can
761 // store a pointer to the context we allocate
765 VOID ETW::GCLog::BeginMovedReferences(size_t * pProfilingContext)
767 LIMITED_METHOD_CONTRACT;
769 MovedReferenceContextForEtwAndProfapi::CreateInGCContext(LPVOID(pProfilingContext));
773 //---------------------------------------------------------------------------------------
775 // Called by the GC at the end of a heap walk to give us a place to flush any remaining
776 // buffers of data to ETW or the profapi profiler
779 // profilingContext - Our context we built up during the heap walk
783 VOID ETW::GCLog::EndMovedReferences(size_t profilingContext, BOOL fAllowProfApiNotification /* = TRUE */)
794 MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi = (MovedReferenceContextForEtwAndProfapi *) profilingContext;
795 if (pCtxForEtwAndProfapi == NULL)
797 _ASSERTE(!"EndMovedReferences() encountered a NULL profilingContext");
801 #ifdef PROFILING_SUPPORTED
803 if (fAllowProfApiNotification)
805 BEGIN_PIN_PROFILER(CORProfilerTrackGC() || CORProfilerTrackGCMovedObjects());
806 g_profControlBlock.pProfInterface->EndMovedReferences(&(pCtxForEtwAndProfapi->pctxProfAPI));
809 #endif //PROFILING_SUPPORTED
813 if (!ShouldTrackMovementForEtw())
816 // If context isn't already set up for us, then we haven't been collecting any data
818 EtwGcMovementContext * pContext = pCtxForEtwAndProfapi->pctxEtw;
819 if (pContext == NULL)
822 // Flush any remaining moved or surviving range data
824 if (pContext->cBulkMovedObjectRanges > 0)
826 FireEtwGCBulkMovedObjectRanges(
827 pContext->iCurBulkMovedObjectRanges,
828 pContext->cBulkMovedObjectRanges,
830 sizeof(pContext->rgGCBulkMovedObjectRanges[0]),
831 &pContext->rgGCBulkMovedObjectRanges[0]);
834 if (pContext->cBulkSurvivingObjectRanges > 0)
836 FireEtwGCBulkSurvivingObjectRanges(
837 pContext->iCurBulkSurvivingObjectRanges,
838 pContext->cBulkSurvivingObjectRanges,
840 sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]),
841 &pContext->rgGCBulkSurvivingObjectRanges[0]);
844 pCtxForEtwAndProfapi->pctxEtw = NULL;
848 /***************************************************************************/
849 /* This implements the public runtime provider's GCHeapCollectKeyword. It
850 performs a full, gen-2, blocking GC. */
851 /***************************************************************************/
852 VOID ETW::GCLog::ForceGC(LONGLONG l64ClientSequenceNumber)
862 #ifndef FEATURE_REDHAWK
863 if (!IsGarbageCollectorFullyInitialized())
865 #endif // FEATURE_REDHAWK
867 InterlockedExchange64(&s_l64LastClientSequenceNumber, l64ClientSequenceNumber);
869 ForceGCForDiagnostics();
872 //---------------------------------------------------------------------------------------
874 // Helper to fire the GCStart event. Figures out which version of GCStart to fire, and
875 // includes the client sequence number, if available.
878 // pGcInfo - ETW_GC_INFO containing details from GC about this collection
882 VOID ETW::GCLog::FireGcStart(ETW_GC_INFO * pGcInfo)
884 LIMITED_METHOD_CONTRACT;
886 if (ETW_TRACING_CATEGORY_ENABLED(
887 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
888 TRACE_LEVEL_INFORMATION,
891 // If the controller specified a client sequence number for us to log with this
892 // GCStart, then retrieve it
893 LONGLONG l64ClientSequenceNumberToLog = 0;
894 if ((s_l64LastClientSequenceNumber != 0) &&
895 (pGcInfo->GCStart.Depth == GCHeapUtilities::GetGCHeap()->GetMaxGeneration()) &&
896 (pGcInfo->GCStart.Reason == ETW_GC_INFO::GC_INDUCED))
898 l64ClientSequenceNumberToLog = InterlockedExchange64(&s_l64LastClientSequenceNumber, 0);
901 FireEtwGCStart_V2(pGcInfo->GCStart.Count, pGcInfo->GCStart.Depth, pGcInfo->GCStart.Reason, pGcInfo->GCStart.Type, GetClrInstanceId(), l64ClientSequenceNumberToLog);
905 //---------------------------------------------------------------------------------------
907 // Contains code common to profapi and ETW scenarios where the profiler wants to force
908 // the CLR to perform a GC. The important work here is to create a managed thread for
909 // the current thread BEFORE the GC begins. On both ETW and profapi threads, there may
910 // not yet be a managed thread object. But some scenarios require a managed thread
911 // object be present (notably if we need to call into Jupiter during the GC).
914 // HRESULT indicating success or failure
917 // Caller should ensure that the EE has fully started up and that the GC heap is
918 // initialized enough to actually perform a GC
922 HRESULT ETW::GCLog::ForceGCForDiagnostics()
934 #ifndef FEATURE_REDHAWK
935 // Caller should ensure we're past startup.
936 _ASSERTE(IsGarbageCollectorFullyInitialized());
938 // In immersive apps the GarbageCollect() call below will call into Jupiter,
939 // which will call back into the runtime to track references. This call
940 // chain would cause a Thread object to be created for this thread while code
941 // higher on the stack owns the ThreadStoreLock. This will lead to asserts
942 // since the ThreadStoreLock is non-reentrant. To avoid this we'll create
943 // the Thread object here instead.
944 if (GetThreadNULLOk() == NULL)
947 SetupThreadNoThrow(&hr);
952 ASSERT_NO_EE_LOCKS_HELD();
956 // Need to switch to cooperative mode as the thread will access managed
957 // references (through Jupiter callbacks).
959 #endif // FEATURE_REDHAWK
961 ForcedGCHolder forcedGCHolder;
963 hr = GCHeapUtilities::GetGCHeap()->GarbageCollect(
964 -1, // all generations should be collected
965 false, // low_memory_p
966 collection_blocking);
968 #ifndef FEATURE_REDHAWK
971 EX_END_CATCH(RethrowCorruptingExceptions);
972 #endif // FEATURE_REDHAWK
982 //---------------------------------------------------------------------------------------
983 // WalkStaticsAndCOMForETW walks both CCW/RCW objects and static variables.
984 //---------------------------------------------------------------------------------------
986 VOID ETW::GCLog::WalkStaticsAndCOMForETW()
997 BulkTypeEventLogger typeLogger;
1000 BulkComLogger comLogger(&typeLogger);
1001 comLogger.LogAllComObjects();
1003 // Walk static variables
1004 BulkStaticsLogger staticLogger(&typeLogger);
1005 staticLogger.LogAllStatics();
1007 // Ensure all loggers have written all events, fire type logger last to batch events
1008 // (FireBulkComEvent or FireBulkStaticsEvent may queue up additional types).
1009 comLogger.FireBulkComEvent();
1010 staticLogger.FireBulkStaticsEvent();
1011 typeLogger.FireBulkTypeEvent();
1016 EX_END_CATCH(SwallowAllExceptions);
1020 //---------------------------------------------------------------------------------------
1021 // BulkStaticsLogger: Batches up and logs static variable roots
1022 //---------------------------------------------------------------------------------------
1024 BulkComLogger::BulkComLogger(BulkTypeEventLogger *typeLogger)
1025 : m_currRcw(0), m_currCcw(0), m_typeLogger(typeLogger), m_etwRcwData(0), m_etwCcwData(0), m_enumResult(0)
1035 m_etwRcwData = new EventRCWEntry[kMaxRcwCount];
1036 m_etwCcwData = new EventCCWEntry[kMaxCcwCount];
1039 BulkComLogger::~BulkComLogger()
1052 delete [] m_etwRcwData;
1055 delete [] m_etwCcwData;
1059 CCWEnumerationEntry *curr = m_enumResult;
1062 CCWEnumerationEntry *next = curr->Next;
1069 void BulkComLogger::FireBulkComEvent()
1071 WRAPPER_NO_CONTRACT;
1077 void BulkComLogger::WriteRcw(RCW *pRcw, Object *obj)
1084 PRECONDITION(pRcw != NULL);
1085 PRECONDITION(obj != NULL);
1089 _ASSERTE(m_currRcw < kMaxRcwCount);
1091 #ifdef FEATURE_COMINTEROP
1092 EventRCWEntry &rcw = m_etwRcwData[m_currRcw];
1093 rcw.ObjectID = (ULONGLONG)obj;
1094 rcw.TypeID = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1095 rcw.IUnk = (ULONGLONG)pRcw->GetIUnknown_NoAddRef();
1096 rcw.VTable = (ULONGLONG)pRcw->GetVTablePtr();
1097 rcw.RefCount = pRcw->GetRefCount();
1100 if (++m_currRcw >= kMaxRcwCount)
1105 void BulkComLogger::FlushRcw()
1115 _ASSERTE(m_currRcw <= kMaxRcwCount);
1122 for (int i = 0; i < m_currRcw; ++i)
1123 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, m_etwRcwData[i].TypeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1126 unsigned short instance = GetClrInstanceId();
1128 #if !defined(FEATURE_PAL)
1129 EVENT_DATA_DESCRIPTOR eventData[3];
1130 EventDataDescCreate(&eventData[0], &m_currRcw, sizeof(const unsigned int));
1131 EventDataDescCreate(&eventData[1], &instance, sizeof(const unsigned short));
1132 EventDataDescCreate(&eventData[2], m_etwRcwData, sizeof(EventRCWEntry) * m_currRcw);
1134 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRCW, _countof(eventData), eventData);
1136 ULONG result = FireEtXplatGCBulkRCW(m_currRcw, instance, sizeof(EventRCWEntry) * m_currRcw, m_etwRcwData);
1137 #endif // !defined(FEATURE_PAL)
1138 result |= EventPipeWriteEventGCBulkRCW(m_currRcw, instance, sizeof(EventRCWEntry) * m_currRcw, m_etwRcwData);
1140 _ASSERTE(result == ERROR_SUCCESS);
1145 void BulkComLogger::WriteCcw(ComCallWrapper *pCcw, Object **handle, Object *obj)
1152 PRECONDITION(handle != NULL);
1153 PRECONDITION(obj != NULL);
1157 _ASSERTE(m_currCcw < kMaxCcwCount);
1159 #ifdef FEATURE_COMINTEROP
1160 IUnknown *iUnk = NULL;
1162 ULONG jupiterRefCount = 0;
1167 iUnk = pCcw->GetOuter();
1169 iUnk = pCcw->GetBasicIP(true);
1171 refCount = pCcw->GetRefCount();
1172 jupiterRefCount = pCcw->GetJupiterRefCount();
1174 if (pCcw->IsWrapperActive())
1175 flags |= EventCCWEntry::Strong;
1177 if (pCcw->IsPegged())
1178 flags |= EventCCWEntry::Pegged;
1181 EventCCWEntry &ccw = m_etwCcwData[m_currCcw++];
1182 ccw.RootID = (ULONGLONG)handle;
1183 ccw.ObjectID = (ULONGLONG)obj;
1184 ccw.TypeID = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1185 ccw.IUnk = (ULONGLONG)iUnk;
1186 ccw.RefCount = refCount;
1187 ccw.JupiterRefCount = jupiterRefCount;
1190 if (m_currCcw >= kMaxCcwCount)
1195 void BulkComLogger::FlushCcw()
1205 _ASSERTE(m_currCcw <= kMaxCcwCount);
1212 for (int i = 0; i < m_currCcw; ++i)
1213 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, m_etwCcwData[i].TypeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1216 unsigned short instance = GetClrInstanceId();
1218 #if !defined(FEATURE_PAL)
1219 EVENT_DATA_DESCRIPTOR eventData[3];
1220 EventDataDescCreate(&eventData[0], &m_currCcw, sizeof(const unsigned int));
1221 EventDataDescCreate(&eventData[1], &instance, sizeof(const unsigned short));
1222 EventDataDescCreate(&eventData[2], m_etwCcwData, sizeof(EventCCWEntry) * m_currCcw);
1224 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRootCCW, _countof(eventData), eventData);
1226 ULONG result = FireEtXplatGCBulkRootCCW(m_currCcw, instance, sizeof(EventCCWEntry) * m_currCcw, m_etwCcwData);
1227 #endif //!defined(FEATURE_PAL)
1228 result |= EventPipeWriteEventGCBulkRootCCW(m_currCcw, instance, sizeof(EventCCWEntry) * m_currCcw, m_etwCcwData);
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)
1433 result |= EventPipeWriteEventGCBulkRootStaticVar(m_count, appDomain, instance, m_used, m_buffer);
1435 _ASSERTE(result == ERROR_SUCCESS);
1441 void BulkStaticsLogger::WriteEntry(AppDomain *domain, Object **address, Object *obj, FieldDesc *fieldDesc)
1448 PRECONDITION(domain != NULL);
1449 PRECONDITION(address != NULL);
1450 PRECONDITION(obj != NULL);
1451 PRECONDITION(fieldDesc != NULL);
1455 // Each bulk statics event is for one AppDomain. If we are now inspecting a new domain,
1456 // we need to flush the built up events now.
1457 if (m_domain != domain)
1459 if (m_domain != NULL)
1460 FireBulkStaticsEvent();
1465 ULONGLONG th = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1466 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, th, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1468 // We should have at least 512 characters remaining in the buffer here.
1469 int remaining = kMaxBytesValues - m_used;
1470 _ASSERTE(kMaxBytesValues - m_used > 512);
1472 int len = EventStaticEntry::WriteEntry(m_buffer + m_used, remaining, (ULONGLONG)address,
1473 (ULONGLONG)obj, th, 0, fieldDesc);
1475 // 512 bytes was not enough buffer? This shouldn't happen, so we'll skip emitting the
1483 // When we are close to running out of buffer, emit the event.
1484 if (kMaxBytesValues - m_used < 512)
1485 FireBulkStaticsEvent();
1488 void BulkStaticsLogger::LogAllStatics()
1499 AppDomain *domain = ::GetAppDomain(); // There is only 1 AppDomain, so no iterator here.
1501 AppDomain::AssemblyIterator assemblyIter = domain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded|kIncludeExecution));
1502 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
1503 while (assemblyIter.Next(pDomainAssembly.This()))
1505 // Make sure the assembly is loaded.
1506 if (!pDomainAssembly->IsLoaded())
1509 CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetAssembly();
1510 DomainModuleIterator modIter = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
1512 while (modIter.Next())
1514 // Get the domain module from the module/appdomain pair.
1515 Module *module = modIter.GetModule();
1519 DomainFile *domainFile = module->GetDomainFile();
1520 if (domainFile == NULL)
1523 // Ensure the module has fully loaded.
1524 if (!domainFile->IsActive())
1527 DomainLocalModule *domainModule = module->GetDomainLocalModule();
1528 if (domainModule == NULL)
1531 // Now iterate all types with
1532 LookupMap<PTR_MethodTable>::Iterator mtIter = module->EnumerateTypeDefs();
1533 while (mtIter.Next())
1535 // I don't think mt can be null here, but the dac does a null check...
1536 // IsFullyLoaded should be equivalent to 'GetLoadLevel() == CLASS_LOADED'
1537 MethodTable *mt = mtIter.GetElement();
1538 if (mt == NULL || !mt->IsFullyLoaded())
1541 EEClass *cls = mt->GetClass();
1542 _ASSERTE(cls != NULL);
1544 if (cls->GetNumStaticFields() <= 0)
1547 ApproxFieldDescIterator fieldIter(mt, ApproxFieldDescIterator::STATIC_FIELDS);
1548 for (FieldDesc *field = fieldIter.Next(); field != NULL; field = fieldIter.Next())
1550 // Don't want thread local
1551 _ASSERTE(field->IsStatic());
1552 if (field->IsSpecialStatic() || field->IsEnCNew())
1555 // Static valuetype values are boxed.
1556 CorElementType fieldType = field->GetFieldType();
1557 if (fieldType != ELEMENT_TYPE_CLASS && fieldType != ELEMENT_TYPE_VALUETYPE)
1560 BYTE *base = field->GetBaseInDomainLocalModule(domainModule);
1564 Object **address = (Object**)field->GetStaticAddressHandle(base);
1566 if (address == NULL || ((obj = *address) == NULL))
1569 WriteEntry(domain, address, *address, field);
1570 } // foreach static field
1572 } // foreach domain module
1573 } // foreach domain assembly
1574 } // foreach AppDomain
1575 } // BulkStaticsLogger::LogAllStatics
1579 //---------------------------------------------------------------------------------------
1580 // BulkTypeValue / BulkTypeEventLogger: These take care of batching up types so they can
1581 // be logged via ETW in bulk
1582 //---------------------------------------------------------------------------------------
1584 BulkTypeValue::BulkTypeValue() : cTypeParameters(0)
1585 #ifdef FEATURE_REDHAWK
1586 , ullSingleTypeParameter(0)
1587 #else // FEATURE_REDHAWK
1589 #endif // FEATURE_REDHAWK
1590 , rgTypeParameters()
1592 LIMITED_METHOD_CONTRACT;
1593 ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
1596 //---------------------------------------------------------------------------------------
1598 // Clears a BulkTypeValue so it can be reused after the buffer is flushed to ETW
1601 void BulkTypeValue::Clear()
1611 ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
1612 cTypeParameters = 0;
1613 #ifdef FEATURE_REDHAWK
1614 ullSingleTypeParameter = 0;
1615 rgTypeParameters.Release();
1616 #else // FEATURE_REDHAWK
1618 rgTypeParameters.Clear();
1619 #endif // FEATURE_REDHAWK
1622 //---------------------------------------------------------------------------------------
1624 // Fire an ETW event for all the types we batched so far, and then reset our state
1625 // so we can start batching new types at the beginning of the array.
1629 void BulkTypeEventLogger::FireBulkTypeEvent()
1631 LIMITED_METHOD_CONTRACT;
1633 if (m_nBulkTypeValueCount == 0)
1635 // No types were batched up, so nothing to send
1638 UINT16 nClrInstanceID = GetClrInstanceId();
1640 if(m_pBulkTypeEventBuffer == NULL)
1642 // The buffer could not be allocated when this object was created, so bail.
1648 for (int iTypeData = 0; iTypeData < m_nBulkTypeValueCount; iTypeData++)
1650 BulkTypeValue& target = m_rgBulkTypeValues[iTypeData];
1652 // Do fixed-size data as one bulk copy
1654 m_pBulkTypeEventBuffer + iSize,
1655 &(target.fixedSizedData),
1656 sizeof(target.fixedSizedData));
1657 iSize += sizeof(target.fixedSizedData);
1659 // Do var-sized data individually per field
1661 LPCWSTR wszName = target.sName.GetUnicode();
1662 if (wszName == NULL)
1664 m_pBulkTypeEventBuffer[iSize++] = 0;
1665 m_pBulkTypeEventBuffer[iSize++] = 0;
1669 UINT nameSize = (target.sName.GetCount() + 1) * sizeof(WCHAR);
1670 memcpy(m_pBulkTypeEventBuffer + iSize, wszName, nameSize);
1674 // Type parameter count
1675 ULONG params = target.rgTypeParameters.GetCount();
1677 ULONG *ptrInt = (ULONG*)(m_pBulkTypeEventBuffer + iSize);
1681 target.cTypeParameters = params;
1683 // Type parameter array
1684 if (target.cTypeParameters > 0)
1686 memcpy(m_pBulkTypeEventBuffer + iSize, target.rgTypeParameters.GetElements(), sizeof(ULONGLONG) * target.cTypeParameters);
1687 iSize += sizeof(ULONGLONG) * target.cTypeParameters;
1691 FireEtwBulkType(m_nBulkTypeValueCount, GetClrInstanceId(), iSize, m_pBulkTypeEventBuffer);
1694 m_nBulkTypeValueCount = 0;
1695 m_nBulkTypeValueByteCount = 0;
1698 #ifndef FEATURE_REDHAWK
1700 //---------------------------------------------------------------------------------------
1702 // Batches a single type into the array, flushing the array to ETW if it fills up. Most
1703 // interaction with the type system (to analyze the type) is done here. This does not
1704 // recursively batch up any parameter types (for arrays or generics), but does add their
1705 // TypeHandles to the rgTypeParameters array. LogTypeAndParameters is responsible for
1706 // initiating any recursive calls to deal with type parameters.
1709 // th - TypeHandle to batch
1712 // Index into array of where this type got batched. -1 if there was a failure.
1715 int BulkTypeEventLogger::LogSingleType(TypeHandle th)
1722 CAN_TAKE_LOCK; // some of the type system stuff can take locks
1726 // If there's no room for another type, flush what we've got
1727 if (m_nBulkTypeValueCount == _countof(m_rgBulkTypeValues))
1729 FireBulkTypeEvent();
1732 _ASSERTE(m_nBulkTypeValueCount < (int)_countof(m_rgBulkTypeValues));
1734 if (!th.IsTypeDesc() && th.GetMethodTable()->IsArray())
1736 _ASSERTE(!"BulkTypeEventLogger::LogSingleType called with MethodTable array");
1740 BulkTypeValue * pVal = &m_rgBulkTypeValues[m_nBulkTypeValueCount];
1742 // Clear out pVal before filling it out (array elements can get reused if there
1743 // are enough types that we need to flush to multiple events). Clearing the
1744 // contained SBuffer can throw, so deal with exceptions
1745 BOOL fSucceeded = FALSE;
1755 EX_END_CATCH(RethrowCorruptingExceptions);
1759 pVal->fixedSizedData.TypeID = (ULONGLONG) th.AsTAddr();
1760 pVal->fixedSizedData.ModuleID = (ULONGLONG) (TADDR) th.GetModule();
1761 pVal->fixedSizedData.TypeNameID = (th.GetMethodTable() == NULL) ? 0 : th.GetCl();
1762 pVal->fixedSizedData.Flags = 0;
1763 pVal->fixedSizedData.CorElementType = (BYTE) th.GetInternalCorElementType();
1767 // Normal typedesc array
1768 pVal->fixedSizedData.Flags |= kEtwTypeFlagsArray;
1770 // Fetch TypeHandle of array elements
1774 pVal->rgTypeParameters.Append((ULONGLONG) th.AsArray()->GetArrayElementTypeHandle().AsTAddr());
1781 EX_END_CATCH(RethrowCorruptingExceptions);
1785 else if (th.IsTypeDesc())
1787 // Non-array Typedescs
1788 PTR_TypeDesc pTypeDesc = th.AsTypeDesc();
1789 if (pTypeDesc->HasTypeParam())
1794 pVal->rgTypeParameters.Append((ULONGLONG) pTypeDesc->GetTypeParam().AsTAddr());
1801 EX_END_CATCH(RethrowCorruptingExceptions);
1808 // Non-array MethodTable
1810 PTR_MethodTable pMT = th.AsMethodTable();
1812 // Make CorElementType more specific if this is a string MT
1813 if (pMT->IsString())
1815 pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_STRING;
1817 else if (pMT->IsObjectClass())
1819 pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_OBJECT;
1822 // Generic arguments
1823 DWORD cTypeParameters = pMT->GetNumGenericArgs();
1824 if (cTypeParameters > 0)
1826 Instantiation inst = pMT->GetInstantiation();
1830 for (DWORD i=0; i < cTypeParameters; i++)
1832 pVal->rgTypeParameters.Append((ULONGLONG) inst[i].AsTAddr());
1840 EX_END_CATCH(RethrowCorruptingExceptions);
1845 if (pMT->HasFinalizer())
1847 pVal->fixedSizedData.Flags |= kEtwTypeFlagsFinalizable;
1849 if (pMT->IsDelegate())
1851 pVal->fixedSizedData.Flags |= kEtwTypeFlagsDelegate;
1853 if (pMT->IsComObjectType())
1855 pVal->fixedSizedData.Flags |= kEtwTypeFlagsExternallyImplementedCOMObject;
1859 // If the profiler wants it, construct a name. Always normalize the string (even if
1860 // type names are not requested) so that calls to sName.GetCount() can't throw
1863 if (ETW_TRACING_CATEGORY_ENABLED(
1864 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
1865 TRACE_LEVEL_INFORMATION,
1866 CLR_GCHEAPANDTYPENAMES_KEYWORD))
1868 th.GetName(pVal->sName);
1870 pVal->sName.Normalize();
1874 // If this failed, the name remains empty, which is ok; the event just
1875 // won't have a name in it.
1876 pVal->sName.Clear();
1878 EX_END_CATCH(RethrowCorruptingExceptions);
1880 // Now that we know the full size of this type's data, see if it fits in our
1881 // batch or whether we need to flush
1883 int cbVal = pVal->GetByteCountInEvent();
1884 if (cbVal > kMaxBytesTypeValues)
1886 // This type is apparently so huge, it's too big to squeeze into an event, even
1887 // if it were the only type batched in the whole event. Bail
1888 _ASSERTE(!"Type too big to log via ETW");
1892 if (m_nBulkTypeValueByteCount + cbVal > kMaxBytesTypeValues)
1894 // Although this type fits into the array, its size is so big that the entire
1895 // array can't be logged via ETW. So flush the array, and start over by
1896 // calling ourselves--this refetches the type info and puts it at the
1897 // beginning of the array. Since we know this type is small enough to be
1898 // batched into an event on its own, this recursive call will not try to
1899 // call itself again.
1900 FireBulkTypeEvent();
1901 return LogSingleType(th);
1904 // The type fits into the batch, so update our state
1905 m_nBulkTypeValueCount++;
1906 m_nBulkTypeValueByteCount += cbVal;
1907 return m_nBulkTypeValueCount - 1; // Index of type we just added
1910 //---------------------------------------------------------------------------------------
1912 // High-level method to batch a type and (recursively) its type parameters, flushing to
1913 // ETW as needed. This is called by (static)
1914 // ETW::TypeSystemLog::LogTypeAndParametersIfNecessary, which is what clients use to log
1918 // * thAsAddr - Type to batch
1919 // * typeLogBehavior - Reminder of whether the type system log lock is held
1920 // (useful if we need to recurively call back into TypeSystemLog), and whether
1921 // we even care to check if the type was already logged
1924 void BulkTypeEventLogger::LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior)
1931 CAN_TAKE_LOCK; // LogSingleType can take locks
1935 TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr);
1937 // Batch up this type. This grabs useful info about the type, including any
1938 // type parameters it may have, and sticks it in m_rgBulkTypeValues
1939 int iBulkTypeEventData = LogSingleType(th);
1940 if (iBulkTypeEventData == -1)
1942 // There was a failure trying to log the type, so don't bother with its type
1947 // Look at the type info we just batched, so we can get the type parameters
1948 BulkTypeValue * pVal = &m_rgBulkTypeValues[iBulkTypeEventData];
1950 // We're about to recursively call ourselves for the type parameters, so make a
1951 // local copy of their type handles first (else, as we log them we could flush
1952 // and clear out m_rgBulkTypeValues, thus trashing pVal)
1954 StackSArray<ULONGLONG> rgTypeParameters;
1955 DWORD cParams = pVal->rgTypeParameters.GetCount();
1957 BOOL fSucceeded = FALSE;
1960 for (COUNT_T i = 0; i < cParams; i++)
1962 rgTypeParameters.Append(pVal->rgTypeParameters[i]);
1970 EX_END_CATCH(RethrowCorruptingExceptions);
1974 // Before we recurse, adjust the special-cased type-log behavior that allows a
1975 // top-level type to be logged without lookup, but still requires lookups to avoid
1976 // dupes of type parameters
1977 if (typeLogBehavior == ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLogTopLevelType)
1978 typeLogBehavior = ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime;
1980 // Recursively log any referenced parameter types
1981 for (COUNT_T i=0; i < cParams; i++)
1983 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(this, rgTypeParameters[i], typeLogBehavior);
1987 #endif // FEATURE_REDHAWK
1989 // Holds state that batches of roots, nodes, edges, and types as the GC walks the heap
1990 // at the end of a collection.
1991 class EtwGcHeapDumpContext
1994 // An instance of EtwGcHeapDumpContext is dynamically allocated and stored inside of
1995 // ProfilingScanContext and ProfilerWalkHeapContext, which are context structures
1996 // that the GC heap walker sends back to the callbacks. This method is passed a
1997 // pointer to ProfilingScanContext::pvEtwContext or
1998 // ProfilerWalkHeapContext::pvEtwContext; if non-NULL it gets returned; else, a new
1999 // EtwGcHeapDumpContext is allocated, stored in that pointer, and then returned.
2000 // Callers should test for NULL, which can be returned if out of memory
2001 static EtwGcHeapDumpContext * GetOrCreateInGCContext(LPVOID * ppvEtwContext)
2003 LIMITED_METHOD_CONTRACT;
2005 _ASSERTE(ppvEtwContext != NULL);
2007 EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) *ppvEtwContext;
2008 if (pContext == NULL)
2010 pContext = new (nothrow) EtwGcHeapDumpContext;
2011 *ppvEtwContext = pContext;
2016 EtwGcHeapDumpContext() :
2017 iCurBulkRootEdge(0),
2018 iCurBulkRootConditionalWeakTableElementEdge(0),
2019 iCurBulkNodeEvent(0),
2020 iCurBulkEdgeEvent(0),
2021 bulkTypeEventLogger()
2023 LIMITED_METHOD_CONTRACT;
2025 ClearRootConditionalWeakTableElementEdges();
2030 // These helpers clear the individual buffers, for use after a flush and on
2031 // construction. They intentionally leave the indices (iCur*) alone, since they
2032 // persist across flushes within a GC
2034 void ClearRootEdges()
2036 LIMITED_METHOD_CONTRACT;
2037 cGcBulkRootEdges = 0;
2038 ZeroMemory(rgGcBulkRootEdges, sizeof(rgGcBulkRootEdges));
2041 void ClearRootConditionalWeakTableElementEdges()
2043 LIMITED_METHOD_CONTRACT;
2044 cGCBulkRootConditionalWeakTableElementEdges = 0;
2045 ZeroMemory(rgGCBulkRootConditionalWeakTableElementEdges, sizeof(rgGCBulkRootConditionalWeakTableElementEdges));
2050 LIMITED_METHOD_CONTRACT;
2051 cGcBulkNodeValues = 0;
2052 ZeroMemory(rgGcBulkNodeValues, sizeof(rgGcBulkNodeValues));
2057 LIMITED_METHOD_CONTRACT;
2058 cGcBulkEdgeValues = 0;
2059 ZeroMemory(rgGcBulkEdgeValues, sizeof(rgGcBulkEdgeValues));
2062 //---------------------------------------------------------------------------------------
2065 // A "root edge" is the relationship between a source "GCRootID" (i.e., stack
2066 // variable, handle, static, etc.) and the target "RootedNodeAddress" (the managed
2067 // object that gets rooted).
2069 //---------------------------------------------------------------------------------------
2071 // Sequence number for each GCBulkRootEdge event
2072 UINT iCurBulkRootEdge;
2074 // Number of root edges currently filled out in rgGcBulkRootEdges array
2075 UINT cGcBulkRootEdges;
2077 // Struct array containing the primary data for each GCBulkRootEdge event. Fix the size so
2078 // the total event stays well below the 64K
2079 // limit (leaving lots of room for non-struct fields that come before the root edge data)
2080 EventStructGCBulkRootEdgeValue rgGcBulkRootEdges[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootEdgeValue)];
2083 //---------------------------------------------------------------------------------------
2084 // GCBulkRootConditionalWeakTableElementEdge
2086 // These describe dependent handles, which simulate an edge connecting a key NodeID
2087 // to a value NodeID.
2089 //---------------------------------------------------------------------------------------
2091 // Sequence number for each GCBulkRootConditionalWeakTableElementEdge event
2092 UINT iCurBulkRootConditionalWeakTableElementEdge;
2094 // Number of root edges currently filled out in rgGCBulkRootConditionalWeakTableElementEdges array
2095 UINT cGCBulkRootConditionalWeakTableElementEdges;
2097 // Struct array containing the primary data for each GCBulkRootConditionalWeakTableElementEdge event. Fix the size so
2098 // the total event stays well below the 64K
2099 // limit (leaving lots of room for non-struct fields that come before the root edge data)
2100 EventStructGCBulkRootConditionalWeakTableElementEdgeValue rgGCBulkRootConditionalWeakTableElementEdges
2101 [(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootConditionalWeakTableElementEdgeValue)];
2103 //---------------------------------------------------------------------------------------
2106 // A "node" is ANY managed object sitting on the heap, including RootedNodeAddresses
2107 // as well as leaf nodes.
2109 //---------------------------------------------------------------------------------------
2111 // Sequence number for each GCBulkNode event
2112 UINT iCurBulkNodeEvent;
2114 // Number of nodes currently filled out in rgGcBulkNodeValues array
2115 UINT cGcBulkNodeValues;
2117 // Struct array containing the primary data for each GCBulkNode event. Fix the size so
2118 // the total event stays well below the 64K
2119 // limit (leaving lots of room for non-struct fields that come before the node data)
2120 EventStructGCBulkNodeValue rgGcBulkNodeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkNodeValue)];
2122 //---------------------------------------------------------------------------------------
2125 // An "edge" is the relationship between a source node and its referenced target
2126 // node. Edges are reported in bulk, separately from Nodes, but it is expected that
2127 // the consumer read the Node and Edge streams together. One takes the first node
2128 // from the Node stream, and then reads EdgeCount entries in the Edge stream, telling
2129 // you all of that Node's targets. Then, one takes the next node in the Node stream,
2130 // and reads the next entries in the Edge stream (using this Node's EdgeCount to
2131 // determine how many) to find all of its targets. This continues on until the Node
2132 // and Edge streams have been fully read.
2134 // GCBulkRootEdges are not duplicated in the GCBulkEdge events. GCBulkEdge events
2135 // begin at the GCBulkRootEdge.RootedNodeAddress and move forward.
2137 //---------------------------------------------------------------------------------------
2139 // Sequence number for each GCBulkEdge event
2140 UINT iCurBulkEdgeEvent;
2142 // Number of nodes currently filled out in rgGcBulkEdgeValues array
2143 UINT cGcBulkEdgeValues;
2145 // Struct array containing the primary data for each GCBulkEdge event. Fix the size so
2146 // the total event stays well below the 64K
2147 // limit (leaving lots of room for non-struct fields that come before the edge data)
2148 EventStructGCBulkEdgeValue rgGcBulkEdgeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkEdgeValue)];
2151 //---------------------------------------------------------------------------------------
2154 // Types are a bit more complicated to batch up, since their data is of varying
2155 // size. BulkTypeEventLogger takes care of the pesky details for us
2156 //---------------------------------------------------------------------------------------
2158 BulkTypeEventLogger bulkTypeEventLogger;
2163 //---------------------------------------------------------------------------------------
2165 // Called during a heap walk for each root reference encountered. Batches up the root in
2169 // * pvHandle - If the root is a handle, this points to the handle
2170 // * pRootedNode - Points to object that is rooted
2171 // * pSecondaryNodeForDependentHandle - For dependent handles, this is the
2173 // * fDependentHandle - nonzero iff this is for a dependent handle
2174 // * profilingScanContext - The shared profapi/etw context built up during the heap walk.
2175 // * dwGCFlags - Bitmask of "GC_"-style flags set by GC
2176 // * rootFlags - Bitmask of EtwGCRootFlags describing the root
2180 VOID ETW::GCLog::RootReference(
2182 Object * pRootedNode,
2183 Object * pSecondaryNodeForDependentHandle,
2184 BOOL fDependentHandle,
2185 ProfilingScanContext * profilingScanContext,
2189 LIMITED_METHOD_CONTRACT;
2191 EtwGcHeapDumpContext * pContext =
2192 EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilingScanContext->pvEtwContext);
2193 if (pContext == NULL)
2196 // Determine root kind, root ID, and handle-specific flags
2197 LPVOID pvRootID = NULL;
2198 BYTE nRootKind = (BYTE) profilingScanContext->dwEtwRootKind;
2201 case kEtwGCRootKindStack:
2202 #if !defined (FEATURE_REDHAWK) && (defined(GC_PROFILING) || defined (DACCESS_COMPILE))
2203 pvRootID = profilingScanContext->pMD;
2204 #endif // !defined (FEATURE_REDHAWK) && (defined(GC_PROFILING) || defined (DACCESS_COMPILE))
2207 case kEtwGCRootKindHandle:
2208 pvRootID = pvHandle;
2211 case kEtwGCRootKindFinalizer:
2212 _ASSERTE(pvRootID == NULL);
2215 case kEtwGCRootKindOther:
2217 _ASSERTE(nRootKind == kEtwGCRootKindOther);
2218 _ASSERTE(pvRootID == NULL);
2222 // Convert GC root flags to ETW root flags
2223 if (dwGCFlags & GC_CALL_INTERIOR)
2224 rootFlags |= kEtwGCRootFlagsInterior;
2225 if (dwGCFlags & GC_CALL_PINNED)
2226 rootFlags |= kEtwGCRootFlagsPinning;
2228 // Add root edge to appropriate buffer
2229 if (fDependentHandle)
2231 _ASSERTE(pContext->cGCBulkRootConditionalWeakTableElementEdges <
2232 _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges));
2233 EventStructGCBulkRootConditionalWeakTableElementEdgeValue * pRCWTEEdgeValue =
2234 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[pContext->cGCBulkRootConditionalWeakTableElementEdges];
2235 pRCWTEEdgeValue->GCKeyNodeID = pRootedNode;
2236 pRCWTEEdgeValue->GCValueNodeID = pSecondaryNodeForDependentHandle;
2237 pRCWTEEdgeValue->GCRootID = pvRootID;
2238 pContext->cGCBulkRootConditionalWeakTableElementEdges++;
2240 // If RCWTE edge buffer is now full, empty it into ETW
2241 if (pContext->cGCBulkRootConditionalWeakTableElementEdges ==
2242 _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges))
2244 FireEtwGCBulkRootConditionalWeakTableElementEdge(
2245 pContext->iCurBulkRootConditionalWeakTableElementEdge,
2246 pContext->cGCBulkRootConditionalWeakTableElementEdges,
2248 sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]),
2249 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]);
2251 pContext->iCurBulkRootConditionalWeakTableElementEdge++;
2252 pContext->ClearRootConditionalWeakTableElementEdges();
2257 _ASSERTE(pContext->cGcBulkRootEdges < _countof(pContext->rgGcBulkRootEdges));
2258 EventStructGCBulkRootEdgeValue * pBulkRootEdgeValue = &pContext->rgGcBulkRootEdges[pContext->cGcBulkRootEdges];
2259 pBulkRootEdgeValue->RootedNodeAddress = pRootedNode;
2260 pBulkRootEdgeValue->GCRootKind = nRootKind;
2261 pBulkRootEdgeValue->GCRootFlag = rootFlags;
2262 pBulkRootEdgeValue->GCRootID = pvRootID;
2263 pContext->cGcBulkRootEdges++;
2265 // If root edge buffer is now full, empty it into ETW
2266 if (pContext->cGcBulkRootEdges == _countof(pContext->rgGcBulkRootEdges))
2268 FireEtwGCBulkRootEdge(
2269 pContext->iCurBulkRootEdge,
2270 pContext->cGcBulkRootEdges,
2272 sizeof(pContext->rgGcBulkRootEdges[0]),
2273 &pContext->rgGcBulkRootEdges[0]);
2275 pContext->iCurBulkRootEdge++;
2276 pContext->ClearRootEdges();
2281 //---------------------------------------------------------------------------------------
2283 // Called during a heap walk for each object reference encountered. Batches up the
2284 // corresponding node, edges, and type data for the ETW events.
2287 // * profilerWalkHeapContext - The shared profapi/etw context built up during the heap walk.
2288 // * pObjReferenceSource - Object doing the pointing
2289 // * typeID - Type of pObjReferenceSource
2290 // * fDependentHandle - nonzero iff this is for a dependent handle
2291 // * cRefs - Count of objects being pointed to
2292 // * rgObjReferenceTargets - Array of objects being pointed to
2296 VOID ETW::GCLog::ObjectReference(
2297 ProfilerWalkHeapContext * profilerWalkHeapContext,
2298 Object * pObjReferenceSource,
2301 Object ** rgObjReferenceTargets)
2309 // LogTypeAndParametersIfNecessary can take a lock
2314 EtwGcHeapDumpContext * pContext =
2315 EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilerWalkHeapContext->pvEtwContext);
2316 if (pContext == NULL)
2319 //---------------------------------------------------------------------------------------
2320 // GCBulkNode events
2321 //---------------------------------------------------------------------------------------
2323 // Add Node (pObjReferenceSource) to buffer
2324 _ASSERTE(pContext->cGcBulkNodeValues < _countof(pContext->rgGcBulkNodeValues));
2325 EventStructGCBulkNodeValue * pBulkNodeValue = &pContext->rgGcBulkNodeValues[pContext->cGcBulkNodeValues];
2326 pBulkNodeValue->Address = pObjReferenceSource;
2327 pBulkNodeValue->Size = pObjReferenceSource->GetSize();
2328 pBulkNodeValue->TypeID = typeID;
2329 pBulkNodeValue->EdgeCount = cRefs;
2330 pContext->cGcBulkNodeValues++;
2332 // If Node buffer is now full, empty it into ETW
2333 if (pContext->cGcBulkNodeValues == _countof(pContext->rgGcBulkNodeValues))
2336 pContext->iCurBulkNodeEvent,
2337 pContext->cGcBulkNodeValues,
2339 sizeof(pContext->rgGcBulkNodeValues[0]),
2340 &pContext->rgGcBulkNodeValues[0]);
2342 pContext->iCurBulkNodeEvent++;
2343 pContext->ClearNodes();
2346 //---------------------------------------------------------------------------------------
2348 //---------------------------------------------------------------------------------------
2350 // We send type information as necessary--only for nodes, and only for nodes that we
2351 // haven't already sent type info for
2354 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(
2355 &pContext->bulkTypeEventLogger, // Batch up this type with others to minimize events
2358 // During heap walk, GC holds the lock for us, so we can directly enter the
2359 // hash to see if the type has already been logged
2360 ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime
2364 //---------------------------------------------------------------------------------------
2365 // GCBulkEdge events
2366 //---------------------------------------------------------------------------------------
2368 // Add Edges (rgObjReferenceTargets) to buffer. Buffer could fill up before all edges
2369 // are added (it could even fill up multiple times during this one call if there are
2370 // a lot of edges), so empty Edge buffer into ETW as we go along, as many times as we
2373 for (ULONGLONG i=0; i < cRefs; i++)
2375 _ASSERTE(pContext->cGcBulkEdgeValues < _countof(pContext->rgGcBulkEdgeValues));
2376 EventStructGCBulkEdgeValue * pBulkEdgeValue = &pContext->rgGcBulkEdgeValues[pContext->cGcBulkEdgeValues];
2377 pBulkEdgeValue->Value = rgObjReferenceTargets[i];
2378 // FUTURE: ReferencingFieldID
2379 pBulkEdgeValue->ReferencingFieldID = 0;
2380 pContext->cGcBulkEdgeValues++;
2382 // If Edge buffer is now full, empty it into ETW
2383 if (pContext->cGcBulkEdgeValues == _countof(pContext->rgGcBulkEdgeValues))
2386 pContext->iCurBulkEdgeEvent,
2387 pContext->cGcBulkEdgeValues,
2389 sizeof(pContext->rgGcBulkEdgeValues[0]),
2390 &pContext->rgGcBulkEdgeValues[0]);
2392 pContext->iCurBulkEdgeEvent++;
2393 pContext->ClearEdges();
2398 //---------------------------------------------------------------------------------------
2400 // Called by GC at end of heap dump to give us a convenient time to flush any remaining
2401 // buffers of data to ETW
2404 // profilerWalkHeapContext - Context containing data we've batched up
2408 VOID ETW::GCLog::EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext)
2410 LIMITED_METHOD_CONTRACT;
2412 // If context isn't already set up for us, then we haven't been collecting any data
2414 EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) profilerWalkHeapContext->pvEtwContext;
2415 if (pContext == NULL)
2418 // If the GC events are enabled, flush any remaining root, node, and / or edge data
2419 if (s_forcedGCInProgress &&
2420 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
2421 TRACE_LEVEL_INFORMATION,
2422 CLR_GCHEAPDUMP_KEYWORD))
2424 if (pContext->cGcBulkRootEdges > 0)
2426 FireEtwGCBulkRootEdge(
2427 pContext->iCurBulkRootEdge,
2428 pContext->cGcBulkRootEdges,
2430 sizeof(pContext->rgGcBulkRootEdges[0]),
2431 &pContext->rgGcBulkRootEdges[0]);
2434 if (pContext->cGCBulkRootConditionalWeakTableElementEdges > 0)
2436 FireEtwGCBulkRootConditionalWeakTableElementEdge(
2437 pContext->iCurBulkRootConditionalWeakTableElementEdge,
2438 pContext->cGCBulkRootConditionalWeakTableElementEdges,
2440 sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]),
2441 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]);
2444 if (pContext->cGcBulkNodeValues > 0)
2447 pContext->iCurBulkNodeEvent,
2448 pContext->cGcBulkNodeValues,
2450 sizeof(pContext->rgGcBulkNodeValues[0]),
2451 &pContext->rgGcBulkNodeValues[0]);
2454 if (pContext->cGcBulkEdgeValues > 0)
2457 pContext->iCurBulkEdgeEvent,
2458 pContext->cGcBulkEdgeValues,
2460 sizeof(pContext->rgGcBulkEdgeValues[0]),
2461 &pContext->rgGcBulkEdgeValues[0]);
2465 // Ditto for type events
2466 if (ETW_TRACING_CATEGORY_ENABLED(
2467 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
2468 TRACE_LEVEL_INFORMATION,
2471 pContext->bulkTypeEventLogger.FireBulkTypeEvent();
2474 // Delete any GC state built up in the context
2475 profilerWalkHeapContext->pvEtwContext = NULL;
2480 //---------------------------------------------------------------------------------------
2482 // Helper to send public finalize object & type events, and private finalize object
2483 // event. If Type events are enabled, this will send the Type event for the finalized
2484 // objects. It will not be batched with other types (except type parameters, if any),
2485 // and will not check if the Type has already been logged (may thus result in dupe
2486 // logging of the Type).
2489 // pMT - MT of object getting finalized
2490 // pObj - object getting finalized
2494 VOID ETW::GCLog::SendFinalizeObjectEvent(MethodTable * pMT, Object * pObj)
2502 // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to
2507 // Send public finalize object event, if it's enabled
2508 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, FinalizeObject))
2510 FireEtwFinalizeObject(pMT, pObj, GetClrInstanceId());
2512 // This function checks if type events are enabled; if so, it sends event for
2513 // finalized object's type (and parameter types, if any)
2514 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(
2515 NULL, // Not batching this type with others
2518 // Don't spend the time entering the lock and checking the hash table to see
2519 // if we've already logged the type; just log it (if type events are enabled).
2520 ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog
2524 // Send private finalize object event, if it's enabled
2525 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, PrvFinalizeObject))
2529 DefineFullyQualifiedNameForClassWOnStack();
2530 FireEtwPrvFinalizeObject(pMT, pObj, GetClrInstanceId(), GetFullyQualifiedNameForClassNestedAwareW(pMT));
2535 EX_END_CATCH(RethrowCorruptingExceptions);
2540 DWORD ETW::ThreadLog::GetEtwThreadFlags(Thread * pThread)
2542 LIMITED_METHOD_CONTRACT;
2544 DWORD dwEtwThreadFlags = 0;
2546 if (pThread->IsThreadPoolThread())
2548 dwEtwThreadFlags |= kEtwThreadFlagThreadPoolWorker;
2550 if (pThread->IsGCSpecial())
2552 dwEtwThreadFlags |= kEtwThreadFlagGCSpecial;
2554 if (IsGarbageCollectorFullyInitialized() &&
2555 (pThread == FinalizerThread::GetFinalizerThread()))
2557 dwEtwThreadFlags |= kEtwThreadFlagFinalizer;
2560 return dwEtwThreadFlags;
2563 VOID ETW::ThreadLog::FireThreadCreated(Thread * pThread)
2565 LIMITED_METHOD_CONTRACT;
2567 FireEtwThreadCreated(
2569 (ULONGLONG)pThread->GetDomain(),
2570 GetEtwThreadFlags(pThread),
2571 pThread->GetThreadId(),
2572 pThread->GetOSThreadId(),
2573 GetClrInstanceId());
2576 VOID ETW::ThreadLog::FireThreadDC(Thread * pThread)
2578 LIMITED_METHOD_CONTRACT;
2582 (ULONGLONG)pThread->GetDomain(),
2583 GetEtwThreadFlags(pThread),
2584 pThread->GetThreadId(),
2585 pThread->GetOSThreadId(),
2586 GetClrInstanceId());
2591 #ifndef FEATURE_REDHAWK
2593 // TypeSystemLog implementation
2595 // We keep track of which TypeHandles have been logged, and stats on instances of these
2596 // TypeHandles that have been allocated, by a hash table of hash tables. The outer hash
2597 // table maps Module*'s to an inner hash table that contains all the TypeLoggingInfos for that
2598 // Module*. Arranging things this way makes it easy to deal with Module unloads, as we
2599 // can simply remove the corresponding inner hash table from the outer hash table.
2601 // The following help define the "inner" hash table: a hash table of TypeLoggingInfos
2602 // from a particular Module (key = TypeHandle, value = TypeLoggingInfo.
2604 class LoggedTypesFromModuleTraits : public NoRemoveSHashTraits< DefaultSHashTraits<ETW::TypeLoggingInfo> >
2608 // explicitly declare local typedefs for these traits types, otherwise
2609 // the compiler may get confused
2610 typedef NoRemoveSHashTraits< DefaultSHashTraits<ETW::TypeLoggingInfo> > PARENT;
2611 typedef PARENT::element_t element_t;
2612 typedef PARENT::count_t count_t;
2614 typedef TypeHandle key_t;
2616 static key_t GetKey(const element_t &e)
2618 LIMITED_METHOD_CONTRACT;
2622 static BOOL Equals(key_t k1, key_t k2)
2624 LIMITED_METHOD_CONTRACT;
2628 static count_t Hash(key_t k)
2630 LIMITED_METHOD_CONTRACT;
2631 return (count_t) k.AsTAddr();
2634 static bool IsNull(const element_t &e)
2636 LIMITED_METHOD_CONTRACT;
2637 return (e.th.AsTAddr() == NULL);
2640 static const element_t Null()
2642 LIMITED_METHOD_CONTRACT;
2643 return ETW::TypeLoggingInfo(NULL);
2646 typedef SHash<LoggedTypesFromModuleTraits> LoggedTypesFromModuleHash;
2648 // The inner hash table is housed inside this class, which acts as an entry in the outer
2650 class ETW::LoggedTypesFromModule
2654 LoggedTypesFromModuleHash loggedTypesFromModuleHash;
2656 // These are used by the outer hash table (mapping Module*'s to instances of
2657 // LoggedTypesFromModule).
2658 static COUNT_T Hash(Module * pModule)
2660 LIMITED_METHOD_CONTRACT;
2661 return (COUNT_T) (SIZE_T) pModule;
2665 LIMITED_METHOD_CONTRACT;
2669 LoggedTypesFromModule(Module * pModuleParam) : loggedTypesFromModuleHash()
2671 LIMITED_METHOD_CONTRACT;
2672 pModule = pModuleParam;
2675 ~LoggedTypesFromModule()
2677 LIMITED_METHOD_CONTRACT;
2681 // The following define the outer hash table (mapping Module*'s to instances of
2682 // LoggedTypesFromModule).
2684 class AllLoggedTypesTraits : public DefaultSHashTraits<ETW::LoggedTypesFromModule *>
2688 // explicitly declare local typedefs for these traits types, otherwise
2689 // the compiler may get confused
2690 typedef DefaultSHashTraits<ETW::LoggedTypesFromModule *> PARENT;
2691 typedef PARENT::element_t element_t;
2692 typedef PARENT::count_t count_t;
2694 typedef Module * key_t;
2696 static key_t GetKey(const element_t &e)
2698 LIMITED_METHOD_CONTRACT;
2702 static BOOL Equals(key_t k1, key_t k2)
2704 LIMITED_METHOD_CONTRACT;
2708 static count_t Hash(key_t k)
2710 LIMITED_METHOD_CONTRACT;
2711 return (count_t) (size_t) k;
2714 static bool IsNull(const element_t &e)
2716 LIMITED_METHOD_CONTRACT;
2720 static element_t Null()
2722 LIMITED_METHOD_CONTRACT;
2727 typedef SHash<AllLoggedTypesTraits> AllLoggedTypesHash;
2729 // The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule) is
2730 // housed in this struct, which is dynamically allocated the first time we decide we need
2732 struct AllLoggedTypes
2735 // This Crst protects the entire outer & inner hash tables. On a GC heap walk, it
2736 // is entered once for the duration of the walk, so that we can freely access the
2737 // hash tables during the walk. On each object allocation, this Crst must be
2738 // entered individually each time.
2739 static CrstStatic s_cs;
2741 // A thread local copy of the global epoch.
2742 // This value is used by each thread to ensure that the thread local data structures
2743 // are in sync with the global state.
2744 unsigned int nEpoch;
2746 // The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule)
2747 AllLoggedTypesHash allLoggedTypesHash;
2751 CrstStatic AllLoggedTypes::s_cs;
2752 AllLoggedTypes * ETW::TypeSystemLog::s_pAllLoggedTypes = NULL;
2753 unsigned int ETW::TypeSystemLog::s_nEpoch = 0;
2754 BOOL ETW::TypeSystemLog::s_fHeapAllocEventEnabledOnStartup = FALSE;
2755 BOOL ETW::TypeSystemLog::s_fHeapAllocHighEventEnabledNow = FALSE;
2756 BOOL ETW::TypeSystemLog::s_fHeapAllocLowEventEnabledNow = FALSE;
2757 int ETW::TypeSystemLog::s_nCustomMsBetweenEvents = 0;
2760 //---------------------------------------------------------------------------------------
2762 // Initializes TypeSystemLog (specifically its crst). Called just before ETW providers
2763 // are registered with the OS
2766 // HRESULT indicating success or failure
2770 HRESULT ETW::TypeSystemLog::PreRegistrationInit()
2772 LIMITED_METHOD_CONTRACT;
2774 if (!AllLoggedTypes::s_cs.InitNoThrow(
2776 CRST_UNSAFE_ANYMODE)) // This lock is taken during a GC while walking the heap
2784 //---------------------------------------------------------------------------------------
2786 // Initializes TypeSystemLog (specifically its crst). Called just after ETW providers
2787 // are registered with the OS
2790 // HRESULT indicating success or failure
2794 void ETW::TypeSystemLog::PostRegistrationInit()
2796 LIMITED_METHOD_CONTRACT;
2798 // Initialize our "current state" BOOLs that remember if low or high allocation
2799 // sampling is turned on
2800 s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD);
2801 s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD);
2803 // Snapshot the current state of the object allocated keyword (on startup), and rely
2804 // on this snapshot for the rest of the process run. Since these events require the
2805 // slow alloc JIT helper to be enabled, and that can only be done on startup, we
2806 // remember in this BOOL that we did so, so that we can prevent the object allocated
2807 // event from being fired if the fast allocation helper were enabled but had to
2808 // degrade down to the slow helper (e.g., thread ran over its allocation limit). This
2809 // keeps things consistent.
2810 s_fHeapAllocEventEnabledOnStartup = (s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow);
2812 if (s_fHeapAllocEventEnabledOnStartup)
2814 // Determine if a COMPLUS env var is overriding the frequency for the sampled
2815 // object allocated events
2817 // Config value intentionally typed as string, b/c DWORD intepretation is hard-coded
2818 // to hex, which is not what the user would expect. This way I can force the
2819 // conversion to use decimal.
2820 NewArrayHolder<WCHAR> wszCustomObjectAllocationEventsPerTypePerSec(NULL);
2821 if (FAILED(CLRConfig::GetConfigValue(
2822 CLRConfig::UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec,
2823 &wszCustomObjectAllocationEventsPerTypePerSec)) ||
2824 (wszCustomObjectAllocationEventsPerTypePerSec == NULL))
2829 DWORD dwCustomObjectAllocationEventsPerTypePerSec = wcstoul(
2830 wszCustomObjectAllocationEventsPerTypePerSec,
2832 10 // Base 10 conversion
2835 if (dwCustomObjectAllocationEventsPerTypePerSec == ULONG_MAX)
2836 dwCustomObjectAllocationEventsPerTypePerSec = 0;
2837 if (dwCustomObjectAllocationEventsPerTypePerSec != 0)
2839 // MsBetweenEvents = (1000 ms/sec) / (custom desired events/sec)
2840 s_nCustomMsBetweenEvents = 1000 / dwCustomObjectAllocationEventsPerTypePerSec;
2846 //---------------------------------------------------------------------------------------
2848 // Update object allocation sampling frequency and / or Type hash table contents based
2849 // on what keywords were changed.
2853 void ETW::TypeSystemLog::OnKeywordsChanged()
2855 LIMITED_METHOD_CONTRACT;
2857 // If the desired frequencey for the GCSampledObjectAllocation events has changed,
2858 // update our state.
2859 s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD);
2860 s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD);
2862 // FUTURE: Would be nice here to log an error event if (s_fHeapAllocLowEventEnabledNow ||
2863 // s_fHeapAllocHighEventEnabledNow), but !s_fHeapAllocEventEnabledOnStartup
2865 // If the type events should be turned off, eliminate the hash tables that tracked
2866 // which types were logged. (If type events are turned back on later, we'll re-log
2867 // them all as we encounter them.) Note that all we can really test for is that the
2868 // Types keyword on the runtime provider is off. Not necessarily that it was on and
2869 // was just turned off with this request. But either way, TypeSystemLog can handle it
2870 // because it is extremely smart.
2871 if (!ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, CLR_TYPE_KEYWORD))
2872 OnTypesKeywordTurnedOff();
2876 //---------------------------------------------------------------------------------------
2878 // Based on keywords alone, determine the what the default sampling rate should be for
2879 // object allocation events. (This function does not consider any COMPLUS overrides for
2880 // the sampling rate.)
2884 int ETW::TypeSystemLog::GetDefaultMsBetweenEvents()
2886 LIMITED_METHOD_CONTRACT;
2888 // We should only get here if the allocation event is enabled. In spirit, this assert
2889 // is correct, but a race could cause the assert to fire (if someone toggled the
2890 // event off after we decided that the event was on and we started down the path of
2891 // calculating statistics to fire the event). In such a case we'll end up returning
2892 // k_nDefaultMsBetweenEventsLow below, but next time we won't get here as we'll know
2893 // early enough not to fire the event.
2894 //_ASSERTE(IsHeapAllocEventEnabled());
2896 // MsBetweenEvents = (1000 ms/sec) / (desired events/sec)
2897 const int k_nDefaultMsBetweenEventsHigh = 1000 / 100; // 100 events per type per sec
2898 const int k_nDefaultMsBetweenEventsLow = 1000 / 5; // 5 events per type per sec
2900 // If both are set, High takes precedence
2901 if (s_fHeapAllocHighEventEnabledNow)
2903 return k_nDefaultMsBetweenEventsHigh;
2905 return k_nDefaultMsBetweenEventsLow;
2908 //---------------------------------------------------------------------------------------
2910 // Use this to decide whether to fire the object allocation event
2913 // nonzero iff we should fire the event.
2917 BOOL ETW::TypeSystemLog::IsHeapAllocEventEnabled()
2919 LIMITED_METHOD_CONTRACT;
2922 // Only fire the event if it was enabled at startup (and thus the slow-JIT new
2923 // helper is used in all cases)
2924 s_fHeapAllocEventEnabledOnStartup &&
2926 // AND a keyword is still enabled. (Thus people can turn off the event
2927 // whenever they want; but they cannot turn it on unless it was also on at startup.)
2928 (s_fHeapAllocHighEventEnabledNow || s_fHeapAllocLowEventEnabledNow);
2931 //---------------------------------------------------------------------------------------
2933 // Helper that adds (or updates) the TypeLoggingInfo inside the inner hash table passed
2937 // * pLoggedTypesFromModule - Inner hash table to update
2938 // * pTypeLoggingInfo - TypeLoggingInfo to store
2941 // nonzero iff the add/replace was successful.
2945 BOOL ETW::TypeSystemLog::AddOrReplaceTypeLoggingInfo(ETW::LoggedTypesFromModule * pLoggedTypesFromModule, const ETW::TypeLoggingInfo * pTypeLoggingInfo)
2947 LIMITED_METHOD_CONTRACT;
2949 _ASSERTE(pLoggedTypesFromModule != NULL);
2951 BOOL fSucceeded = FALSE;
2954 pLoggedTypesFromModule->loggedTypesFromModuleHash.AddOrReplace(*pTypeLoggingInfo);
2961 EX_END_CATCH(RethrowCorruptingExceptions);
2966 //---------------------------------------------------------------------------------------
2968 // Records stats about the object's allocation, and determines based on those stats whether
2969 // to fires the high / low frequency GCSampledObjectAllocation ETW event
2972 // * pObject - Allocated object to log
2973 // * th - TypeHandle for the object
2977 void ETW::TypeSystemLog::SendObjectAllocatedEvent(Object * pObject)
2987 // No-op if the appropriate keywords were not enabled on startup (or we're not yet
2989 if (!s_fHeapAllocEventEnabledOnStartup || !g_fEEStarted)
2992 TypeHandle th = pObject->GetTypeHandle();
2994 SIZE_T size = pObject->GetSize();
2995 if (size < MIN_OBJECT_SIZE)
2997 size = PtrAlign(size);
3000 SIZE_T nTotalSizeForTypeSample = size;
3001 DWORD dwTickNow = GetTickCount();
3002 DWORD dwObjectCountForTypeSample = 0;
3004 // Get stats for type
3005 TypeLoggingInfo typeLoggingInfo(NULL);
3006 LoggedTypesFromModule * pLoggedTypesFromModule = NULL;
3007 BOOL fCreatedNew = FALSE;
3008 typeLoggingInfo = LookupOrCreateTypeLoggingInfo(th, &fCreatedNew, &pLoggedTypesFromModule);
3009 if (typeLoggingInfo.th.IsNull())
3012 // Update stats with current allocation
3013 typeLoggingInfo.dwAllocsSkippedForSample++;
3014 typeLoggingInfo.cbIgnoredSizeForSample += size;
3016 // If both the high and low verbosity keywords are enabled, log all allocations.
3017 if (!(s_fHeapAllocHighEventEnabledNow && s_fHeapAllocLowEventEnabledNow))
3019 // Get the number of threads so that we can scale the per-thread sampling data.
3020 // NOTE: We don't do this while holding the thread store lock, so this may not be perfect,
3021 // but it will be close enough.
3022 LONG numThreads = ThreadStore::s_pThreadStore->ThreadCountInEE();
3024 // This is our filter. If we should ignore this alloc, then record our updated
3025 // our stats, and bail without sending the event. Note that we always log objects
3026 // over 10K in size.
3027 if (size < 10000 && typeLoggingInfo.dwAllocsSkippedForSample < (typeLoggingInfo.dwAllocsToSkipPerSample * numThreads))
3029 // Update hash table's copy of type logging info with these values. It is not optimal that
3030 // we're doing another hash table lookup here. Could instead have used LookupPtr()
3031 // if it gave us back a non-const pointer, and then we could have updated in-place
3032 AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo);
3035 // Although we're skipping logging the allocation, we still need to log
3036 // the type (so it's available for resolving future allocation events to
3039 // (See other call to LogTypeAndParametersIfNecessary further down for
3041 LogTypeAndParametersIfNecessary(
3044 kTypeLogBehaviorAlwaysLogTopLevelType);
3049 // Based on observed allocation stats, adjust our sampling rate for this type
3051 typeLoggingInfo.dwAllocCountInCurrentBucket += typeLoggingInfo.dwAllocsSkippedForSample;
3052 int delta = (dwTickNow - typeLoggingInfo.dwTickOfCurrentTimeBucket) & 0x7FFFFFFF; // make wrap around work.
3054 int nMinAllocPerMSec = typeLoggingInfo.dwAllocCountInCurrentBucket / 16 / numThreads; // This is an underestimation of the true rate.
3055 if (delta >= 16 || (nMinAllocPerMSec > 2 && nMinAllocPerMSec > typeLoggingInfo.flAllocPerMSec * 1.5F))
3057 float flNewAllocPerMSec = 0;
3060 // This is the normal case, our allocation rate is under control with the current throttling.
3061 flNewAllocPerMSec = ((float) typeLoggingInfo.dwAllocCountInCurrentBucket) / delta;
3062 // Do a exponential decay window that is 5 * max(16, AllocationInterval)
3063 typeLoggingInfo.flAllocPerMSec = 0.8F * typeLoggingInfo.flAllocPerMSec + 0.2F * flNewAllocPerMSec;
3064 typeLoggingInfo.dwTickOfCurrentTimeBucket = dwTickNow;
3065 typeLoggingInfo.dwAllocCountInCurrentBucket = 0;
3069 flNewAllocPerMSec = (float) nMinAllocPerMSec;
3070 // This means the second clause above is true, which means our sampling rate is too low
3071 // so we need to throttle quickly.
3072 typeLoggingInfo.flAllocPerMSec = flNewAllocPerMSec;
3076 // Obey the desired sampling rate, but don't ignore > 1000 allocations per second
3078 int nDesiredMsBetweenEvents = (s_nCustomMsBetweenEvents == 0) ? GetDefaultMsBetweenEvents() : s_nCustomMsBetweenEvents;
3079 typeLoggingInfo.dwAllocsToSkipPerSample = min((int) (typeLoggingInfo.flAllocPerMSec * nDesiredMsBetweenEvents), 1000);
3080 if (typeLoggingInfo.dwAllocsToSkipPerSample == 1)
3081 typeLoggingInfo.dwAllocsToSkipPerSample = 0;
3085 // We're logging this sample, so save the values we need into locals, and reset
3086 // our counts for the next sample.
3087 nTotalSizeForTypeSample = typeLoggingInfo.cbIgnoredSizeForSample;
3088 dwObjectCountForTypeSample = typeLoggingInfo.dwAllocsSkippedForSample;
3089 typeLoggingInfo.cbIgnoredSizeForSample = 0;
3090 typeLoggingInfo.dwAllocsSkippedForSample = 0;
3092 // Save updated stats into hash table
3093 if (!AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo))
3098 // While we're still holding the crst, optionally log any relevant Types now (we may need
3099 // to reconsult the hash in here if there are any type parameters, though we can
3100 // optimize and NOT consult the hash for th itself).
3103 // We were the ones to add the Type to the hash. So it wasn't there before,
3104 // which means it hasn't been logged yet.
3105 LogTypeAndParametersIfNecessary(
3107 // No BulkTypeEventLogger, as we're not batching during a GC heap walk
3112 // We've determined the type is not yet logged, so no need to check
3113 kTypeLogBehaviorAlwaysLogTopLevelType);
3116 // Now log the allocation
3117 if (s_fHeapAllocHighEventEnabledNow)
3119 FireEtwGCSampledObjectAllocationHigh(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId());
3123 FireEtwGCSampledObjectAllocationLow(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId());
3127 //---------------------------------------------------------------------------------------
3129 // Accessor for global hash table crst
3132 // global hash table crst
3136 CrstBase * ETW::TypeSystemLog::GetHashCrst()
3138 LIMITED_METHOD_CONTRACT;
3139 return &AllLoggedTypes::s_cs;
3142 //---------------------------------------------------------------------------------------
3144 // Outermost level of ETW-type-logging. Clients outside eventtrace.cpp call this to log
3145 // a TypeHandle and (recursively) its type parameters when present. This guy then calls
3146 // into the appropriate BulkTypeEventLogger to do the batching and logging
3149 // * pBulkTypeEventLogger - If our caller is keeping track of batched types, it
3150 // passes this to us so we can use it to batch the current type (GC heap walk
3151 // does this). If this is NULL, no batching is going on (e.g., we're called on
3152 // object allocation, not a GC heal walk), in which case we create our own
3153 // temporary BulkTypeEventLogger.
3154 // * thAsAddr - TypeHandle to batch
3155 // * typeLogBehavior - Optimization to tell us we don't need to enter the
3156 // TypeSystemLog's crst, as the TypeSystemLog's hash table is already protected
3157 // by a prior acquisition of the crst by our caller. (Or that we don't even
3158 // need to check the hash in the first place.)
3162 VOID ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pLogger, ULONGLONG thAsAddr, TypeLogBehavior typeLogBehavior)
3170 // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to
3175 if (!ETW_TRACING_CATEGORY_ENABLED(
3176 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3177 TRACE_LEVEL_INFORMATION,
3183 TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr);
3184 if (!th.IsRestored())
3189 // Check to see if we've already logged this type. If so, bail immediately.
3190 // Otherwise, mark that it's getting logged (by adding it to the hash), and fall
3191 // through to the logging code below. If caller doesn't care, then don't even
3192 // check; just log the type
3193 BOOL fShouldLogType = ((typeLogBehavior == kTypeLogBehaviorAlwaysLog) ||
3194 (typeLogBehavior == kTypeLogBehaviorAlwaysLogTopLevelType)) ?
3197 if (!fShouldLogType)
3200 if (pLogger == NULL)
3202 // We're not batching this type against previous types (e.g., we're being called
3203 // on object allocate instead of a GC heap walk). So create a temporary logger
3204 // on the stack. If there are generic parameters that need to be logged, then
3205 // at least they'll get batched together with the type
3206 BulkTypeEventLogger logger;
3207 logger.LogTypeAndParameters(thAsAddr, typeLogBehavior);
3209 // Since this logger isn't being used to batch anything else, flush what we have
3210 logger.FireBulkTypeEvent();
3214 // We are batching this type with others (e.g., we're being called at the end of
3215 // a GC on a heap walk). So use the logger our caller set up for us.
3216 pLogger->LogTypeAndParameters(thAsAddr, typeLogBehavior);
3221 //---------------------------------------------------------------------------------------
3223 // Ask hash table if we've already logged the type, without first acquiring the lock
3224 // (our caller already did this). As a side-effect, a TypeLoggingInfo will be created
3225 // for this type (so future calls to this function will return FALSE to avoid dupe type
3229 // pth - TypeHandle to query
3232 // nonzero iff type should be logged (i.e., not previously logged)
3236 BOOL ETW::TypeSystemLog::ShouldLogType(TypeHandle th)
3248 // Check to see if TypeLoggingInfo exists yet for th. If not, creates one and
3249 // adds it to the hash.
3250 BOOL fCreatedNew = FALSE;
3252 // When we have a thread context, default to calling the API that requires one which
3253 // reduces the cost of locking.
3254 if (GetThread() != NULL)
3256 LookupOrCreateTypeLoggingInfo(th, &fCreatedNew);
3260 AddTypeToGlobalCacheIfNotExists(th, &fCreatedNew);
3263 // Return whether we had to create the TypeLoggingInfo (indicating it was not yet in
3264 // the hash, and thus that we hadn't yet logged the type).
3269 //---------------------------------------------------------------------------------------
3271 // Helper that returns (creating if necessary) the TypeLoggingInfo in the hash table
3272 // corresponding with the specified TypeHandle
3275 // * th - Key to lookup the TypeLoggingInfo
3276 // * pfCreatedNew - [out] Points to nonzero iff a new TypeLoggingInfo was created
3277 // (i.e., none existed yet in the hash for th).
3278 // * ppLoggedTypesFromModule - [out] Points to the inner hash that was used to do
3279 // the lookup. (An otpimization so the caller doesn't have to find this again,
3280 // if it needs to do further operations on it.)
3283 // TypeLoggingInfo found or created.
3288 ETW::TypeLoggingInfo ETW::TypeSystemLog::LookupOrCreateTypeLoggingInfo(TypeHandle th, BOOL * pfCreatedNew, LoggedTypesFromModule ** ppLoggedTypesFromModule /* = NULL */)
3290 //LIMITED_METHOD_CONTRACT;
3299 _ASSERTE(pfCreatedNew != NULL);
3301 if (ppLoggedTypesFromModule != NULL)
3303 *ppLoggedTypesFromModule = NULL;
3306 BOOL fSucceeded = FALSE;
3308 Thread *pThread = GetThread();
3310 // Compare the thread local epoch value against the global epoch.
3311 // If the epoch has changed, dump the thread local state and start over.
3312 AllLoggedTypes * pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3313 if((pThreadAllLoggedTypes != NULL) && (pThreadAllLoggedTypes->nEpoch != s_nEpoch))
3315 // Set the type hash pointer on the thread to NULL.
3316 pThread->SetAllocationSamplingTable(NULL);
3318 // DeleteTypeHashNoLock will set pThreadAllLoggedTypes to NULL
3319 DeleteTypeHashNoLock(&pThreadAllLoggedTypes);
3322 // Create the thread local state if it doesn't exist.
3323 if (pThreadAllLoggedTypes == NULL)
3325 pThreadAllLoggedTypes = new (nothrow) AllLoggedTypes;
3326 if (pThreadAllLoggedTypes == NULL)
3328 // out of memory. Bail on ETW stuff
3329 *pfCreatedNew = FALSE;
3330 return TypeLoggingInfo(NULL);
3333 // Set the epoch so we know we can track when changes to global state occur.
3334 pThreadAllLoggedTypes->nEpoch = s_nEpoch;
3336 // Save the thread local state to the thread.
3337 pThread->SetAllocationSamplingTable(pThreadAllLoggedTypes);
3340 BOOL addTypeToGlobalList = FALSE;
3342 // Step 1: go from LoaderModule to hash of types.
3344 Module * pLoaderModule = th.GetLoaderModule();
3345 _ASSERTE(pLoaderModule != NULL);
3346 LoggedTypesFromModule * pLoggedTypesFromModule = pThreadAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule);
3347 if (pLoggedTypesFromModule == NULL)
3349 addTypeToGlobalList = TRUE;
3350 pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule);
3351 if (pLoggedTypesFromModule == NULL)
3353 // out of memory. Bail on ETW stuff
3354 *pfCreatedNew = FALSE;
3355 return TypeLoggingInfo(NULL);
3361 pThreadAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule);
3368 EX_END_CATCH(RethrowCorruptingExceptions);
3371 *pfCreatedNew = FALSE;
3372 return TypeLoggingInfo(NULL);
3376 if (ppLoggedTypesFromModule != NULL)
3378 *ppLoggedTypesFromModule = pLoggedTypesFromModule;
3381 // Step 2: From hash of types, see if our TypeHandle is there already
3382 TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th);
3383 if (!typeLoggingInfoPreexisting.th.IsNull())
3385 // Type is already hashed, so it's already logged, so we don't need to
3387 *pfCreatedNew = FALSE;
3388 return typeLoggingInfoPreexisting;
3391 // We haven't logged this type, so we need to continue with this function to
3392 // log it below. Add it to the hash table first so any recursive calls will
3393 // see that this type is already being taken care of
3394 addTypeToGlobalList = TRUE;
3396 TypeLoggingInfo typeLoggingInfoNew(th);
3399 pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew);
3406 EX_END_CATCH(RethrowCorruptingExceptions);
3409 *pfCreatedNew = FALSE;
3410 return TypeLoggingInfo(NULL);
3413 // This is the first time that we've seen this type on this thread, so we should attempt to
3414 // add it to the global list.
3415 if(!AddTypeToGlobalCacheIfNotExists(th, pfCreatedNew))
3417 // out of memory or ETW has been disabled. Bail on ETW stuff
3418 *pfCreatedNew = FALSE;
3419 return TypeLoggingInfo(NULL);
3422 return typeLoggingInfoNew;
3425 //---------------------------------------------------------------------------------------
3427 // Helper that creates a Type entry in the global type logging cache if one doesn't
3431 // * th - Key to lookup or create
3434 // TRUE if the type needed to be added to the cache.
3439 BOOL ETW::TypeSystemLog::AddTypeToGlobalCacheIfNotExists(TypeHandle th, BOOL * pfCreatedNew)
3449 BOOL fSucceeded = FALSE;
3452 CrstHolder _crst(GetHashCrst());
3454 // Check if ETW is enabled, and if not, bail here.
3455 // We do this inside of the lock to ensure that we don't immediately
3456 // re-allocate the global type hash after it has been cleaned up.
3457 if (!ETW_TRACING_CATEGORY_ENABLED(
3458 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3459 TRACE_LEVEL_INFORMATION,
3462 *pfCreatedNew = FALSE;
3466 if (s_pAllLoggedTypes == NULL)
3468 s_pAllLoggedTypes = new (nothrow) AllLoggedTypes;
3469 if (s_pAllLoggedTypes == NULL)
3471 // out of memory. Bail on ETW stuff
3472 *pfCreatedNew = FALSE;
3477 // Step 1: go from LoaderModule to hash of types.
3479 Module * pLoaderModule = th.GetLoaderModule();
3480 _ASSERTE(pLoaderModule != NULL);
3481 LoggedTypesFromModule * pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule);
3482 if (pLoggedTypesFromModule == NULL)
3484 pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule);
3485 if (pLoggedTypesFromModule == NULL)
3487 // out of memory. Bail on ETW stuff
3488 *pfCreatedNew = FALSE;
3495 s_pAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule);
3502 EX_END_CATCH(RethrowCorruptingExceptions);
3505 *pfCreatedNew = FALSE;
3510 // Step 2: From hash of types, see if our TypeHandle is there already
3511 TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th);
3512 if (!typeLoggingInfoPreexisting.th.IsNull())
3514 // Type is already hashed, so it's already logged, so we don't need to
3516 *pfCreatedNew = FALSE;
3520 // We haven't logged this type, so we need to continue with this function to
3521 // log it below. Add it to the hash table first so any recursive calls will
3522 // see that this type is already being taken care of
3524 TypeLoggingInfo typeLoggingInfoNew(th);
3527 pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew);
3534 EX_END_CATCH(RethrowCorruptingExceptions);
3537 *pfCreatedNew = FALSE;
3540 } // RELEASE: CrstHolder _crst(GetHashCrst());
3542 *pfCreatedNew = TRUE;
3547 //---------------------------------------------------------------------------------------
3549 // Called when we determine if a module was unloaded, so we can clear out that module's
3550 // set of types from our hash table
3553 // pModule - Module getting unloaded
3557 VOID ETW::TypeSystemLog::OnModuleUnload(Module * pModule)
3568 // We don't need to do anything if allocation sampling is disabled.
3569 if (!ETW_TRACING_CATEGORY_ENABLED(
3570 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3571 TRACE_LEVEL_INFORMATION,
3577 LoggedTypesFromModule * pLoggedTypesFromModule = NULL;
3580 CrstHolder _crst(GetHashCrst());
3582 // We don't need to do anything if the global type hash doesn't contain any data.
3583 if (s_pAllLoggedTypes == NULL)
3586 // Is there a TypesHash for this module?
3587 pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pModule);
3588 if (pLoggedTypesFromModule == NULL)
3591 // Remove TypesHash from master hash mapping modules to their TypesHash
3592 s_pAllLoggedTypes->allLoggedTypesHash.Remove(pModule);
3594 // Increment the epoch to signal the change to all threads.
3598 // Destruct this TypesHash we just removed
3599 delete pLoggedTypesFromModule;
3600 pLoggedTypesFromModule = NULL;
3604 //---------------------------------------------------------------------------------------
3606 // Same semantics as DeleteTypeHash but assumes that the appropriate lock
3607 // has already been acquired.
3611 VOID ETW::TypeSystemLog::DeleteTypeHashNoLock(AllLoggedTypes **ppAllLoggedTypes)
3613 LIMITED_METHOD_CONTRACT;
3615 if(ppAllLoggedTypes == NULL)
3620 AllLoggedTypes *pAllLoggedTypes = *ppAllLoggedTypes;
3622 if(pAllLoggedTypes == NULL)
3627 // Destruct each of the per-module TypesHashes
3628 AllLoggedTypesHash * pLoggedTypesHash = &pAllLoggedTypes->allLoggedTypesHash;
3629 for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin();
3630 iter != pLoggedTypesHash->End();
3633 LoggedTypesFromModule * pLoggedTypesFromModule = *iter;
3634 delete pLoggedTypesFromModule;
3637 // This causes the default ~AllLoggedTypes() to be called, and thus
3638 // ~AllLoggedTypesHash() to be called
3639 delete pAllLoggedTypes;
3640 *ppAllLoggedTypes = NULL;
3643 //---------------------------------------------------------------------------------------
3645 // Called from shutdown to give us the opportunity to dump any sampled object allocation
3646 // information before the process shuts down.
3650 VOID ETW::TypeSystemLog::FlushObjectAllocationEvents()
3661 // If logging is not enabled, then we don't need to do any work.
3662 if (!(s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow))
3667 AllLoggedTypes * pThreadAllLoggedTypes = NULL;
3668 Thread * pThread = NULL;
3670 // Get the thread store lock.
3671 ThreadStoreLockHolder tsl;
3673 // Iterate over each thread and log any un-logged allocations.
3674 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
3676 pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3677 if (pThreadAllLoggedTypes == NULL)
3682 DWORD dwAllocsSkippedForSample;
3683 SIZE_T cbIgnoredSizeForSample;
3685 // Iterate over each module.
3686 AllLoggedTypesHash * pLoggedTypesHash = &pThreadAllLoggedTypes->allLoggedTypesHash;
3687 for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin();
3688 iter != pLoggedTypesHash->End();
3691 // Iterate over each type in the module.
3692 LoggedTypesFromModule * pLoggedTypesFromModule = *iter;
3693 LoggedTypesFromModuleHash * pLoggedTypesFromModuleHash = &pLoggedTypesFromModule->loggedTypesFromModuleHash;
3694 for (LoggedTypesFromModuleHash::Iterator typeIter = pLoggedTypesFromModuleHash->Begin();
3695 typeIter != pLoggedTypesFromModuleHash->End();
3698 dwAllocsSkippedForSample = typeIter->dwAllocsSkippedForSample;
3699 cbIgnoredSizeForSample = typeIter->cbIgnoredSizeForSample;
3701 // Only write the event if there were allocations that have not been logged.
3702 if (dwAllocsSkippedForSample > 0 || cbIgnoredSizeForSample > 0)
3704 // Write the event based on which keyword was specified when ETW was configured.
3705 if (s_fHeapAllocHighEventEnabledNow)
3707 FireEtwGCSampledObjectAllocationHigh(NULL, (LPVOID) typeIter->th.AsTAddr(), dwAllocsSkippedForSample, cbIgnoredSizeForSample, GetClrInstanceId());
3711 FireEtwGCSampledObjectAllocationLow(NULL, (LPVOID) typeIter->th.AsTAddr(), dwAllocsSkippedForSample, cbIgnoredSizeForSample, GetClrInstanceId());
3719 //---------------------------------------------------------------------------------------
3721 // Whenever we detect that the Types keyword is off, this gets called. This eliminates the
3722 // global hash tables that tracked which types were logged (if the hash tables had been created
3723 // previously). If type events are turned back on later, we'll re-log them all as we
3724 // encounter them. Thread local hash tables are destroyed in the Cleanup method, which is
3725 // called during GC to ensure that there aren't any races.
3729 VOID ETW::TypeSystemLog::OnTypesKeywordTurnedOff()
3740 // Take the global cache lock.
3741 CrstHolder _crst(GetHashCrst());
3743 // Clean-up the global TypeHash if necessary.
3744 if (s_pAllLoggedTypes == NULL)
3746 // Even if we don't increment the epoch, but we get into a situation where
3747 // some per thread data has been allocated, it will be cleaned up during the
3748 // next GC because we are guaranteed that s_nEpoch has been incremented at
3749 // least once (to shutdown allocation sampling).
3753 // Destruct the global TypeHash
3754 DeleteTypeHashNoLock(&s_pAllLoggedTypes);
3756 // Increment the epoch to signal the change to all threads.
3760 //---------------------------------------------------------------------------------------
3762 // Clean-up thread local type hashes. This is called from within the GC to ensure that
3763 // there are no races. All threads are suspended when this is called.
3767 VOID ETW::TypeSystemLog::Cleanup()
3777 // If allocation sampling is enabled, bail here so that we don't delete
3778 // any of the thread local state.
3779 if (ETW_TRACING_CATEGORY_ENABLED(
3780 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3781 TRACE_LEVEL_INFORMATION,
3787 // If logging is disabled but the epoch has not been incremented,
3788 // we haven't ever turned on allocation sampling, so there is nothing
3795 // Iterate over each thread and destruct the per thread caches
3796 AllLoggedTypes * pThreadAllLoggedTypes = NULL;
3797 Thread * pThread = NULL;
3798 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
3800 pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3801 if(pThreadAllLoggedTypes == NULL)
3806 // Destruct each of the thread local TypesHashes
3807 DeleteTypeHashNoLock(&pThreadAllLoggedTypes);
3809 // Set the thread type hash pointer to NULL
3810 pThread->SetAllocationSamplingTable(NULL);
3815 /****************************************************************************/
3816 /* Called when ETW is turned ON on an existing process and ModuleRange events are to
3818 /****************************************************************************/
3819 VOID ETW::EnumerationLog::ModuleRangeRundown()
3828 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context,
3829 TRACE_LEVEL_INFORMATION,
3830 CLR_PERFTRACK_PRIVATE_KEYWORD))
3832 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate);
3834 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
3838 /****************************************************************************/
3839 // Called when ETW is turned ON or OFF on an existing process, to send
3840 // events that are only sent once per rundown
3841 /****************************************************************************/
3842 VOID ETW::EnumerationLog::SendOneTimeRundownEvents()
3849 // Fire the runtime information event
3850 ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Callback);
3852 if (ETW::CompilationLog::TieredCompilation::Rundown::IsEnabled() && g_pConfig->TieredCompilation())
3854 ETW::CompilationLog::TieredCompilation::Rundown::SendSettings();
3859 /****************************************************************************/
3860 /* Called when ETW is turned ON on an existing process */
3861 /****************************************************************************/
3862 VOID ETW::EnumerationLog::StartRundown()
3871 SendOneTimeRundownEvents();
3873 BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3874 TRACE_LEVEL_INFORMATION,
3875 CLR_RUNDOWNPERFTRACK_KEYWORD);
3876 BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(
3877 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3878 TRACE_LEVEL_INFORMATION,
3879 CLR_RUNDOWNTHREADING_KEYWORD);
3881 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3882 TRACE_LEVEL_INFORMATION,
3883 CLR_RUNDOWNJIT_KEYWORD)
3885 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3886 TRACE_LEVEL_INFORMATION,
3887 CLR_RUNDOWNLOADER_KEYWORD)
3889 IsRundownNgenKeywordEnabledAndNotSuppressed()
3891 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3892 TRACE_LEVEL_INFORMATION,
3893 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)
3895 bIsPerfTrackRundownEnabled
3897 bIsThreadingRundownEnabled)
3899 // begin marker event will go to the rundown provider
3900 FireEtwDCStartInit_V1(GetClrInstanceId());
3902 // The rundown flag is expected to be checked in the caller, so no need to check here again
3903 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
3904 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3905 TRACE_LEVEL_INFORMATION,
3906 CLR_RUNDOWNLOADER_KEYWORD))
3908 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart;
3910 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3911 TRACE_LEVEL_INFORMATION,
3912 CLR_RUNDOWNJIT_KEYWORD))
3914 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart;
3916 if(IsRundownNgenKeywordEnabledAndNotSuppressed())
3918 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart;
3920 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
3921 TRACE_LEVEL_INFORMATION,
3922 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD))
3924 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap;
3926 if(bIsPerfTrackRundownEnabled)
3928 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart;
3931 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions);
3933 if (bIsThreadingRundownEnabled)
3935 SendThreadRundownEvent();
3938 // end marker event will go to the rundown provider
3939 FireEtwDCStartComplete_V1(GetClrInstanceId());
3941 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
3944 //---------------------------------------------------------------------------------------
3946 // Simple helper to convert the currently active keywords on the runtime provider into a
3947 // bitmask of enumeration options as defined in ETW::EnumerationLog::EnumerationStructs
3950 // ETW::EnumerationLog::EnumerationStructs bitmask corresponding to the currently
3951 // active keywords on the runtime provider
3955 DWORD ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords()
3957 LIMITED_METHOD_CONTRACT;
3959 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
3960 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3961 TRACE_LEVEL_INFORMATION,
3962 CLR_LOADER_KEYWORD))
3964 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload;
3966 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3967 TRACE_LEVEL_INFORMATION,
3969 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3970 TRACE_LEVEL_INFORMATION,
3971 CLR_ENDENUMERATION_KEYWORD))
3973 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodUnload;
3975 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed() &&
3976 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
3977 TRACE_LEVEL_INFORMATION,
3978 CLR_ENDENUMERATION_KEYWORD))
3980 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload;
3983 return enumerationOptions;
3986 //---------------------------------------------------------------------------------------
3988 // Executes a flavor of rundown initiated by a CAPTURE_STATE request to
3989 // code:#EtwCallback. CAPTURE_STATE is the "ETW-sanctioned" way of performing a
3990 // rundown, whereas the CLR's rundown provider was *our* version of this, implemented
3991 // before CAPTURE_STATE was standardized.
3993 // When doing a CAPTURE_STATE, the CLR rundown provider is completely unused. Instead,
3994 // we pay attention to the runtime keywords active at the time the CAPTURE_STATE was
3995 // requested, and enumerate through the appropriate objects (AppDomains, assemblies,
3996 // modules, types, methods, threads) and send runtime events for each of them.
3998 // CAPTURE_STATE is intended to be used primarily by PerfTrack. Implementing this form
3999 // of rundown allows PerfTrack to be blissfully unaware of the CLR's rundown provider.
4003 VOID ETW::EnumerationLog::EnumerateForCaptureState()
4014 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO))
4016 DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords();
4018 // Send unload events for all remaining domains, including shared domain and
4020 ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions);
4022 // Send thread created events for all currently active threads, if requested
4023 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
4024 TRACE_LEVEL_INFORMATION,
4025 CLR_THREADING_KEYWORD))
4027 SendThreadRundownEvent();
4030 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4033 /**************************************************************************************/
4034 /* Called when ETW is turned OFF on an existing process .Will be used by the controller for end rundown*/
4035 /**************************************************************************************/
4036 VOID ETW::EnumerationLog::EndRundown()
4045 SendOneTimeRundownEvents();
4047 BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4048 TRACE_LEVEL_INFORMATION,
4049 CLR_RUNDOWNPERFTRACK_KEYWORD);
4050 BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(
4051 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4052 TRACE_LEVEL_INFORMATION,
4053 CLR_RUNDOWNTHREADING_KEYWORD);
4054 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4055 TRACE_LEVEL_INFORMATION,
4056 CLR_RUNDOWNJIT_KEYWORD)
4058 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4059 TRACE_LEVEL_INFORMATION,
4060 CLR_RUNDOWNLOADER_KEYWORD)
4062 IsRundownNgenKeywordEnabledAndNotSuppressed()
4064 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4065 TRACE_LEVEL_INFORMATION,
4066 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)
4068 bIsPerfTrackRundownEnabled
4070 bIsThreadingRundownEnabled
4073 // begin marker event will go to the rundown provider
4074 FireEtwDCEndInit_V1(GetClrInstanceId());
4076 // The rundown flag is expected to be checked in the caller, so no need to check here again
4077 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
4078 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4079 TRACE_LEVEL_INFORMATION,
4080 CLR_RUNDOWNLOADER_KEYWORD))
4082 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd;
4084 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4085 TRACE_LEVEL_INFORMATION,
4086 CLR_RUNDOWNJIT_KEYWORD))
4088 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd;
4090 if(IsRundownNgenKeywordEnabledAndNotSuppressed())
4092 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd;
4094 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4095 TRACE_LEVEL_INFORMATION,
4096 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD))
4098 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap;
4100 if(bIsPerfTrackRundownEnabled)
4102 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd;
4105 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions);
4107 if (bIsThreadingRundownEnabled)
4109 SendThreadRundownEvent();
4112 // end marker event will go to the rundown provider
4113 FireEtwDCEndComplete_V1(GetClrInstanceId());
4116 STRESS_LOG1(LF_ALWAYS, LL_ERROR, "Exception during Rundown Enumeration, EIP of last AV = %p", g_LastAccessViolationEIP);
4117 } EX_END_CATCH(SwallowAllExceptions);
4123 Routine Description:
4125 Registers provider with ETW tracing framework.
4126 This function should not be called more than once, on
4127 Dll Process attach only.
4134 Returns the return value from RegisterTraceGuids or EventRegister.
4138 void InitializeEventTracing()
4148 // Do startup-only initialization of any state required by the ETW classes before
4149 // events can be fired
4150 HRESULT hr = ETW::TypeSystemLog::PreRegistrationInit();
4154 #if !defined(FEATURE_PAL)
4155 // Register CLR providers with the OS
4156 if (g_pEtwTracer == NULL)
4158 NewHolder <ETW::CEtwTracer> tempEtwTracer (new (nothrow) ETW::CEtwTracer());
4159 if (tempEtwTracer != NULL && tempEtwTracer->Register () == ERROR_SUCCESS)
4160 g_pEtwTracer = tempEtwTracer.Extract ();
4164 g_nClrInstanceId = GetRuntimeId() & 0x0000FFFF; // This will give us duplicate ClrInstanceId after UINT16_MAX
4166 // Any classes that need some initialization to happen after we've registered the
4167 // providers can do so now
4168 ETW::TypeSystemLog::PostRegistrationInit();
4170 #if defined(FEATURE_PAL) && defined (FEATURE_PERFTRACING)
4171 XplatEventLogger::InitializeLogger();
4172 #endif // FEATURE_PAL && FEATURE_PERFTRACING
4175 // Plumbing to funnel event pipe callbacks and ETW callbacks together into a single common
4176 // handler, for the purposes of informing the GC of changes to the event state.
4178 // There is one callback for every EventPipe provider and one for all of ETW. The reason
4179 // for this is that ETW passes the registration handle of the provider that was enabled
4180 // as a field on the "CallbackContext" field of the callback, while EventPipe passes null
4181 // unless another token is given to it when the provider is constructed. In the absence of
4182 // a suitable token, this implementation has a different callback for every EventPipe provider
4183 // that ultimately funnels them all into a common handler.
4185 #if defined(FEATURE_PAL)
4186 // CLR_GCHEAPCOLLECT_KEYWORD is defined by the generated ETW manifest on Windows.
4187 // On non-Windows, we need to make sure that this is defined. Given that we can't change
4188 // the value due to compatibility, we specify it here rather than generating defines based on the manifest.
4189 #define CLR_GCHEAPCOLLECT_KEYWORD 0x800000
4190 #endif // defined(FEATURE_PAL)
4192 // CallbackProviderIndex provides a quick identification of which provider triggered the
4194 enum CallbackProviderIndex
4197 DotNETRuntimeRundown = 1,
4198 DotNETRuntimeStress = 2,
4199 DotNETRuntimePrivate = 3
4202 // Common handler for all ETW or EventPipe event notifications. Based on the provider that
4203 // was enabled/disabled, this implementation forwards the event state change onto GCHeapUtilities
4204 // which will inform the GC to update its local state about what events are enabled.
4205 VOID EtwCallbackCommon(
4206 CallbackProviderIndex ProviderIndex,
4209 ULONGLONG MatchAnyKeyword,
4211 BOOL isEventPipeCallback)
4213 LIMITED_METHOD_CONTRACT;
4215 bool bIsPublicTraceHandle = ProviderIndex == DotNETRuntime;
4216 #if !defined(FEATURE_PAL)
4217 static_assert(GCEventLevel_Fatal == TRACE_LEVEL_FATAL, "GCEventLevel_Fatal value mismatch");
4218 static_assert(GCEventLevel_Error == TRACE_LEVEL_ERROR, "GCEventLevel_Error value mismatch");
4219 static_assert(GCEventLevel_Warning == TRACE_LEVEL_WARNING, "GCEventLevel_Warning mismatch");
4220 static_assert(GCEventLevel_Information == TRACE_LEVEL_INFORMATION, "GCEventLevel_Information mismatch");
4221 static_assert(GCEventLevel_Verbose == TRACE_LEVEL_VERBOSE, "GCEventLevel_Verbose mismatch");
4222 #endif // !defined(FEATURE_PAL)
4223 GCEventKeyword keywords = static_cast<GCEventKeyword>(MatchAnyKeyword);
4224 GCEventLevel level = static_cast<GCEventLevel>(Level);
4225 GCHeapUtilities::RecordEventStateChange(bIsPublicTraceHandle, keywords, level);
4227 DOTNET_TRACE_CONTEXT * ctxToUpdate;
4228 switch(ProviderIndex)
4231 ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
4233 case DotNETRuntimeRundown:
4234 ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context;
4236 case DotNETRuntimePrivate:
4237 ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context;
4239 case DotNETRuntimeStress:
4240 ctxToUpdate = &MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context;
4243 _ASSERTE(!"EtwCallbackCommon was called with invalid context");
4247 // This callback gets called on both ETW/EventPipe session enable/disable.
4248 // We need toupdate the EventPipe provider context if we are in a callback
4249 // from EventPipe, but not from ETW.
4250 if (isEventPipeCallback)
4252 ctxToUpdate->EventPipeProvider.Level = Level;
4253 ctxToUpdate->EventPipeProvider.EnabledKeywordsBitmask = MatchAnyKeyword;
4256 // Special check for the runtime provider's GCHeapCollectKeyword. Profilers
4257 // flick this to force a full GC.
4258 if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle &&
4259 ((MatchAnyKeyword & CLR_GCHEAPCOLLECT_KEYWORD) != 0))
4261 // Profilers may (optionally) specify extra data in the filter parameter
4262 // to log with the GCStart event.
4263 LONGLONG l64ClientSequenceNumber = 0;
4264 #if !defined(FEATURE_PAL)
4265 PEVENT_FILTER_DESCRIPTOR FilterData = (PEVENT_FILTER_DESCRIPTOR)pFilterData;
4266 if ((FilterData != NULL) &&
4267 (FilterData->Type == 1) &&
4268 (FilterData->Size == sizeof(l64ClientSequenceNumber)))
4270 l64ClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr);
4272 #endif // !defined(FEATURE_PAL)
4273 ETW::GCLog::ForceGC(l64ClientSequenceNumber);
4275 // TypeSystemLog needs a notification when certain keywords are modified, so
4276 // give it a hook here.
4277 if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle)
4279 ETW::TypeSystemLog::OnKeywordsChanged();
4283 // Individual callbacks for each EventPipe provider.
4285 VOID EventPipeEtwCallbackDotNETRuntimeStress(
4286 _In_ LPCGUID SourceId,
4287 _In_ ULONG ControlCode,
4289 _In_ ULONGLONG MatchAnyKeyword,
4290 _In_ ULONGLONG MatchAllKeyword,
4291 _In_opt_ EventFilterDescriptor* FilterData,
4292 _Inout_opt_ PVOID CallbackContext)
4294 LIMITED_METHOD_CONTRACT;
4296 EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData, true);
4299 VOID EventPipeEtwCallbackDotNETRuntime(
4300 _In_ LPCGUID SourceId,
4301 _In_ ULONG ControlCode,
4303 _In_ ULONGLONG MatchAnyKeyword,
4304 _In_ ULONGLONG MatchAllKeyword,
4305 _In_opt_ EventFilterDescriptor* FilterData,
4306 _Inout_opt_ PVOID CallbackContext)
4308 LIMITED_METHOD_CONTRACT;
4310 EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, true);
4313 VOID EventPipeEtwCallbackDotNETRuntimeRundown(
4314 _In_ LPCGUID SourceId,
4315 _In_ ULONG ControlCode,
4317 _In_ ULONGLONG MatchAnyKeyword,
4318 _In_ ULONGLONG MatchAllKeyword,
4319 _In_opt_ EventFilterDescriptor* FilterData,
4320 _Inout_opt_ PVOID CallbackContext)
4322 LIMITED_METHOD_CONTRACT;
4324 EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData, true);
4327 VOID EventPipeEtwCallbackDotNETRuntimePrivate(
4328 _In_ LPCGUID SourceId,
4329 _In_ ULONG ControlCode,
4331 _In_ ULONGLONG MatchAnyKeyword,
4332 _In_ ULONGLONG MatchAllKeyword,
4333 _In_opt_ EventFilterDescriptor* FilterData,
4334 _Inout_opt_ PVOID CallbackContext)
4336 WRAPPER_NO_CONTRACT;
4338 EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, true);
4342 #if !defined(FEATURE_PAL)
4343 HRESULT ETW::CEtwTracer::Register()
4345 WRAPPER_NO_CONTRACT;
4347 EventRegisterMicrosoft_Windows_DotNETRuntime();
4348 EventRegisterMicrosoft_Windows_DotNETRuntimePrivate();
4349 EventRegisterMicrosoft_Windows_DotNETRuntimeRundown();
4351 // Stress Log ETW events are available only on the desktop version of the runtime
4353 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeHandle;
4354 MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimePrivateHandle;
4355 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeRundownHandle;
4363 Routine Description:
4364 Unregisters the provider from ETW. This function
4365 should only be called once from DllMain Detach process.
4372 Returns ERROR_SUCCESS
4375 HRESULT ETW::CEtwTracer::UnRegister()
4377 LIMITED_METHOD_CONTRACT;
4378 EventUnregisterMicrosoft_Windows_DotNETRuntime();
4379 EventUnregisterMicrosoft_Windows_DotNETRuntimePrivate();
4380 EventUnregisterMicrosoft_Windows_DotNETRuntimeRundown();
4387 VOID EtwCallout(REGHANDLE RegHandle,
4388 PCEVENT_DESCRIPTOR Descriptor,
4389 ULONG ArgumentCount,
4390 PEVENT_DATA_DESCRIPTOR EventData)
4392 WRAPPER_NO_CONTRACT;
4393 UINT8 providerIndex = 0;
4394 if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4396 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4398 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4400 } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4403 _ASSERTE(!"Provider not one of Runtime, Rundown, Private and Stress");
4407 // stacks are supposed to be fired for only the events with a bit set in the etwStackSupportedEvents bitmap
4408 if(((etwStackSupportedEvents[providerIndex][Descriptor->Id/8]) &
4409 (1<<(Descriptor->Id%8))) != 0)
4411 if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4412 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, &CLRStackWalk, &CLRStackId);
4413 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4414 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, &CLRStackWalkDCStart, &CLRStackRundownId);
4415 } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4416 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, &CLRStackWalkPrivate, &CLRStackPrivateId);
4417 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4418 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context, &CLRStackWalkStress, &CLRStackStressId);
4427 // During the build, MC generates the code to register our provider, and to register
4428 // our ETW callback. (This is buried under Intermediates, in a path like
4429 // Intermediate\clr\corguids.nativeproj_1723354836\obj1c\x86\ClrEtwAll.h.) The ETW
4430 // callback is also generated for us by MC. But we can hook into this generated
4431 // callback by #defining MCGEN_PRIVATE_ENABLE_CALLBACK_V2 to be a call to this
4432 // function (EtwCallback), thus causing EtwCallback to get called after the
4433 // MC-generated code executes.
4435 // This callback function is called whenever an ETW session is enabled or disabled. A
4436 // callback function needs to be specified when the provider is registered. C style
4437 // callback wrappers are needed during event registration. To handle the callback
4438 // action in this class, we pass "this" during provider registration and modify the
4439 // context to the relevant context in the C callback later.
4442 _In_ LPCGUID SourceId,
4443 _In_ ULONG ControlCode,
4445 _In_ ULONGLONG MatchAnyKeyword,
4446 _In_ ULONGLONG MatchAllKeyword,
4447 _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
4448 _Inout_opt_ PVOID CallbackContext)
4452 if(g_fEEStarted) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);};
4455 STATIC_CONTRACT_FAULT;
4458 // Mark that we are the special ETWRundown thread. Currently all this does
4459 // is insure that AVs thrown in this thread are treated as normal exceptions.
4460 // This allows us to catch and swallow them. We can do this because we have
4461 // a reasonably strong belief that doing ETW Rundown does not change runtime state
4462 // and thus if an AV happens it is better to simply give up logging ETW and
4463 // instead of terminating the process (which is what we would do normally)
4464 ClrFlsThreadTypeSwitch etwRundownThreadHolder(ThreadType_ETWRundownThread);
4465 PMCGEN_TRACE_CONTEXT context = (PMCGEN_TRACE_CONTEXT)CallbackContext;
4467 BOOLEAN bIsPublicTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeHandle);
4469 BOOLEAN bIsPrivateTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimePrivateHandle);
4471 BOOLEAN bIsRundownTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeRundownHandle);
4473 GCEventKeyword keywords = static_cast<GCEventKeyword>(MatchAnyKeyword);
4474 GCEventLevel level = static_cast<GCEventLevel>(Level);
4475 GCHeapUtilities::RecordEventStateChange(!!bIsPublicTraceHandle, keywords, level);
4477 // EventPipeEtwCallback contains some GC eventing functionality shared between EventPipe and ETW.
4478 // Eventually, we'll want to merge these two codepaths whenever we can.
4479 CallbackProviderIndex providerIndex = DotNETRuntime;
4480 DOTNET_TRACE_CONTEXT providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
4481 if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4482 providerIndex = DotNETRuntime;
4483 providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context;
4484 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4485 providerIndex = DotNETRuntimeRundown;
4486 providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context;
4487 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4488 providerIndex = DotNETRuntimeStress;
4489 providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_DOTNET_Context;
4490 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4491 providerIndex = DotNETRuntimePrivate;
4492 providerContext = MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context;
4494 assert(!"unknown registration handle");
4498 EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData, false);
4500 // A manifest based provider can be enabled to multiple event tracing sessions
4501 // As long as there is atleast 1 enabled session, IsEnabled will be TRUE
4502 // Since classic providers can be enabled to only a single session,
4503 // IsEnabled will be TRUE when it is enabled and FALSE when disabled
4505 ((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
4506 (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE));
4509 if (bIsPrivateTraceHandle)
4511 ETW::GCLog::GCSettingsEvent();
4512 if(g_fEEStarted && !g_fEEShutDown)
4514 ETW::EnumerationLog::ModuleRangeRundown();
4518 #ifdef _TARGET_AMD64_
4519 // We only do this on amd64 (NOT ARM, because ARM uses frame based stack crawling)
4520 // If we have turned on the JIT keyword to the INFORMATION setting (needed to get JIT names) then
4521 // we assume that we also want good stack traces so we need to publish unwind information so
4522 // ETW can get at it
4523 if(bIsPublicTraceHandle && ETW_CATEGORY_ENABLED(providerContext, TRACE_LEVEL_INFORMATION, CLR_RUNDOWNJIT_KEYWORD))
4524 UnwindInfoTable::PublishUnwindInfo(g_fEEStarted != FALSE);
4527 if(g_fEEStarted && !g_fEEShutDown && bIsRundownTraceHandle)
4529 // Start and End Method/Module Rundowns
4530 // Used to fire events that we missed since we started the controller after the process started
4531 // flags for immediate start rundown
4532 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4533 TRACE_LEVEL_INFORMATION,
4534 CLR_RUNDOWNSTART_KEYWORD))
4535 ETW::EnumerationLog::StartRundown();
4537 // flags delayed end rundown
4538 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
4539 TRACE_LEVEL_INFORMATION,
4540 CLR_RUNDOWNEND_KEYWORD))
4541 ETW::EnumerationLog::EndRundown();
4544 if (g_fEEStarted && !g_fEEShutDown && (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE))
4546 ETW::EnumerationLog::EnumerateForCaptureState();
4549 #ifdef FEATURE_COMINTEROP
4550 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, CCWRefCountChange))
4551 g_pConfig->SetLogCCWRefCountChangeEnabled(bEnabled != 0);
4552 #endif // FEATURE_COMINTEROP
4556 #endif // FEATURE_REDHAWK
4558 #endif // FEATURE_PAL
4559 #ifndef FEATURE_REDHAWK
4561 /****************************************************************************/
4562 /* This is called by the runtime when an exception is thrown */
4563 /****************************************************************************/
4564 VOID ETW::ExceptionLog::ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownException, BOOL bIsNewException)
4569 PRECONDITION(GetThread() != NULL);
4570 PRECONDITION(GetThread()->GetThrowable() != NULL);
4573 if(!(bIsReThrownException || bIsNewException))
4577 if(!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionThrown_V1))
4583 SString exceptionType(W(""));
4584 LPWSTR exceptionMessage = NULL;
4585 BOOL bIsCLSCompliant=FALSE, bIsCSE=FALSE, bIsNestedException=FALSE, bHasInnerException=FALSE;
4586 UINT16 exceptionFlags=0;
4587 PVOID exceptionEIP=0;
4589 Thread *pThread = GetThread();
4593 OBJECTREF exceptionObj;
4594 OBJECTREF innerExceptionObj;
4595 STRINGREF exceptionMessageRef;
4597 ZeroMemory(&gc, sizeof(gc));
4598 GCPROTECT_BEGIN(gc);
4600 gc.exceptionObj = pThread->GetThrowable();
4601 gc.innerExceptionObj = ((EXCEPTIONREF)gc.exceptionObj)->GetInnerException();
4603 ThreadExceptionState *pExState = pThread->GetExceptionState();
4604 #ifndef WIN64EXCEPTIONS
4605 PTR_ExInfo pExInfo = NULL;
4607 PTR_ExceptionTracker pExInfo = NULL;
4608 #endif //!WIN64EXCEPTIONS
4609 pExInfo = pExState->GetCurrentExceptionTracker();
4610 _ASSERTE(pExInfo != NULL);
4611 bIsNestedException = (pExInfo->GetPreviousExceptionTracker() != NULL);
4612 bIsCSE = (pExInfo->GetCorruptionSeverity() == ProcessCorrupting);
4613 bIsCLSCompliant = IsException((gc.exceptionObj)->GetMethodTable()) &&
4614 ((gc.exceptionObj)->GetMethodTable() != MscorlibBinder::GetException(kRuntimeWrappedException));
4616 // A rethrown exception is also a nested exception
4617 // but since we have a separate flag for it, lets unset the nested flag
4618 if(bIsReThrownException)
4620 bIsNestedException = FALSE;
4622 bHasInnerException = (gc.innerExceptionObj) != NULL;
4624 exceptionFlags = ((bHasInnerException ? ETW::ExceptionLog::ExceptionStructs::HasInnerException : 0) |
4625 (bIsNestedException ? ETW::ExceptionLog::ExceptionStructs::IsNestedException : 0) |
4626 (bIsReThrownException ? ETW::ExceptionLog::ExceptionStructs::IsReThrownException : 0) |
4627 (bIsCSE ? ETW::ExceptionLog::ExceptionStructs::IsCSE : 0) |
4628 (bIsCLSCompliant ? ETW::ExceptionLog::ExceptionStructs::IsCLSCompliant : 0));
4630 if (pCf->IsFrameless())
4633 exceptionEIP = (PVOID)pCf->GetRegisterSet()->ControlPC;
4635 exceptionEIP = (PVOID)GetIP(pCf->GetRegisterSet()->pContext);
4640 exceptionEIP = (PVOID)(pCf->GetFrame()->GetIP());
4643 // On platforms other than IA64, we are at the instruction after the faulting instruction
4644 // This check has been copied from StackTraceInfo::AppendElement
4645 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && exceptionEIP != 0)
4647 exceptionEIP = (PVOID)((UINT_PTR)exceptionEIP - 1);
4650 gc.exceptionMessageRef = ((EXCEPTIONREF)gc.exceptionObj)->GetMessage();
4651 TypeHandle exceptionTypeHandle = (gc.exceptionObj)->GetTypeHandle();
4652 exceptionTypeHandle.GetName(exceptionType);
4653 WCHAR *exceptionTypeName = (WCHAR *)exceptionType.GetUnicode();
4655 if(gc.exceptionMessageRef != NULL)
4657 exceptionMessage = (gc.exceptionMessageRef)->GetBuffer();
4660 HRESULT exceptionHRESULT = ((EXCEPTIONREF)gc.exceptionObj)->GetHResult();
4662 FireEtwExceptionThrown_V1(exceptionTypeName,
4667 GetClrInstanceId());
4669 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4673 VOID ETW::ExceptionLog::ExceptionThrownEnd()
4680 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionThrownStop))
4685 FireEtwExceptionThrownStop();
4688 /****************************************************************************/
4689 /* This is called by the runtime when an exception is handled by the runtime */
4690 /****************************************************************************/
4691 VOID ETW::ExceptionLog::ExceptionCatchBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4698 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionCatchStart))
4706 pMethodDesc->GetFullMethodInfo(methodName);
4708 FireEtwExceptionCatchStart((uint64_t)pEntryEIP,
4709 (uint64_t)pMethodDesc,
4710 methodName.GetUnicode(),
4711 GetClrInstanceId());
4713 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4716 VOID ETW::ExceptionLog::ExceptionCatchEnd()
4723 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionCatchStop))
4728 FireEtwExceptionCatchStop();
4731 VOID ETW::ExceptionLog::ExceptionFinallyBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4738 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionFinallyStart))
4746 pMethodDesc->GetFullMethodInfo(methodName);
4748 FireEtwExceptionFinallyStart((uint64_t)pEntryEIP,
4749 (uint64_t)pMethodDesc,
4750 methodName.GetUnicode(),
4751 GetClrInstanceId());
4753 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4756 VOID ETW::ExceptionLog::ExceptionFinallyEnd()
4763 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionFinallyStop))
4768 FireEtwExceptionFinallyStop();
4771 VOID ETW::ExceptionLog::ExceptionFilterBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4778 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionFilterStart))
4786 pMethodDesc->GetFullMethodInfo(methodName);
4788 FireEtwExceptionFilterStart((uint64_t)pEntryEIP,
4789 (uint64_t)pMethodDesc,
4790 methodName.GetUnicode(),
4791 GetClrInstanceId());
4793 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4796 VOID ETW::ExceptionLog::ExceptionFilterEnd()
4803 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ExceptionFilterStop))
4808 FireEtwExceptionFilterStop();
4811 /****************************************************************************/
4812 /* This is called by the runtime when a domain is loaded */
4813 /****************************************************************************/
4814 VOID ETW::LoaderLog::DomainLoadReal(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName)
4823 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
4824 TRACE_LEVEL_INFORMATION,
4825 CLR_LOADER_KEYWORD))
4827 DWORD dwEventOptions = ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad;
4828 ETW::LoaderLog::SendDomainEvent(pDomain, dwEventOptions, wszFriendlyName);
4830 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4833 /****************************************************************************/
4834 /* This is called by the runtime when an AppDomain is unloaded */
4835 /****************************************************************************/
4836 VOID ETW::LoaderLog::DomainUnload(AppDomain *pDomain)
4845 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
4846 TRACE_LEVEL_INFORMATION,
4849 DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
4851 // Domain unload also causes type unload events
4852 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
4853 TRACE_LEVEL_INFORMATION,
4856 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
4859 ETW::EnumerationLog::EnumerationHelper(NULL, pDomain, enumerationOptions);
4861 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4864 /****************************************************************************/
4865 /* This is called by the runtime when a LoaderAllocator is unloaded */
4866 /****************************************************************************/
4867 VOID ETW::LoaderLog::CollectibleLoaderAllocatorUnload(AssemblyLoaderAllocator *pLoaderAllocator)
4876 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
4877 TRACE_LEVEL_INFORMATION,
4880 DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
4882 // Collectible Loader Allocator unload also causes type unload events
4883 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
4884 TRACE_LEVEL_INFORMATION,
4887 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
4890 ETW::EnumerationLog::IterateCollectibleLoaderAllocator(pLoaderAllocator, enumerationOptions);
4892 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4895 /****************************************************************************/
4896 /* This is called by the runtime when the runtime is loaded
4897 Function gets called by both the Callback mechanism and regular ETW events.
4898 Type is used to differentiate whether its a callback or a normal call*/
4899 /****************************************************************************/
4900 VOID ETW::InfoLog::RuntimeInformation(INT32 type)
4908 if((type == ETW::InfoLog::InfoStructs::Normal && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, RuntimeInformationStart))
4910 (type == ETW::InfoLog::InfoStructs::Callback && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context, RuntimeInformationDCStart))
4913 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W("");
4914 UINT8 startupMode = 0;
4915 UINT startupFlags = 0;
4918 _ASSERTE(CLRHosted() || g_fEEHostedStartup || // CLR started through one of the Hosting API CLRHosted() returns true if CLR started through the V2 Interface while
4919 // g_fEEHostedStartup is true if CLR is hosted through the V1 API.
4920 g_fEEComActivatedStartup || //CLR started as a COM object
4921 g_fEEOtherStartup ); //In case none of the 4 above mentioned cases are true for example ngen, ildasm then we asssume its a "other" startup
4923 Sku = ETW::InfoLog::InfoStructs::CoreCLR;
4925 //version info for clr.dll
4926 USHORT vmMajorVersion = CLR_MAJOR_VERSION;
4927 USHORT vmMinorVersion = CLR_MINOR_VERSION;
4928 USHORT vmBuildVersion = CLR_BUILD_VERSION;
4929 USHORT vmQfeVersion = CLR_BUILD_VERSION_QFE;
4931 //version info for mscorlib.dll
4932 USHORT bclMajorVersion = VER_ASSEMBLYMAJORVERSION;
4933 USHORT bclMinorVersion = VER_ASSEMBLYMINORVERSION;
4934 USHORT bclBuildVersion = VER_ASSEMBLYBUILD;
4935 USHORT bclQfeVersion = VER_ASSEMBLYBUILD_QFE;
4937 LPCGUID comGUID=&g_EEComObjectGuid;
4939 PCWSTR lpwszCommandLine = W("");
4943 // Determine the startupmode
4944 if (CLRHosted() || g_fEEHostedStartup)
4947 startupMode = ETW::InfoLog::InfoStructs::HostedCLR;
4949 else if(g_fEEComActivatedStartup)
4952 startupMode = ETW::InfoLog::InfoStructs::COMActivated;
4954 else if(g_fEEOtherStartup)
4956 //startup type is other
4957 startupMode = ETW::InfoLog::InfoStructs::Other;
4961 // if WszGetModuleFileName fails, we return an empty string
4962 if (!WszGetModuleFileName(GetCLRModule(), dllPath)) {
4963 dllPath.Set(W("\0"));
4967 if(type == ETW::InfoLog::InfoStructs::Callback)
4969 FireEtwRuntimeInformationDCStart( GetClrInstanceId(),
4987 FireEtwRuntimeInformationStart( GetClrInstanceId(),
5004 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5007 /* Fires ETW events every time a pdb is dynamically loaded.
5009 * The ETW events correspond to sending parts of the pdb in roughly
5010 * 64K sized chunks in order. Additional information sent is as follows:
5011 * ModuleID, TotalChunks, Size of Current Chunk, Chunk Number, CLRInstanceID
5013 * Note: The current implementation does not support reflection.emit.
5014 * The method will silently return without firing an event.
5017 VOID ETW::CodeSymbolLog::EmitCodeSymbols(Module* pModule)
5019 #if !defined(FEATURE_PAL) //UNIXTODO: Enable EmitCodeSymbols
5029 if (ETW_TRACING_CATEGORY_ENABLED(
5030 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5031 TRACE_LEVEL_VERBOSE,
5032 CLR_CODESYMBOLS_KEYWORD))
5034 if (pModule != NULL)
5036 UINT16 clrInstanceID = GetClrInstanceId();
5037 UINT64 moduleID = (ModuleID)pModule;
5039 // We silently exit if pdb is of length 0 instead of sending an event with no pdb bytes
5040 if (CodeSymbolLog::GetInMemorySymbolsLength(pModule, &length) == S_OK && length > 0)
5042 // The maximum data size allowed is 64K - (Size of the Event_Header)
5043 // Since the actual size of user data can only be determined at runtime
5044 // we simplify the header size value to be 1000 bytes as a conservative
5046 static const DWORD maxDataSize = 63000;
5048 ldiv_t qr = ldiv(length, maxDataSize);
5050 // We do not allow pdbs of size greater than 2GB for now,
5051 // so totalChunks should fit in 16 bits.
5052 if (qr.quot < UINT16_MAX)
5054 // If there are trailing bits in the last chunk, then increment totalChunks by 1
5055 UINT16 totalChunks = (UINT16)(qr.quot + ((qr.rem != 0) ? 1 : 0));
5056 NewArrayHolder<BYTE> chunk(new BYTE[maxDataSize]);
5058 for (UINT16 chunkNum = 0; offset < length; chunkNum++)
5060 DWORD lengthRead = 0;
5061 // We expect ReadInMemorySymbols to always return maxDataSize sized chunks
5062 // Or it is the last chunk and it is less than maxDataSize.
5063 CodeSymbolLog::ReadInMemorySymbols(pModule, offset, chunk, maxDataSize, &lengthRead);
5065 _ASSERTE(lengthRead == maxDataSize || // Either we are in the first to (n-1)th chunk
5066 (lengthRead < maxDataSize && chunkNum + 1 == totalChunks)); // Or we are in the last chunk
5068 FireEtwCodeSymbols(moduleID, totalChunks, chunkNum, lengthRead, chunk, clrInstanceID);
5069 offset += lengthRead;
5075 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
5076 #endif// !defined(FEATURE_PAL)
5079 /* Returns the length of an in-memory symbol stream
5081 * If the module has in-memory symbols the length of the stream will
5082 * be placed in pCountSymbolBytes. If the module doesn't have in-memory
5083 * symbols, *pCountSymbolBytes = 0
5085 * Returns S_OK if the length could be determined (even if it is 0)
5087 * Note: The current implementation does not support reflection.emit.
5088 * CORPROF_E_MODULE_IS_DYNAMIC will be returned in that case.
5090 * //IMPORTANT NOTE: The desktop code outside the Project K branch
5091 * contains copies of this function in the clr\src\vm\proftoeeinterfaceimpl.cpp
5092 * file of the desktop version corresponding to the profiler version
5093 * of this feature. Anytime that feature/code is ported to Project K
5094 * the code below should be appropriately merged so as to avoid
5098 HRESULT ETW::CodeSymbolLog::GetInMemorySymbolsLength(
5100 DWORD* pCountSymbolBytes)
5111 if (pCountSymbolBytes == NULL)
5113 return E_INVALIDARG;
5115 *pCountSymbolBytes = 0;
5117 if (pModule == NULL)
5119 return E_INVALIDARG;
5121 if (pModule->IsBeingUnloaded())
5123 return CORPROF_E_DATAINCOMPLETE;
5126 //This method would work fine on reflection.emit, but there would be no way to know
5127 //if some other thread was changing the size of the symbols before this method returned.
5128 //Adding events or locks to detect/prevent changes would make the scenario workable
5129 if (pModule->IsReflection())
5131 return COR_PRF_MODULE_DYNAMIC;
5134 CGrowableStream* pStream = pModule->GetInMemorySymbolStream();
5135 if (pStream == NULL)
5140 STATSTG SizeData = { 0 };
5141 hr = pStream->Stat(&SizeData, STATFLAG_NONAME);
5146 if (SizeData.cbSize.u.HighPart > 0)
5148 return COR_E_OVERFLOW;
5150 *pCountSymbolBytes = SizeData.cbSize.u.LowPart;
5155 /* Reads bytes from an in-memory symbol stream
5157 * This function attempts to read countSymbolBytes of data starting at offset
5158 * symbolsReadOffset within the in-memory stream. The data will be copied into
5159 * pSymbolBytes which is expected to have countSymbolBytes of space available.
5160 * pCountSymbolsBytesRead contains the actual number of bytes read which
5161 * may be less than countSymbolBytes if the end of the stream is reached.
5163 * Returns S_OK if a non-zero number of bytes were read.
5165 * Note: The current implementation does not support reflection.emit.
5166 * CORPROF_E_MODULE_IS_DYNAMIC will be returned in that case.
5168 * //IMPORTANT NOTE: The desktop code outside the Project K branch
5169 * contains copies of this function in the clr\src\vm\proftoeeinterfaceimpl.cpp
5170 * file of the desktop version corresponding to the profiler version
5171 * of this feature. Anytime that feature/code is ported to Project K
5172 * the code below should be appropriately merged so as to avoid
5177 HRESULT ETW::CodeSymbolLog::ReadInMemorySymbols(
5179 DWORD symbolsReadOffset,
5181 DWORD countSymbolBytes,
5182 DWORD* pCountSymbolBytesRead)
5193 if (pSymbolBytes == NULL)
5195 return E_INVALIDARG;
5197 if (pCountSymbolBytesRead == NULL)
5199 return E_INVALIDARG;
5201 *pCountSymbolBytesRead = 0;
5203 if (pModule == NULL)
5205 return E_INVALIDARG;
5207 if (pModule->IsBeingUnloaded())
5209 return CORPROF_E_DATAINCOMPLETE;
5212 //This method would work fine on reflection.emit, but there would be no way to know
5213 //if some other thread was changing the size of the symbols before this method returned.
5214 //Adding events or locks to detect/prevent changes would make the scenario workable
5215 if (pModule->IsReflection())
5217 return COR_PRF_MODULE_DYNAMIC;
5220 CGrowableStream* pStream = pModule->GetInMemorySymbolStream();
5221 if (pStream == NULL)
5223 return E_INVALIDARG;
5226 STATSTG SizeData = { 0 };
5227 hr = pStream->Stat(&SizeData, STATFLAG_NONAME);
5232 if (SizeData.cbSize.u.HighPart > 0)
5234 return COR_E_OVERFLOW;
5236 DWORD streamSize = SizeData.cbSize.u.LowPart;
5237 if (symbolsReadOffset >= streamSize)
5239 return E_INVALIDARG;
5242 *pCountSymbolBytesRead = min(streamSize - symbolsReadOffset, countSymbolBytes);
5243 memcpy_s(pSymbolBytes, countSymbolBytes, ((BYTE*)pStream->GetRawBuffer().StartAddress()) + symbolsReadOffset, *pCountSymbolBytesRead);
5248 VOID ETW::MethodLog::GetR2RGetEntryPoint(MethodDesc *pMethodDesc, PCODE pEntryPoint)
5255 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, R2RGetEntryPoint))
5259 SString tNamespace, tMethodName, tMethodSignature;
5260 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
5262 FireEtwR2RGetEntryPoint(
5263 (UINT64)pMethodDesc,
5264 (PCWSTR)tNamespace.GetUnicode(),
5265 (PCWSTR)tMethodName.GetUnicode(),
5266 (PCWSTR)tMethodSignature.GetUnicode(),
5268 GetClrInstanceId());
5270 } EX_CATCH{ } EX_END_CATCH(SwallowAllExceptions);
5274 /*******************************************************/
5275 /* This is called by the runtime when a method is jitted completely */
5276 /*******************************************************/
5277 VOID ETW::MethodLog::MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, PCODE pNativeCodeStartAddress, PrepareCodeConfig *pConfig)
5286 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5287 TRACE_LEVEL_INFORMATION,
5290 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodLoad, TRUE, namespaceOrClassName, methodName, methodSignature, pNativeCodeStartAddress, pConfig);
5293 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5294 TRACE_LEVEL_INFORMATION,
5295 CLR_JITTEDMETHODILTONATIVEMAP_KEYWORD))
5297 // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy
5298 // data has already been initialized.
5300 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
5301 // or profiler is loaded. So it should always be available.
5302 _ASSERTE(g_pDebugInterface != NULL);
5303 g_pDebugInterface->InitializeLazyDataIfNecessary();
5305 ETW::MethodLog::SendMethodILToNativeMapEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap, pNativeCodeStartAddress, pConfig->GetCodeVersion().GetILCodeVersionId());
5308 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5311 /*************************************************/
5312 /* This is called by the runtime when method jitting started */
5313 /*************************************************/
5314 VOID ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature)
5319 PRECONDITION(pMethodDesc != NULL);
5324 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5325 TRACE_LEVEL_VERBOSE,
5328 pMethodDesc->GetMethodInfo(*namespaceOrClassName, *methodName, *methodSignature);
5329 ETW::MethodLog::SendMethodJitStartEvent(pMethodDesc, namespaceOrClassName, methodName, methodSignature);
5331 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5334 /**********************************************************************/
5335 /* This is called by the runtime when a single jit helper method with stub is initialized */
5336 /**********************************************************************/
5337 VOID ETW::MethodLog::StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName)
5342 PRECONDITION(ullHelperStartAddress != 0);
5347 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5348 TRACE_LEVEL_INFORMATION,
5351 DWORD dwHelperSize=0;
5352 Stub::RecoverStubAndSize((TADDR)ullHelperStartAddress, &dwHelperSize);
5353 ETW::MethodLog::SendHelperEvent(ullHelperStartAddress, dwHelperSize, pHelperName);
5355 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5358 /**********************************************************/
5359 /* This is called by the runtime when helpers with stubs are initialized */
5360 /**********************************************************/
5361 VOID ETW::MethodLog::StubsInitialized(PVOID *pHelperStartAddress, PVOID *pHelperNames, LONG lNoOfHelpers)
5363 WRAPPER_NO_CONTRACT;
5365 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5366 TRACE_LEVEL_INFORMATION,
5369 for(int i=0; i<lNoOfHelpers; i++)
5371 if(pHelperStartAddress[i])
5373 StubInitialized((ULONGLONG)pHelperStartAddress[i], (LPCWSTR)pHelperNames[i]);
5379 /****************************************************************************/
5380 /* This is called by the runtime when a dynamic method is destroyed */
5381 /****************************************************************************/
5382 VOID ETW::MethodLog::DynamicMethodDestroyed(MethodDesc *pMethodDesc)
5391 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5392 TRACE_LEVEL_INFORMATION,
5394 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodUnload, TRUE);
5395 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5398 /****************************************************************************/
5399 /* This is called by the runtime when a ngen method is restored */
5400 /****************************************************************************/
5401 VOID ETW::MethodLog::MethodRestored(MethodDesc *pMethodDesc)
5410 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed()
5412 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5413 TRACE_LEVEL_INFORMATION,
5414 CLR_STARTENUMERATION_KEYWORD))
5416 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE);
5418 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5421 /****************************************************************************/
5422 /* This is called by the runtime when a method table is restored */
5423 /****************************************************************************/
5424 VOID ETW::MethodLog::MethodTableRestored(MethodTable *pMethodTable)
5432 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed()
5434 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5435 TRACE_LEVEL_INFORMATION,
5436 CLR_STARTENUMERATION_KEYWORD))
5439 MethodTable::MethodIterator iter(pMethodTable);
5440 for (; iter.IsValid(); iter.Next())
5442 MethodDesc *pMD = (MethodDesc *)(iter.GetMethodDesc());
5443 if(pMD && pMD->IsRestored() && pMD->GetMethodTable_NoLogging() == pMethodTable)
5444 ETW::MethodLog::SendMethodEvent(pMD, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE);
5448 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5452 /****************************************************************************/
5453 /* This is called by the runtime when a Strong Name Verification Starts */
5454 /****************************************************************************/
5455 VOID ETW::SecurityLog::StrongNameVerificationStart(DWORD dwInFlags, __in LPWSTR strFullyQualifiedAssemblyName)
5457 WRAPPER_NO_CONTRACT;
5461 /****************************************************************************/
5462 /* This is called by the runtime when a Strong Name Verification Ends */
5463 /****************************************************************************/
5464 VOID ETW::SecurityLog::StrongNameVerificationStop(DWORD dwInFlags,ULONG result, __in LPWSTR strFullyQualifiedAssemblyName)
5466 WRAPPER_NO_CONTRACT;
5469 /****************************************************************************/
5470 /* This is called by the runtime when field transparency calculations begin */
5471 /****************************************************************************/
5472 void ETW::SecurityLog::FireFieldTransparencyComputationStart(LPCWSTR wszFieldName,
5473 LPCWSTR wszModuleName,
5476 WRAPPER_NO_CONTRACT;
5477 FireEtwFieldTransparencyComputationStart(wszFieldName, wszModuleName, dwAppDomain, GetClrInstanceId());
5480 /****************************************************************************/
5481 /* This is called by the runtime when field transparency calculations end */
5482 /****************************************************************************/
5483 void ETW::SecurityLog::FireFieldTransparencyComputationEnd(LPCWSTR wszFieldName,
5484 LPCWSTR wszModuleName,
5487 BOOL fIsTreatAsSafe)
5489 WRAPPER_NO_CONTRACT;
5490 FireEtwFieldTransparencyComputationEnd(wszFieldName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5493 /*****************************************************************************/
5494 /* This is called by the runtime when method transparency calculations begin */
5495 /*****************************************************************************/
5496 void ETW::SecurityLog::FireMethodTransparencyComputationStart(LPCWSTR wszMethodName,
5497 LPCWSTR wszModuleName,
5500 WRAPPER_NO_CONTRACT;
5501 FireEtwMethodTransparencyComputationStart(wszMethodName, wszModuleName, dwAppDomain, GetClrInstanceId());
5504 /*****************************************************************************/
5505 /* This is called by the runtime when method transparency calculations end */
5506 /********************************************(********************************/
5507 void ETW::SecurityLog::FireMethodTransparencyComputationEnd(LPCWSTR wszMethodName,
5508 LPCWSTR wszModuleName,
5511 BOOL fIsTreatAsSafe)
5513 WRAPPER_NO_CONTRACT;
5514 FireEtwMethodTransparencyComputationEnd(wszMethodName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5517 /*****************************************************************************/
5518 /* This is called by the runtime when module transparency calculations begin */
5519 /*****************************************************************************/
5520 void ETW::SecurityLog::FireModuleTransparencyComputationStart(LPCWSTR wszModuleName,
5523 WRAPPER_NO_CONTRACT;
5524 FireEtwModuleTransparencyComputationStart(wszModuleName, dwAppDomain, GetClrInstanceId());
5527 /****************************************************************************/
5528 /* This is called by the runtime when module transparency calculations end */
5529 /****************************************************************************/
5530 void ETW::SecurityLog::FireModuleTransparencyComputationEnd(LPCWSTR wszModuleName,
5532 BOOL fIsAllCritical,
5533 BOOL fIsAllTransparent,
5534 BOOL fIsTreatAsSafe,
5535 BOOL fIsOpportunisticallyCritical,
5536 DWORD dwSecurityRuleSet)
5538 WRAPPER_NO_CONTRACT;
5539 FireEtwModuleTransparencyComputationEnd(wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsTreatAsSafe, fIsOpportunisticallyCritical, dwSecurityRuleSet, GetClrInstanceId());
5542 /****************************************************************************/
5543 /* This is called by the runtime when token transparency calculations begin */
5544 /****************************************************************************/
5545 void ETW::SecurityLog::FireTokenTransparencyComputationStart(DWORD dwToken,
5546 LPCWSTR wszModuleName,
5549 WRAPPER_NO_CONTRACT;
5550 FireEtwTokenTransparencyComputationStart(dwToken, wszModuleName, dwAppDomain, GetClrInstanceId());
5553 /****************************************************************************/
5554 /* This is called by the runtime when token transparency calculations end */
5555 /****************************************************************************/
5556 void ETW::SecurityLog::FireTokenTransparencyComputationEnd(DWORD dwToken,
5557 LPCWSTR wszModuleName,
5560 BOOL fIsTreatAsSafe)
5562 WRAPPER_NO_CONTRACT;
5563 FireEtwTokenTransparencyComputationEnd(dwToken, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5566 /*****************************************************************************/
5567 /* This is called by the runtime when type transparency calculations begin */
5568 /*****************************************************************************/
5569 void ETW::SecurityLog::FireTypeTransparencyComputationStart(LPCWSTR wszTypeName,
5570 LPCWSTR wszModuleName,
5573 WRAPPER_NO_CONTRACT;
5574 FireEtwTypeTransparencyComputationStart(wszTypeName, wszModuleName, dwAppDomain, GetClrInstanceId());
5577 /****************************************************************************/
5578 /* This is called by the runtime when type transparency calculations end */
5579 /****************************************************************************/
5580 void ETW::SecurityLog::FireTypeTransparencyComputationEnd(LPCWSTR wszTypeName,
5581 LPCWSTR wszModuleName,
5583 BOOL fIsAllCritical,
5584 BOOL fIsAllTransparent,
5586 BOOL fIsTreatAsSafe)
5588 WRAPPER_NO_CONTRACT;
5589 FireEtwTypeTransparencyComputationEnd(wszTypeName, wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5592 /**********************************************************************************/
5593 /* This is called by the runtime when a module is loaded */
5594 /* liReportedSharedModule will be 0 when this module is reported for the 1st time */
5595 /**********************************************************************************/
5596 VOID ETW::LoaderLog::ModuleLoad(Module *pModule, LONG liReportedSharedModule)
5605 DWORD enumerationOptions = ETW::EnumerationLog::EnumerationStructs::None;
5606 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5607 TRACE_LEVEL_INFORMATION,
5610 BOOL bTraceFlagLoaderSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5611 TRACE_LEVEL_INFORMATION,
5612 CLR_LOADER_KEYWORD);
5613 BOOL bTraceFlagNgenMethodSet = IsRuntimeNgenKeywordEnabledAndNotSuppressed();
5614 BOOL bTraceFlagStartRundownSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5615 TRACE_LEVEL_INFORMATION,
5616 CLR_STARTENUMERATION_KEYWORD);
5617 BOOL bTraceFlagPerfTrackSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
5618 TRACE_LEVEL_INFORMATION,
5619 CLR_PERFTRACK_KEYWORD);
5621 if(liReportedSharedModule == 0)
5624 if(bTraceFlagLoaderSet)
5625 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad;
5626 if (bTraceFlagPerfTrackSet)
5627 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad;
5628 if(bTraceFlagNgenMethodSet && bTraceFlagStartRundownSet)
5629 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad;
5631 if(pModule->IsManifest() && bTraceFlagLoaderSet)
5632 ETW::LoaderLog::SendAssemblyEvent(pModule->GetAssembly(), enumerationOptions);
5634 if(bTraceFlagLoaderSet || bTraceFlagPerfTrackSet)
5635 ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad | ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad);
5637 ETW::EnumerationLog::EnumerationHelper(pModule, NULL, enumerationOptions);
5640 // we want to report domainmodule events whenever they are loaded in any AppDomain
5641 if(bTraceFlagLoaderSet)
5642 ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad, TRUE);
5646 BOOL bTraceFlagPerfTrackPrivateSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context,
5647 TRACE_LEVEL_INFORMATION,
5648 CLR_PERFTRACK_PRIVATE_KEYWORD);
5649 if (liReportedSharedModule == 0 && bTraceFlagPerfTrackPrivateSet)
5651 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate;
5652 ETW::LoaderLog::SendModuleRange(pModule, enumerationOptions);
5655 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5658 /****************************************************************************/
5659 /* This is called by the runtime when the process is being shutdown */
5660 /****************************************************************************/
5661 VOID ETW::EnumerationLog::ProcessShutdown()
5670 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO))
5672 DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords();
5674 // Send unload events for all remaining domains, including shared domain and
5676 ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions);
5678 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5681 /****************************************************************************/
5682 /****************************************************************************/
5683 /* Begining of helper functions */
5684 /****************************************************************************/
5685 /****************************************************************************/
5687 /****************************************************************************/
5688 /* This routine is used to send a domain load/unload or rundown event */
5689 /****************************************************************************/
5690 VOID ETW::LoaderLog::SendDomainEvent(BaseDomain *pBaseDomain, DWORD dwEventOptions, LPCWSTR wszFriendlyName)
5700 PCWSTR szDtraceOutput1=W("");
5701 BOOL bIsAppDomain = pBaseDomain->IsAppDomain();
5703 ULONGLONG ullDomainId = (ULONGLONG)pBaseDomain;
5704 ULONG ulDomainFlags = ETW::LoaderLog::LoaderStructs::DefaultDomain | ETW::LoaderLog::LoaderStructs::ExecutableDomain;
5706 LPCWSTR wsEmptyString = W("");
5708 LPWSTR lpswzDomainName = (LPWSTR)wsEmptyString;
5711 lpswzDomainName = (PWCHAR)wszFriendlyName;
5713 lpswzDomainName = (PWCHAR)pBaseDomain->AsAppDomain()->GetFriendlyName();
5715 /* prepare events args for ETW and ETM */
5716 szDtraceOutput1 = (PCWSTR)lpswzDomainName;
5718 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
5720 FireEtwAppDomainLoad_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, DefaultADID, GetClrInstanceId());
5722 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
5724 FireEtwAppDomainUnload_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, DefaultADID, GetClrInstanceId());
5726 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
5728 FireEtwAppDomainDCStart_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, DefaultADID, GetClrInstanceId());
5730 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
5732 FireEtwAppDomainDCEnd_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, DefaultADID, GetClrInstanceId());
5736 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
5737 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
5738 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
5739 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
5743 /********************************************************/
5744 /* This routine is used to send thread rundown events when ARM is enabled */
5745 /********************************************************/
5746 VOID ETW::EnumerationLog::SendThreadRundownEvent()
5753 #ifndef DACCESS_COMPILE
5754 Thread *pThread = NULL;
5756 // Take the thread store lock while we enumerate threads.
5757 ThreadStoreLockHolder tsl;
5758 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
5760 if (pThread->IsUnstarted() || pThread->IsDead())
5763 // Send thread rundown provider events and thread created runtime provider
5764 // events (depending on which are enabled)
5765 ThreadLog::FireThreadDC(pThread);
5766 ThreadLog::FireThreadCreated(pThread);
5768 #endif // !DACCESS_COMPILE
5771 /****************************************************************************/
5772 /* This routine is used to send an assembly load/unload or rundown event ****/
5773 /****************************************************************************/
5775 VOID ETW::LoaderLog::SendAssemblyEvent(Assembly *pAssembly, DWORD dwEventOptions)
5785 PCWSTR szDtraceOutput1=W("");
5786 BOOL bIsDynamicAssembly = pAssembly->IsDynamic();
5787 BOOL bIsCollectibleAssembly = pAssembly->IsCollectible();
5788 BOOL bHasNativeImage = pAssembly->GetManifestFile()->HasNativeImage();
5789 BOOL bIsReadyToRun = pAssembly->GetManifestFile()->IsILImageReadyToRun();
5791 ULONGLONG ullAssemblyId = (ULONGLONG)pAssembly;
5792 ULONGLONG ullDomainId = (ULONGLONG)pAssembly->GetDomain();
5793 ULONGLONG ullBindingID = 0;
5794 ULONG ulAssemblyFlags = ((bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicAssembly : 0) |
5795 (bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeAssembly : 0) |
5796 (bIsCollectibleAssembly ? ETW::LoaderLog::LoaderStructs::CollectibleAssembly : 0) |
5797 (bIsReadyToRun ? ETW::LoaderLog::LoaderStructs::ReadyToRunAssembly : 0));
5799 SString sAssemblyPath;
5800 pAssembly->GetDisplayName(sAssemblyPath);
5801 LPWSTR lpszAssemblyPath = (LPWSTR)sAssemblyPath.GetUnicode();
5803 /* prepare events args for ETW and ETM */
5804 szDtraceOutput1 = (PCWSTR)lpszAssemblyPath;
5806 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
5808 FireEtwAssemblyLoad_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5810 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
5812 FireEtwAssemblyUnload_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5814 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
5816 FireEtwAssemblyDCStart_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5818 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
5820 FireEtwAssemblyDCEnd_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5824 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
5825 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
5826 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
5827 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
5833 ETW::LoaderLog::SendModuleRange(
5834 __in Module *pModule,
5835 __in DWORD dwEventOptions)
5838 ULONG Result = ERROR_SUCCESS;
5840 #ifdef FEATURE_PREJIT
5841 // do not fire the ETW event when:
5842 // 1. We did not load the native image
5843 // 2. We do not have IBC data for the native image
5844 if( !pModule || !pModule->HasNativeImage() || !pModule->IsIbcOptimized() )
5849 // get information about the hot sections from the native image that has been loaded
5850 COUNT_T cbSizeOfSectionTable;
5851 CORCOMPILE_VIRTUAL_SECTION_INFO* pVirtualSectionsTable = (CORCOMPILE_VIRTUAL_SECTION_INFO* )pModule->GetNativeImage()->GetVirtualSectionsTable(&cbSizeOfSectionTable);
5853 COUNT_T RangeCount = cbSizeOfSectionTable/sizeof(CORCOMPILE_VIRTUAL_SECTION_INFO);
5855 // if we do not have any hot ranges, we do not fire the ETW event
5857 // Figure out the rest of the event data
5858 UINT16 ClrInstanceId = GetClrInstanceId();
5859 UINT64 ModuleID = (ULONGLONG)(TADDR) pModule;
5861 for (COUNT_T i = 0; i < RangeCount; ++i)
5863 DWORD rangeBegin = pVirtualSectionsTable[i].VirtualAddress;
5864 DWORD rangeSize = pVirtualSectionsTable[i].Size;
5865 DWORD sectionType = pVirtualSectionsTable[i].SectionType;
5867 UINT8 ibcType = VirtualSectionData::IBCType(sectionType);
5868 UINT8 rangeType = VirtualSectionData::RangeType(sectionType);
5869 UINT16 virtualSectionType = VirtualSectionData::VirtualSectionType(sectionType);
5870 BOOL isIBCProfiledColdSection = VirtualSectionData::IsIBCProfiledColdSection(sectionType);
5871 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad)
5873 if (isIBCProfiledColdSection)
5874 Result &= FireEtwModuleRangeLoad(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5876 else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart)
5878 if (isIBCProfiledColdSection)
5879 Result &= FireEtwModuleRangeDCStart(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5881 else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd)
5883 if (isIBCProfiledColdSection)
5884 Result &= FireEtwModuleRangeDCEnd(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5886 // Fire private events if they are requested.
5887 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate)
5889 Result &= FireEtwModuleRangeLoadPrivate(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType, ibcType, virtualSectionType);
5897 //---------------------------------------------------------------------------------------
5899 // Helper that takes a module, and returns the managed and native PDB information
5900 // corresponding to that module. Used by the routine that fires the module load / unload
5904 // * pModule - Module to examine
5905 // * pCvInfoIL - [out] CV_INFO_PDB70 corresponding to managed PDB for this module
5906 // (the last debug directory entry in the PE File), if it exists. If it doesn't
5907 // exist, this is zeroed out.
5908 // * pCvInfoNative - [out] CV_INFO_PDB70 corresponding to native NGEN PDB for this
5909 // module (the next-to-last debug directory entry in the PE File), if it exists.
5910 // If it doesn't exist, this is zeroed out.
5913 // * This method only understands the CV_INFO_PDB70 / RSDS format. If the format
5914 // changes, this function will act as if there are no debug directory entries.
5915 // Module load / unload events will still be fired, but all PDB info will be
5917 // * The raw data in the PE file's debug directory entries are assumed to be
5918 // untrusted, and reported sizes of buffers are verified against their data.
5921 static void GetCodeViewInfo(Module * pModule, CV_INFO_PDB70 * pCvInfoIL, CV_INFO_PDB70 * pCvInfoNative)
5923 LIMITED_METHOD_CONTRACT;
5925 _ASSERTE (pModule != NULL);
5926 _ASSERTE (pCvInfoIL != NULL);
5927 _ASSERTE (pCvInfoNative != NULL);
5929 ZeroMemory(pCvInfoIL, sizeof(*pCvInfoIL));
5930 ZeroMemory(pCvInfoNative, sizeof(*pCvInfoNative));
5932 PTR_PEFile pPEFile = pModule->GetFile();
5933 _ASSERTE(pPEFile != NULL);
5935 PTR_PEImageLayout pLayout = NULL;
5936 if (pPEFile->HasNativeImage())
5938 pLayout = pPEFile->GetLoadedNative();
5940 else if (pPEFile->HasOpenedILimage())
5942 pLayout = pPEFile->GetLoadedIL();
5945 if (pLayout == NULL)
5947 // This can happen for reflection-loaded modules
5951 if (!pLayout->HasNTHeaders())
5953 // Without NT headers, we'll have a tough time finding the debug directory
5954 // entries. This can happen for nlp files.
5958 if (!pLayout->HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG))
5961 COUNT_T cbDebugEntries;
5962 IMAGE_DEBUG_DIRECTORY * rgDebugEntries =
5963 (IMAGE_DEBUG_DIRECTORY *) pLayout->GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugEntries);
5965 if (cbDebugEntries < sizeof(IMAGE_DEBUG_DIRECTORY))
5968 // Since rgDebugEntries is an array of IMAGE_DEBUG_DIRECTORYs, cbDebugEntries
5969 // should be a multiple of sizeof(IMAGE_DEBUG_DIRECTORY).
5970 if (cbDebugEntries % sizeof(IMAGE_DEBUG_DIRECTORY) != 0)
5973 // Temporary storage for a CV_INFO_PDB70 and its size (which could be less than
5974 // sizeof(CV_INFO_PDB70); see below).
5977 CV_INFO_PDB70 * m_pPdb70;
5981 // Iterate through all debug directory entries. The very last one will be the
5982 // managed PDB entry. The next to last one (if it exists) will be the (native) NGEN
5983 // PDB entry. Treat raw bytes we read as untrusted.
5984 PdbInfo pdbInfoLast = {0};
5985 PdbInfo pdbInfoNextToLast = {0};
5986 int cEntries = cbDebugEntries / sizeof(IMAGE_DEBUG_DIRECTORY);
5987 for (int i = 0; i < cEntries; i++)
5989 if (rgDebugEntries[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW)
5992 // Get raw data pointed to by this IMAGE_DEBUG_DIRECTORY
5994 // Some compilers set PointerToRawData but not AddressOfRawData as they put the
5995 // data at the end of the file in an unmapped part of the file
5996 RVA rvaOfRawData = (rgDebugEntries[i].AddressOfRawData != NULL) ?
5997 rgDebugEntries[i].AddressOfRawData :
5998 pLayout->OffsetToRva(rgDebugEntries[i].PointerToRawData);
6000 ULONG cbDebugData = rgDebugEntries[i].SizeOfData;
6001 if (cbDebugData < (offsetof(CV_INFO_PDB70, magic) + sizeof(((CV_INFO_PDB70*)0)->magic)))
6003 // raw data too small to contain magic number at expected spot, so its format
6004 // is not recognizeable. Skip
6008 if (!pLayout->CheckRva(rvaOfRawData, cbDebugData))
6010 // Memory claimed to belong to the raw data does not fit.
6011 // IMAGE_DEBUG_DIRECTORY is outright corrupt. Do not include PDB info in
6016 // Verify the magic number is as expected
6017 CV_INFO_PDB70 * pPdb70 = (CV_INFO_PDB70 *) pLayout->GetRvaData(rvaOfRawData);
6018 if (pPdb70->magic != CV_SIGNATURE_RSDS)
6020 // Unrecognized magic number. Skip
6024 // From this point forward, the format should adhere to the expected layout of
6025 // CV_INFO_PDB70. If we find otherwise, then assume the IMAGE_DEBUG_DIRECTORY is
6026 // outright corrupt, and do not include PDB info in event at all. The caller will
6027 // still fire the module event, but have zeroed-out / empty PDB fields.
6029 // Verify sane size of raw data
6030 if (cbDebugData > sizeof(CV_INFO_PDB70))
6033 // cbDebugData actually can be < sizeof(CV_INFO_PDB70), since the "path" field
6034 // can be truncated to its actual data length (i.e., fewer than MAX_LONGPATH chars
6035 // may be present in the PE file). In some cases, though, cbDebugData will
6036 // include all MAX_LONGPATH chars even though path gets null-terminated well before
6037 // the MAX_LONGPATH limit.
6039 // Gotta have at least one byte of the path
6040 if (cbDebugData < offsetof(CV_INFO_PDB70, path) + sizeof(char))
6043 // How much space is available for the path?
6044 size_t cchPathMaxIncludingNullTerminator = (cbDebugData - offsetof(CV_INFO_PDB70, path)) / sizeof(char);
6045 _ASSERTE(cchPathMaxIncludingNullTerminator >= 1); // Guaranteed above
6047 // Verify path string fits inside the declared size
6048 size_t cchPathActualExcludingNullTerminator = strnlen(pPdb70->path, cchPathMaxIncludingNullTerminator);
6049 if (cchPathActualExcludingNullTerminator == cchPathMaxIncludingNullTerminator)
6051 // This is how strnlen indicates failure--it couldn't find the null
6052 // terminator within the buffer size specified
6056 // Looks valid. Remember it.
6057 pdbInfoNextToLast = pdbInfoLast;
6058 pdbInfoLast.m_pPdb70 = pPdb70;
6059 pdbInfoLast.m_cbPdb70 = cbDebugData;
6062 // Return whatever we found
6064 if (pdbInfoLast.m_pPdb70 != NULL)
6066 // The last guy is the IL (managed) PDB info
6067 _ASSERTE(pdbInfoLast.m_cbPdb70 <= sizeof(*pCvInfoIL)); // Guaranteed by checks above
6068 memcpy(pCvInfoIL, pdbInfoLast.m_pPdb70, pdbInfoLast.m_cbPdb70);
6071 if (pdbInfoNextToLast.m_pPdb70 != NULL)
6073 // The next-to-last guy is the NGEN (native) PDB info
6074 _ASSERTE(pdbInfoNextToLast.m_cbPdb70 <= sizeof(*pCvInfoNative)); // Guaranteed by checks above
6075 memcpy(pCvInfoNative, pdbInfoNextToLast.m_pPdb70, pdbInfoNextToLast.m_cbPdb70);
6080 //---------------------------------------------------------------------------------------
6082 // send a module load/unload or rundown event and domainmodule load and rundown event
6085 // * pModule - Module loading or unloading
6086 // * dwEventOptions - Bitmask of which events to fire
6087 // * bFireDomainModuleEvents - nonzero if we are to fire DomainModule events; zero
6088 // if we are to fire Module events
6090 VOID ETW::LoaderLog::SendModuleEvent(Module *pModule, DWORD dwEventOptions, BOOL bFireDomainModuleEvents)
6100 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W("");
6101 BOOL bIsDynamicAssembly = pModule->GetAssembly()->IsDynamic();
6102 BOOL bHasNativeImage = FALSE;
6103 #ifdef FEATURE_PREJIT
6104 bHasNativeImage = pModule->HasNativeImage();
6105 #endif // FEATURE_PREJIT
6106 BOOL bIsManifestModule = pModule->IsManifest();
6107 ULONGLONG ullAppDomainId = 0; // This is used only with DomainModule events
6108 ULONGLONG ullModuleId = (ULONGLONG)(TADDR) pModule;
6109 ULONGLONG ullAssemblyId = (ULONGLONG)pModule->GetAssembly();
6110 BOOL bIsIbcOptimized = FALSE;
6113 bIsIbcOptimized = pModule->IsIbcOptimized();
6115 BOOL bIsReadyToRun = pModule->IsReadyToRun();
6116 BOOL bIsPartialReadyToRun = FALSE;
6119 bIsPartialReadyToRun = pModule->GetReadyToRunInfo()->IsPartial();
6121 ULONG ulReservedFlags = 0;
6122 ULONG ulFlags = ((bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeModule : 0) |
6123 (bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicModule : 0) |
6124 (bIsManifestModule ? ETW::LoaderLog::LoaderStructs::ManifestModule : 0) |
6125 (bIsIbcOptimized ? ETW::LoaderLog::LoaderStructs::IbcOptimized : 0) |
6126 (bIsReadyToRun ? ETW::LoaderLog::LoaderStructs::ReadyToRunModule : 0) |
6127 (bIsPartialReadyToRun ? ETW::LoaderLog::LoaderStructs::PartialReadyToRunModule : 0));
6129 // Grab PDB path, guid, and age for managed PDB and native (NGEN) PDB when
6130 // available. Any failures are not fatal. The corresponding PDB info will remain
6131 // zeroed out, and that's what we'll include in the event.
6132 CV_INFO_PDB70 cvInfoIL = {0};
6133 CV_INFO_PDB70 cvInfoNative = {0};
6134 GetCodeViewInfo(pModule, &cvInfoIL, &cvInfoNative);
6136 PWCHAR ModuleILPath=(PWCHAR)W(""), ModuleNativePath=(PWCHAR)W("");
6138 if(bFireDomainModuleEvents)
6140 if(pModule->GetDomain()->IsSharedDomain()) // for shared domains, we do not fire domainmodule event
6142 ullAppDomainId = (ULONGLONG)pModule->GetDomainAssembly()->GetAppDomain();
6145 LPCWSTR pEmptyString = W("");
6146 SString moduleName = SString::Empty();
6148 if(!bIsDynamicAssembly)
6150 ModuleILPath = (PWCHAR)pModule->GetAssembly()->GetManifestFile()->GetILimage()->GetPath().GetUnicode();
6151 ModuleNativePath = (PWCHAR)pEmptyString;
6153 #ifdef FEATURE_PREJIT
6155 ModuleNativePath = (PWCHAR)pModule->GetNativeImage()->GetPath().GetUnicode();
6156 #endif // FEATURE_PREJIT
6159 // if we do not have a module path yet, we put the module name
6160 if(bIsDynamicAssembly || ModuleILPath==NULL || wcslen(ModuleILPath) <= 2)
6162 moduleName.SetUTF8(pModule->GetSimpleName());
6163 ModuleILPath = (PWCHAR)moduleName.GetUnicode();
6164 ModuleNativePath = (PWCHAR)pEmptyString;
6167 /* prepare events args for ETW and ETM */
6168 szDtraceOutput1 = (PCWSTR)ModuleILPath;
6169 szDtraceOutput2 = (PCWSTR)ModuleNativePath;
6171 // Convert PDB paths to UNICODE
6172 StackSString managedPdbPath(SString::Utf8, cvInfoIL.path);
6173 StackSString nativePdbPath(SString::Utf8, cvInfoNative.path);
6175 if(bFireDomainModuleEvents)
6177 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
6179 FireEtwDomainModuleLoad_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6181 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
6183 FireEtwDomainModuleDCStart_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6185 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
6187 FireEtwDomainModuleDCEnd_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6191 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
6192 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
6193 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
6198 if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad))
6200 FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6202 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
6204 FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6206 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart))
6208 FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6210 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd))
6212 FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6216 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
6217 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
6218 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
6219 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
6220 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny));
6224 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny)
6226 // Fire ModuleRangeLoad, ModuleRangeDCStart, ModuleRangeDCEnd or ModuleRangeLoadPrivate event for this Module
6227 SendModuleRange(pModule, dwEventOptions);
6232 /*****************************************************************/
6233 /* This routine is used to send an ETW event just before a method starts jitting*/
6234 /*****************************************************************/
6235 VOID ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature)
6242 Module *pModule = NULL;
6243 Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID
6245 ULONGLONG ullMethodIdentifier=0;
6246 ULONGLONG ullModuleID=0;
6247 ULONG ulMethodToken=0;
6248 ULONG ulMethodILSize=0;
6249 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""),szDtraceOutput3=W("");
6252 pModule = pMethodDesc->GetModule_NoLogging();
6254 if(!pMethodDesc->IsRestored()) {
6258 bool bIsDynamicMethod = pMethodDesc->IsDynamicMethod();
6259 BOOL bIsGenericMethod = FALSE;
6260 if(pMethodDesc->GetMethodTable_NoLogging())
6261 bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging();
6263 ullModuleID = (ULONGLONG)(TADDR) pModule;
6264 ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6266 // Use MethodDesc if Dynamic or Generic methods
6267 if( bIsDynamicMethod || bIsGenericMethod)
6269 if(bIsGenericMethod)
6270 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6271 if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0
6272 ulMethodToken = (ULONG)0;
6275 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6277 if(pMethodDesc->IsIL())
6279 COR_ILMETHOD_DECODER::DecoderStatus decoderstatus = COR_ILMETHOD_DECODER::FORMAT_ERROR;
6280 COR_ILMETHOD_DECODER ILHeader(pMethodDesc->GetILHeader(), pMethodDesc->GetMDImport(), &decoderstatus);
6281 ulMethodILSize = (ULONG)ILHeader.GetCodeSize();
6284 SString tNamespace, tMethodName, tMethodSignature;
6285 if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty()))
6287 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
6288 namespaceOrClassName = &tNamespace;
6289 methodName = &tMethodName;
6290 methodSignature = &tMethodSignature;
6293 // fire method information
6294 /* prepare events args for ETW and ETM */
6295 szDtraceOutput1 = (PCWSTR)namespaceOrClassName->GetUnicode();
6296 szDtraceOutput2 = (PCWSTR)methodName->GetUnicode();
6297 szDtraceOutput3 = (PCWSTR)methodSignature->GetUnicode();
6299 FireEtwMethodJittingStarted_V1(ullMethodIdentifier,
6306 GetClrInstanceId());
6310 /****************************************************************************/
6311 /* This routine is used to send a method load/unload or rundown event */
6312 /****************************************************************************/
6313 VOID ETW::MethodLog::SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, PCODE pNativeCodeStartAddress, PrepareCodeConfig *pConfig)
6320 Module *pModule = NULL;
6321 Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID
6322 ULONGLONG ullMethodStartAddress=0, ullColdMethodStartAddress=0, ullModuleID=0, ullMethodIdentifier=0;
6323 ULONG ulMethodSize=0, ulColdMethodSize=0, ulMethodToken=0, ulMethodFlags=0, ulColdMethodFlags=0;
6324 PWCHAR pMethodName=NULL, pNamespaceName=NULL, pMethodSignature=NULL;
6325 BOOL bHasNativeImage = FALSE, bShowVerboseOutput = FALSE, bIsDynamicMethod = FALSE, bHasSharedGenericCode = FALSE, bIsGenericMethod = FALSE;
6326 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""),szDtraceOutput3=W("");
6328 BOOL bIsRundownProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6329 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6330 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
6331 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd));
6333 BOOL bIsRuntimeProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6334 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6335 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
6336 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload));
6338 if (pMethodDesc == NULL)
6341 if(!pMethodDesc->IsRestored())
6343 // Forcibly restoring ngen methods can cause all sorts of deadlocks and contract violations
6344 // These events are therefore put under the private provider
6345 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context,
6346 TRACE_LEVEL_INFORMATION,
6347 CLR_PRIVATENGENFORCERESTORE_KEYWORD))
6349 PERMANENT_CONTRACT_VIOLATION(GCViolation, ReasonNonShippingCode);
6350 pMethodDesc->CheckRestore();
6359 if(bIsRundownProvider)
6361 bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_DOTNET_Context,
6362 TRACE_LEVEL_VERBOSE,
6365 else if(bIsRuntimeProvider)
6367 bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context,
6368 TRACE_LEVEL_VERBOSE,
6372 pModule = pMethodDesc->GetModule_NoLogging();
6373 #ifdef FEATURE_PREJIT
6374 bHasNativeImage = pModule->HasNativeImage();
6375 #endif // FEATURE_PREJIT
6376 bIsDynamicMethod = (BOOL)pMethodDesc->IsDynamicMethod();
6377 bHasSharedGenericCode = pMethodDesc->IsSharedByGenericInstantiations();
6379 if(pMethodDesc->GetMethodTable_NoLogging())
6380 bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging();
6382 NativeCodeVersionId nativeCodeId = 0;
6383 ulMethodFlags = ulMethodFlags |
6384 (bHasSharedGenericCode ? ETW::MethodLog::MethodStructs::SharedGenericCode : 0) |
6385 (bIsGenericMethod ? ETW::MethodLog::MethodStructs::GenericMethod : 0) |
6386 (bIsDynamicMethod ? ETW::MethodLog::MethodStructs::DynamicMethod : 0) |
6387 (bIsJit ? ETW::MethodLog::MethodStructs::JittedMethod : 0);
6388 if (pConfig != nullptr)
6390 if (pConfig->ProfilerRejectedPrecompiledCode())
6392 ulMethodFlags |= ETW::MethodLog::MethodStructs::ProfilerRejectedPrecompiledCode;
6394 if (pConfig->ReadyToRunRejectedPrecompiledCode())
6396 ulMethodFlags |= ETW::MethodLog::MethodStructs::ReadyToRunRejectedPrecompiledCode;
6399 #ifdef FEATURE_CODE_VERSIONING
6400 nativeCodeId = pConfig->GetCodeVersion().GetVersionId();
6404 unsigned int jitOptimizationTier = (unsigned int)PrepareCodeConfig::GetJitOptimizationTier(pConfig, pMethodDesc);
6405 static_assert_no_msg((unsigned int)PrepareCodeConfig::JitOptimizationTier::Count - 1 <= MethodFlagsJitOptimizationTierLowMask);
6406 _ASSERTE(jitOptimizationTier <= MethodFlagsJitOptimizationTierLowMask);
6407 _ASSERTE(((ulMethodFlags >> MethodFlagsJitOptimizationTierShift) & MethodFlagsJitOptimizationTierLowMask) == 0);
6408 ulMethodFlags |= jitOptimizationTier << MethodFlagsJitOptimizationTierShift;
6410 // Intentionally set the extent flags (cold vs. hot) only after all the other common
6411 // flags (above) have been set.
6412 ulColdMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::ColdSection; // Method Extent (bits 28, 29, 30, 31)
6413 ulMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::HotSection; // Method Extent (bits 28, 29, 30, 31)
6415 // MethodDesc ==> Code Address ==>JitMananger
6416 TADDR start = PCODEToPINSTR(pNativeCodeStartAddress ? pNativeCodeStartAddress : pMethodDesc->GetNativeCode());
6418 // this method hasn't been jitted
6422 // EECodeInfo is technically initialized by a "PCODE", but it can also be initialized
6423 // by a TADDR (i.e., w/out thumb bit set on ARM)
6424 EECodeInfo codeInfo(start);
6426 // MethodToken ==> MethodRegionInfo
6427 IJitManager::MethodRegionInfo methodRegionInfo;
6428 codeInfo.GetMethodRegionInfo(&methodRegionInfo);
6430 ullMethodStartAddress = (ULONGLONG)methodRegionInfo.hotStartAddress;
6431 ulMethodSize = (ULONG)methodRegionInfo.hotSize;
6433 ullModuleID = (ULONGLONG)(TADDR) pModule;
6434 ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6436 // Use MethodDesc if Dynamic or Generic methods
6437 if( bIsDynamicMethod || bIsGenericMethod)
6439 bShowVerboseOutput = TRUE;
6440 if(bIsGenericMethod)
6441 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6442 if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0
6443 ulMethodToken = (ULONG)0;
6446 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6450 ullColdMethodStartAddress = (ULONGLONG)methodRegionInfo.coldStartAddress;
6451 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
6454 SString tNamespace, tMethodName, tMethodSignature;
6456 // if verbose method load info needed, only then
6457 // find method name and signature and fire verbose method load info
6458 if(bShowVerboseOutput)
6460 if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty()))
6462 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
6463 namespaceOrClassName = &tNamespace;
6464 methodName = &tMethodName;
6465 methodSignature = &tMethodSignature;
6467 pNamespaceName = (PWCHAR)namespaceOrClassName->GetUnicode();
6468 pMethodName = (PWCHAR)methodName->GetUnicode();
6469 pMethodSignature = (PWCHAR)methodSignature->GetUnicode();
6472 BOOL bFireEventForColdSection = (bHasNativeImage && ullColdMethodStartAddress && ulColdMethodSize);
6474 /* prepare events args for ETW and ETM */
6475 szDtraceOutput1 = (PCWSTR)pNamespaceName;
6476 szDtraceOutput2 = (PCWSTR)pMethodName;
6477 szDtraceOutput3 = (PCWSTR)pMethodSignature;
6479 if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6480 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad))
6482 if(bShowVerboseOutput)
6484 FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier,
6486 ullMethodStartAddress,
6498 FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier,
6500 ullMethodStartAddress,
6507 if(bFireEventForColdSection)
6509 if(bShowVerboseOutput)
6511 FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier,
6513 ullColdMethodStartAddress,
6525 FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier,
6527 ullColdMethodStartAddress,
6536 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6537 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload))
6539 if(bShowVerboseOutput)
6541 FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier,
6543 ullMethodStartAddress,
6555 FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier,
6557 ullMethodStartAddress,
6564 if(bFireEventForColdSection)
6566 if(bShowVerboseOutput)
6568 FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier,
6570 ullColdMethodStartAddress,
6582 FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier,
6584 ullColdMethodStartAddress,
6593 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6594 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart))
6596 if(bShowVerboseOutput)
6598 FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier,
6600 ullMethodStartAddress,
6612 FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier,
6614 ullMethodStartAddress,
6621 if(bFireEventForColdSection)
6623 if(bShowVerboseOutput)
6625 FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier,
6627 ullColdMethodStartAddress,
6639 FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier,
6641 ullColdMethodStartAddress,
6650 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6651 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd))
6653 if(bShowVerboseOutput)
6655 FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier,
6657 ullMethodStartAddress,
6669 FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier,
6671 ullMethodStartAddress,
6678 if(bFireEventForColdSection)
6680 if(bShowVerboseOutput)
6682 FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier,
6684 ullColdMethodStartAddress,
6696 FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier,
6698 ullColdMethodStartAddress,
6709 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6710 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6711 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6712 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6713 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
6714 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) ||
6715 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
6716 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd));
6720 //---------------------------------------------------------------------------------------
6722 // Fires the IL-to-native map event for JITted methods. This is used for the runtime,
6723 // rundown start, and rundown end events that include the il-to-native map information
6726 // pMethodDesc - MethodDesc for which we'll fire the map event
6727 // dwEventOptions - Options that tells us, in the rundown case, whether we're
6728 // supposed to fire the start or end rundown events.
6732 VOID ETW::MethodLog::SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, PCODE pNativeCodeStartAddress, ReJITID ilCodeId)
6741 // This is the limit on how big the il-to-native map can get, as measured by number
6742 // of entries in each parallel array (IL offset array and native offset array).
6743 // This number was chosen to ensure the overall event stays under the Windows limit
6745 const USHORT kMapEntriesMax = 7000;
6747 if (pMethodDesc == NULL)
6750 if (pMethodDesc->HasClassOrMethodInstantiation() && pMethodDesc->IsTypicalMethodDefinition())
6753 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
6754 // or profiler is loaded. So it should always be available.
6755 _ASSERTE(g_pDebugInterface != NULL);
6757 ULONGLONG ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6760 NewArrayHolder<UINT> rguiILOffset;
6761 NewArrayHolder<UINT> rguiNativeOffset;
6763 HRESULT hr = g_pDebugInterface->GetILToNativeMappingIntoArrays(
6765 pNativeCodeStartAddress,
6773 // Runtime provider.
6775 // This macro already checks for the JittedMethodILToNativeMapKeyword before
6776 // choosing to fire the event
6777 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap) != 0)
6779 FireEtwMethodILToNativeMap(
6780 ullMethodIdentifier,
6782 0, // Extent: This event is only sent for JITted (not NGENd) methods, and
6783 // currently there is only one extent (hot) for JITted methods.
6787 GetClrInstanceId());
6792 // These macros already check for the JittedMethodILToNativeMapRundownKeyword
6793 // before choosing to fire the event--we further check our options to see if we
6794 // should fire the Start and / or End flavor of the event (since the keyword alone
6795 // is insufficient to distinguish these).
6797 // (for an explanation of the parameters see the FireEtwMethodILToNativeMap call above)
6798 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap) != 0)
6799 FireEtwMethodDCStartILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId());
6800 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap) != 0)
6801 FireEtwMethodDCEndILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId());
6805 VOID ETW::MethodLog::SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName)
6807 WRAPPER_NO_CONTRACT;
6810 PCWSTR szDtraceOutput1=W("");
6811 ULONG methodFlags = ETW::MethodLog::MethodStructs::JitHelperMethod; // helper flag set
6812 FireEtwMethodLoadVerbose_V1(ullHelperStartAddress,
6814 ullHelperStartAddress,
6821 GetClrInstanceId());
6826 /****************************************************************************/
6827 /* This routine sends back method events of type 'dwEventOptions', for all
6828 NGEN methods in pModule */
6829 /****************************************************************************/
6830 VOID ETW::MethodLog::SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions)
6840 #ifdef FEATURE_READYTORUN
6841 if (pModule->IsReadyToRun())
6843 ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo());
6846 // Call GetMethodDesc_NoRestore instead of GetMethodDesc to avoid restoring methods at shutdown.
6847 MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc_NoRestore();
6848 if (hotDesc != NULL)
6850 ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
6856 #endif // FEATURE_READYTORUN
6858 #ifdef FEATURE_PREJIT
6859 if (pModule->HasNativeImage())
6861 MethodIterator mi(pModule);
6865 MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc();
6866 ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
6869 #endif // FEATURE_PREJIT
6872 // Called be ETW::MethodLog::SendEventsForJitMethods
6873 // Sends the ETW events once our caller determines whether or not rejit locks can be acquired
6874 VOID ETW::MethodLog::SendEventsForJitMethodsHelper(LoaderAllocator *pLoaderAllocatorFilter,
6875 DWORD dwEventOptions,
6876 BOOL fLoadOrDCStart,
6877 BOOL fUnloadOrDCEnd,
6878 BOOL fSendMethodEvent,
6879 BOOL fSendILToNativeMapEvent,
6887 _ASSERTE(pLoaderAllocatorFilter == nullptr || pLoaderAllocatorFilter->IsCollectible());
6888 _ASSERTE(pLoaderAllocatorFilter == nullptr || !fGetCodeIds);
6890 EEJitManager::CodeHeapIterator heapIterator(pLoaderAllocatorFilter);
6891 while (heapIterator.Next())
6893 MethodDesc * pMD = heapIterator.GetMethod();
6897 PCODE codeStart = PINSTRToPCODE(heapIterator.GetMethodCode());
6899 // Get info relevant to the native code version. In some cases, such as collectible loader
6900 // allocators, we don't support code versioning so we need to short circuit the call.
6901 // This also allows our caller to avoid having to pre-enter the relevant locks.
6902 // see code:#TableLockHolder
6903 ReJITID ilCodeId = 0;
6904 NativeCodeVersion nativeCodeVersion;
6905 #ifdef FEATURE_CODE_VERSIONING
6908 CodeVersionManager *pCodeVersionManager = pMD->GetCodeVersionManager();
6909 _ASSERTE(pCodeVersionManager->LockOwnedByCurrentThread());
6910 nativeCodeVersion = pCodeVersionManager->GetNativeCodeVersion(pMD, codeStart);
6911 if (nativeCodeVersion.IsNull())
6913 // The code version manager hasn't been updated with the jitted code
6914 if (codeStart != pMD->GetNativeCode())
6921 ilCodeId = nativeCodeVersion.GetILCodeVersionId();
6926 if (codeStart != pMD->GetNativeCode())
6931 PrepareCodeConfig config(!nativeCodeVersion.IsNull() ? nativeCodeVersion : NativeCodeVersion(pMD), FALSE, FALSE);
6933 // When we're called to announce loads, then the methodload event itself must
6934 // precede any supplemental events, so that the method load or method jitting
6935 // event is the first event the profiler sees for that MethodID (and not, say,
6936 // the MethodILToNativeMap event.)
6939 if (fSendMethodEvent)
6941 ETW::MethodLog::SendMethodEvent(
6945 NULL, // namespaceOrClassName
6947 NULL, // methodSignature
6953 // Send any supplemental events requested for this MethodID
6954 if (fSendILToNativeMapEvent)
6955 ETW::MethodLog::SendMethodILToNativeMapEvent(pMD, dwEventOptions, codeStart, ilCodeId);
6957 // When we're called to announce unloads, then the methodunload event itself must
6958 // come after any supplemental events, so that the method unload event is the
6959 // last event the profiler sees for this MethodID
6962 if (fSendMethodEvent)
6964 ETW::MethodLog::SendMethodEvent(
6968 NULL, // namespaceOrClassName
6970 NULL, // methodSignature
6978 /****************************************************************************/
6979 /* This routine sends back method events of type 'dwEventOptions', for all
6980 JITed methods in either a given LoaderAllocator (if pLoaderAllocatorFilter is non NULL)
6981 or in a given Domain (if pDomainFilter is non NULL) or for
6982 all methods (if both filters are null) */
6983 /****************************************************************************/
6984 // Code review indicates this method is never called with both filters NULL. Ideally we would
6985 // assert this and change the comment above, but given I am making a change late in the release I am being cautious
6986 VOID ETW::MethodLog::SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAllocator *pLoaderAllocatorFilter, DWORD dwEventOptions)
6993 #if !defined(DACCESS_COMPILE)
6996 // This is only called for JITted methods loading xor unloading
6997 BOOL fLoadOrDCStart = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny) != 0);
6998 BOOL fUnloadOrDCEnd = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny) != 0);
6999 _ASSERTE((fLoadOrDCStart || fUnloadOrDCEnd) && !(fLoadOrDCStart && fUnloadOrDCEnd));
7001 BOOL fSendMethodEvent =
7003 (ETW::EnumerationLog::EnumerationStructs::JitMethodLoad |
7004 ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart |
7005 ETW::EnumerationLog::EnumerationStructs::JitMethodUnload |
7006 ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd)) != 0;
7008 BOOL fSendILToNativeMapEvent =
7010 (ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap |
7011 ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap)) != 0;
7013 if (fSendILToNativeMapEvent)
7015 // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy
7016 // data has already been initialized, to ensure we don't try to do the lazy init
7017 // while under the implicit, notrigger CodeHeapIterator lock below.
7019 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
7020 // or profiler is loaded. So it should always be available.
7021 _ASSERTE(g_pDebugInterface != NULL);
7022 g_pDebugInterface->InitializeLazyDataIfNecessary();
7025 // #TableLockHolder:
7027 // A word about ReJitManager::TableLockHolder... As we enumerate through the functions,
7028 // we may need to grab their code IDs. The ReJitManager grabs its table Crst in order to
7029 // fetch these. However, several other kinds of locks are being taken during this
7030 // enumeration, such as the SystemDomain lock and the EEJitManager::CodeHeapIterator's
7031 // lock. In order to avoid lock-leveling issues, we grab the appropriate ReJitManager
7032 // table locks after SystemDomain and before CodeHeapIterator. In particular, we need to
7033 // grab the SharedDomain's ReJitManager table lock as well as the specific AppDomain's
7034 // ReJitManager table lock for the current AppDomain we're iterating. Why the SharedDomain's
7035 // ReJitManager lock? For any given AppDomain we're iterating over, the MethodDescs we
7036 // find may be managed by that AppDomain's ReJitManger OR the SharedDomain's ReJitManager.
7037 // (This is due to generics and whether given instantiations may be shared based on their
7038 // arguments.) Therefore, we proactively take the SharedDomain's ReJitManager's table
7039 // lock up front, and then individually take the appropriate AppDomain's ReJitManager's
7040 // table lock that corresponds to the domain or module we're currently iterating over.
7043 // We only support getting rejit IDs when filtering by domain.
7044 #ifdef FEATURE_CODE_VERSIONING
7047 CodeVersionManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetCodeVersionManager());
7048 SendEventsForJitMethodsHelper(
7049 pLoaderAllocatorFilter,
7054 fSendILToNativeMapEvent,
7060 SendEventsForJitMethodsHelper(
7061 pLoaderAllocatorFilter,
7066 fSendILToNativeMapEvent,
7069 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
7070 #endif // !DACCESS_COMPILE
7073 //---------------------------------------------------------------------------------------
7075 // Wrapper around IterateDomain, which locks the AppDomain to be <
7076 // STAGE_FINALIZED until the iteration is complete.
7079 // pAppDomain - AppDomain to iterate
7080 // enumerationOptions - Flags indicating what to enumerate. Just passed
7081 // straight through to IterateDomain
7083 VOID ETW::EnumerationLog::IterateAppDomain(AppDomain * pAppDomain, DWORD enumerationOptions)
7089 PRECONDITION(pAppDomain != NULL);
7093 // Hold the system domain lock during the entire iteration, so we can
7094 // ensure the App Domain does not get finalized until we're all done
7095 SystemDomain::LockHolder lh;
7097 // Now it's safe to do the iteration
7098 IterateDomain(pAppDomain, enumerationOptions);
7101 /********************************************************************************/
7102 /* This routine fires ETW events for
7105 DomainModule's in them,
7107 JIT methods in them,
7108 and the NGEN methods in them
7109 based on enumerationOptions.*/
7110 /********************************************************************************/
7111 VOID ETW::EnumerationLog::IterateDomain(BaseDomain *pDomain, DWORD enumerationOptions)
7116 PRECONDITION(pDomain != NULL);
7119 #if defined(_DEBUG) && !defined(DACCESS_COMPILE)
7120 // Do not call IterateDomain() directly with an AppDomain. Use
7121 // IterateAppDomain(), whch wraps this function with a hold on the
7122 // SystemDomain lock, which ensures pDomain's type data doesn't disappear
7124 if (pDomain->IsAppDomain())
7126 _ASSERTE(SystemDomain::IsUnderDomainLock());
7128 #endif // defined(_DEBUG) && !defined(DACCESS_COMPILE)
7132 // DC Start events for Domain
7133 if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7135 ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions);
7138 // DC End or Unload Jit Method events
7139 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny)
7141 ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions);
7144 AppDomain::AssemblyIterator assemblyIterator = pDomain->AsAppDomain()->IterateAssembliesEx(
7145 (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
7146 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
7147 while (assemblyIterator.Next(pDomainAssembly.This()))
7149 CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
7150 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7152 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7155 DomainModuleIterator domainModuleIterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
7156 while (domainModuleIterator.Next())
7158 Module * pModule = domainModuleIterator.GetModule();
7159 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7162 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7163 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7165 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7169 // DC Start or Load Jit Method events
7170 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny)
7172 ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions);
7175 // DC End or Unload events for Domain
7176 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7177 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7179 ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions);
7181 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7185 /********************************************************************************/
7186 /* This routine fires ETW events for
7187 Assembly in LoaderAllocator,
7188 DomainModule's in them,
7190 JIT methods in them,
7191 and the NGEN methods in them
7192 based on enumerationOptions.*/
7193 /********************************************************************************/
7194 VOID ETW::EnumerationLog::IterateCollectibleLoaderAllocator(AssemblyLoaderAllocator *pLoaderAllocator, DWORD enumerationOptions)
7199 PRECONDITION(pLoaderAllocator != NULL);
7204 // Unload Jit Method events
7205 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload)
7207 ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions);
7210 // Iterate on all DomainAssembly loaded from the same AssemblyLoaderAllocator
7211 DomainAssemblyIterator domainAssemblyIt = pLoaderAllocator->Id()->GetDomainAssemblyIterator();
7212 while (!domainAssemblyIt.end())
7214 Assembly *pAssembly = domainAssemblyIt->GetAssembly(); // TODO: handle iterator
7216 DomainModuleIterator domainModuleIterator = domainAssemblyIt->IterateModules(kModIterIncludeLoaded);
7217 while (domainModuleIterator.Next())
7219 Module *pModule = domainModuleIterator.GetModule();
7220 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7223 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
7225 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7231 // Load Jit Method events
7232 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad)
7234 ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions);
7236 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7239 /********************************************************************************/
7240 /* This routine fires ETW events for Assembly and the DomainModule's in them
7241 based on enumerationOptions.*/
7242 /********************************************************************************/
7243 VOID ETW::EnumerationLog::IterateAssembly(Assembly *pAssembly, DWORD enumerationOptions)
7248 PRECONDITION(pAssembly != NULL);
7253 // DC Start events for Assembly
7254 if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7256 ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions);
7259 // DC Start, DCEnd, events for DomainModule
7260 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7261 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart))
7263 if(pAssembly->GetDomain()->IsAppDomain())
7265 DomainModuleIterator dmIterator = pAssembly->GetDomainAssembly()->IterateModules(kModIterIncludeLoaded);
7266 while (dmIterator.Next())
7268 ETW::LoaderLog::SendModuleEvent(dmIterator.GetModule(), enumerationOptions, TRUE);
7273 // DC End or Unload events for Assembly
7274 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7275 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7277 ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions);
7279 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7282 /********************************************************************************/
7283 /* This routine fires ETW events for Module, their range information and the NGEN methods in them
7284 based on enumerationOptions.*/
7285 /********************************************************************************/
7286 VOID ETW::EnumerationLog::IterateModule(Module *pModule, DWORD enumerationOptions)
7291 PRECONDITION(pModule != NULL);
7296 // DC Start events for Module
7297 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
7298 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart))
7300 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7303 // DC Start or Load or DC End or Unload Ngen Method events
7304 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
7305 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
7306 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) ||
7307 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd))
7309 ETW::MethodLog::SendEventsForNgenMethods(pModule, enumerationOptions);
7312 // DC End or Unload events for Module
7313 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7314 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
7315 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd))
7317 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7320 // If we're logging types, then update the internal Type hash table to account
7321 // for the module's unloading
7322 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::TypeUnload)
7324 ETW::TypeSystemLog::OnModuleUnload(pModule);
7327 // ModuleRangeLoadPrivate events for module range information from attach/detach scenarios
7328 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context,
7329 TRACE_LEVEL_INFORMATION,
7330 CLR_PERFTRACK_PRIVATE_KEYWORD) &&
7331 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate))
7333 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7335 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7338 //---------------------------------------------------------------------------------------
7340 // This routine sends back domain, assembly, module and method events based on
7341 // enumerationOptions.
7344 // * moduleFilter - if non-NULL, events from only moduleFilter module are reported
7345 // * domainFilter - if non-NULL, events from only domainFilter domain are reported
7346 // * enumerationOptions - Flags from ETW::EnumerationLog::EnumerationStructs which
7347 // describe which events should be sent.
7350 // * if all filter args are NULL, events from all domains are reported
7355 VOID ETW::EnumerationLog::EnumerationHelper(Module *moduleFilter, BaseDomain *domainFilter, DWORD enumerationOptions)
7362 // Disable IBC logging during ETW enumeration since we call a lot of functionality
7363 // that does logging and causes problems in the shutdown path due to critical
7364 // section access for IBC logging
7365 IBCLoggingDisabler disableLogging;
7369 // Iterate modules first because their number is usually smaller then the number of methods.
7370 // Thus hitting a timeout due to a large number of methods will not affect modules rundown.tf g
7371 ETW::EnumerationLog::IterateModule(moduleFilter, enumerationOptions);
7373 // As best I can tell from code review, these if statements below are never true. There is
7374 // only one caller to this method that specifies a moduleFilter, ETW::LoaderLog::ModuleLoad.
7375 // That method never specifies these flags. Because it is late in a release cycle I am not
7376 // making a change, but if you see this comment early in the next release cycle consider
7377 // deleting this apparently dead code.
7379 // DC End or Unload Jit Method events from all Domains
7380 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny)
7382 ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions);
7385 // DC Start or Load Jit Method events from all Domains
7386 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny)
7388 ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions);
7395 if(domainFilter->IsAppDomain())
7397 ETW::EnumerationLog::IterateAppDomain(domainFilter->AsAppDomain(), enumerationOptions);
7401 ETW::EnumerationLog::IterateDomain(domainFilter, enumerationOptions);
7406 AppDomainIterator appDomainIterator(FALSE);
7407 while(appDomainIterator.Next())
7409 AppDomain *pDomain = appDomainIterator.GetDomain();
7410 if (pDomain != NULL)
7412 ETW::EnumerationLog::IterateAppDomain(pDomain, enumerationOptions);
7419 void ETW::CompilationLog::TieredCompilation::GetSettings(UINT32 *flagsRef)
7425 _ASSERTE(g_pConfig->TieredCompilation());
7426 _ASSERTE(flagsRef != nullptr);
7428 enum class Flags : UINT32
7432 QuickJitForLoops = 0x2,
7435 UINT32 flags = (UINT32)Flags::None;
7436 if (g_pConfig->TieredCompilation_QuickJit())
7438 flags |= (UINT32)Flags::QuickJit;
7439 if (g_pConfig->TieredCompilation_QuickJitForLoops())
7441 flags |= (UINT32)Flags::QuickJitForLoops;
7447 void ETW::CompilationLog::TieredCompilation::Runtime::SendSettings()
7453 _ASSERTE(g_pConfig->TieredCompilation());
7456 GetSettings(&flags);
7458 FireEtwTieredCompilationSettings(GetClrInstanceId(), flags);
7461 void ETW::CompilationLog::TieredCompilation::Rundown::SendSettings()
7467 _ASSERTE(g_pConfig->TieredCompilation());
7470 GetSettings(&flags);
7472 FireEtwTieredCompilationSettingsDCStart(GetClrInstanceId(), flags);
7475 void ETW::CompilationLog::TieredCompilation::Runtime::SendPause()
7481 _ASSERTE(g_pConfig->TieredCompilation());
7483 FireEtwTieredCompilationPause(GetClrInstanceId());
7486 void ETW::CompilationLog::TieredCompilation::Runtime::SendResume(UINT32 newMethodCount)
7492 _ASSERTE(g_pConfig->TieredCompilation());
7494 FireEtwTieredCompilationResume(GetClrInstanceId(), newMethodCount);
7497 void ETW::CompilationLog::TieredCompilation::Runtime::SendBackgroundJitStart(UINT32 pendingMethodCount)
7503 _ASSERTE(g_pConfig->TieredCompilation());
7505 FireEtwTieredCompilationBackgroundJitStart(GetClrInstanceId(), pendingMethodCount);
7508 void ETW::CompilationLog::TieredCompilation::Runtime::SendBackgroundJitStop(UINT32 pendingMethodCount, UINT32 jittedMethodCount)
7514 _ASSERTE(g_pConfig->TieredCompilation());
7516 FireEtwTieredCompilationBackgroundJitStop(GetClrInstanceId(), pendingMethodCount, jittedMethodCount);
7519 #endif // !FEATURE_REDHAWK
7521 #ifdef FEATURE_PERFTRACING
7522 #include "eventpipe.h"
7523 bool EventPipeHelper::Enabled()
7525 LIMITED_METHOD_CONTRACT;
7526 return EventPipe::Enabled();
7529 bool EventPipeHelper::IsEnabled(DOTNET_TRACE_CONTEXT Context, UCHAR Level, ULONGLONG Keyword)
7538 if (Level <= Context.EventPipeProvider.Level || Context.EventPipeProvider.Level == 0)
7540 return (Keyword == (ULONGLONG)0) || (Keyword & Context.EventPipeProvider.EnabledKeywordsBitmask) != 0;
7545 #endif // FEATURE_PERFTRACING