1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// File: eventtrace.cpp
6// Abstract: This module implements Event Tracing support
7//
8
9//
10
11//
12// ============================================================================
13
14#include "common.h"
15
16#ifdef FEATURE_REDHAWK
17
18#include "commontypes.h"
19#include "daccess.h"
20#include "debugmacrosext.h"
21#include "palredhawkcommon.h"
22#include "gcrhenv.h"
23#define Win32EventWrite PalEtwEventWrite
24#define InterlockedExchange64 PalInterlockedExchange64
25
26#else // !FEATURE_REDHAWK
27
28#include "eventtrace.h"
29#include "winbase.h"
30#include "contract.h"
31#include "ex.h"
32#include "dbginterface.h"
33#include "finalizerthread.h"
34
35#define Win32EventWrite EventWrite
36
37#ifdef FEATURE_COMINTEROP
38#include "comcallablewrapper.h"
39#include "runtimecallablewrapper.h"
40#endif
41
42// Flags used to store some runtime information for Event Tracing
43BOOL g_fEEOtherStartup=FALSE;
44BOOL g_fEEComActivatedStartup=FALSE;
45GUID g_EEComObjectGuid=GUID_NULL;
46
47BOOL g_fEEHostedStartup = FALSE;
48
49#endif // FEATURE_REDHAWK
50
51#include "eventtracepriv.h"
52
53#ifdef FEATURE_REDHAWK
54volatile LONGLONG ETW::GCLog::s_l64LastClientSequenceNumber = 0;
55#else // FEATURE_REDHAWK
56Volatile<LONGLONG> ETW::GCLog::s_l64LastClientSequenceNumber = 0;
57#endif // FEATURE_REDHAWK
58
59#ifndef FEATURE_REDHAWK
60
61//---------------------------------------------------------------------------------------
62// Helper macros to determine which version of the Method events to use
63//
64// The V2 versions of these events include the ReJITID, the V1 versions do not.
65// Historically, when we version events, we'd just stop sending the old version and only
66// send the new one. However, now that we have xperf in heavy use internally and soon to be
67// used externally, we need to be a bit careful. In particular, we'd like to allow
68// current xperf to continue working without knowledge of ReJITIDs, and allow future
69// xperf to decode symbols in ReJITted functions. Thus,
70// * During a first-JIT, only issue the existing V1 MethodLoad, etc. events (NOT v0,
71// NOT v2). This event does not include a ReJITID, and can thus continue to be
72// parsed by older decoders.
73// * During a rejit, only issue the new V2 events (NOT v0 or v1), which will include a
74// nonzero ReJITID. Thus, your unique key for a method extent would be MethodID +
75// ReJITID + extent (hot/cold). These events will be ignored by older decoders
76// (including current xperf) because of the version number, but xperf will be
77// updated to decode these in the future.
78
79#define FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
80{ \
81 if (rejitID == 0) \
82 { FireEtwMethodLoadVerbose_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
83 else \
84 { FireEtwMethodLoadVerbose_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
85}
86
87#define FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, rejitID) \
88{ \
89 if (rejitID == 0) \
90 { FireEtwMethodLoad_V1(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID); } \
91 else \
92 { FireEtwMethodLoad_V2(ullMethodIdentifier, ullModuleID, ullMethodStartAddress, ulMethodSize, ulMethodToken, ulMethodFlags, clrInstanceID, rejitID); } \
93}
94
95#define FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
96{ \
97 if (rejitID == 0) \
98 { FireEtwMethodUnloadVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
99 else \
100 { FireEtwMethodUnloadVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
101}
102
103#define FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \
104{ \
105 if (rejitID == 0) \
106 { FireEtwMethodUnload_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
107 else \
108 { FireEtwMethodUnload_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \
109}
110
111#define FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
112{ \
113 if (rejitID == 0) \
114 { FireEtwMethodDCStartVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
115 else \
116 { FireEtwMethodDCStartVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
117}
118
119#define FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \
120{ \
121 if (rejitID == 0) \
122 { FireEtwMethodDCStart_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
123 else \
124 { FireEtwMethodDCStart_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \
125}
126
127#define FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID) \
128{ \
129 if (rejitID == 0) \
130 { FireEtwMethodDCEndVerbose_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID); } \
131 else \
132 { FireEtwMethodDCEndVerbose_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, szDtraceOutput1, szDtraceOutput2, szDtraceOutput3, clrInstanceID, rejitID); } \
133}
134
135#define FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID) \
136{ \
137 if (rejitID == 0) \
138 { FireEtwMethodDCEnd_V1(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID); } \
139 else \
140 { FireEtwMethodDCEnd_V2(ullMethodIdentifier, ullModuleID, ullColdMethodStartAddress, ulColdMethodSize, ulMethodToken, ulColdMethodFlags, clrInstanceID, rejitID); } \
141}
142
143// Module load / unload events:
144
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)
153
154
155
156//---------------------------------------------------------------------------------------
157//
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.
161//
162// OverrideAndSuppressNGenEvents allows controllers to set the expensive NGEN keyword for
163// older runtimes (< 4.0) where NGEN PDB info is NOT available, while suppressing those
164// expensive events on newer runtimes (>= 4.5) where NGEN PDB info IS available. Note
165// that 4.0 has NGEN PDBS but unfortunately not the OverrideAndSuppressNGenEvents
166// keyword, b/c NGEN PDBs were made publicly only after 4.0 shipped. So tools that need
167// to consume both <4.0 and 4.0 events would neeed to enable the expensive NGEN events to
168// deal properly with 3.5, even though those events aren't necessary on 4.0.
169//
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).
172//
173// Return Value:
174// nonzero iff NGenKeyword is enabled on the runtime provider and
175// OverrideAndSuppressNGenEventsKeyword is not enabled on the runtime provider.
176//
177
178BOOL IsRuntimeNgenKeywordEnabledAndNotSuppressed()
179{
180 LIMITED_METHOD_CONTRACT;
181
182 return
183 (
184 ETW_TRACING_CATEGORY_ENABLED(
185 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
186 TRACE_LEVEL_INFORMATION,
187 CLR_NGEN_KEYWORD)
188 && ! ( ETW_TRACING_CATEGORY_ENABLED(
189 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
190 TRACE_LEVEL_INFORMATION,
191 CLR_OVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD) )
192 );
193}
194
195// Same as above, but for the rundown provider
196BOOL IsRundownNgenKeywordEnabledAndNotSuppressed()
197{
198 LIMITED_METHOD_CONTRACT;
199
200 return
201#ifdef FEATURE_PERFTRACING
202 EventPipeHelper::Enabled() ||
203#endif // FEATURE_PERFTRACING
204 (
205 ETW_TRACING_CATEGORY_ENABLED(
206 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
207 TRACE_LEVEL_INFORMATION,
208 CLR_RUNDOWNNGEN_KEYWORD)
209 && ! ( ETW_TRACING_CATEGORY_ENABLED(
210 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
211 TRACE_LEVEL_INFORMATION,
212 CLR_RUNDOWNOVERRIDEANDSUPPRESSNGENEVENTS_KEYWORD) )
213 );
214}
215
216/*******************************************************/
217/* Fast assembly function to get the topmost EBP frame */
218/*******************************************************/
219#if defined(_TARGET_X86_)
220extern "C"
221{
222 CallStackFrame* GetEbp()
223 {
224 CallStackFrame *frame=NULL;
225 __asm
226 {
227 mov frame, ebp
228 }
229 return frame;
230 }
231}
232#endif //_TARGET_X86_
233
234/*************************************/
235/* Function to append a frame to an existing stack */
236/*************************************/
237#if !defined(FEATURE_PAL)
238void ETW::SamplingLog::Append(SIZE_T currentFrame)
239{
240 LIMITED_METHOD_CONTRACT;
241 if(m_FrameCount < (ETW::SamplingLog::s_MaxStackSize-1) &&
242 currentFrame != 0)
243 {
244 m_EBPStack[m_FrameCount] = currentFrame;
245 m_FrameCount++;
246 }
247};
248
249/********************************************************/
250/* Function to get the callstack on the current thread */
251/********************************************************/
252ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::GetCurrentThreadsCallStack(UINT32 *frameCount, PVOID **Stack)
253{
254 CONTRACTL
255 {
256 NOTHROW;
257 GC_NOTRIGGER;
258 MODE_ANY;
259 SO_TOLERANT;
260 }
261 CONTRACTL_END;
262
263 // The stack walk performed below can cause allocations (thus entering the host). But
264 // this is acceptable, since we're not supporting the use of SQL/F1 profiling and
265 // full-blown ETW CLR stacks (which would be redundant).
266 PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonUnsupportedForSQLF1Profiling);
267
268 m_FrameCount = 0;
269 ETW::SamplingLog::EtwStackWalkStatus stackwalkStatus = SaveCurrentStack();
270
271 _ASSERTE(m_FrameCount < ETW::SamplingLog::s_MaxStackSize);
272
273 // this not really needed, but let's do it
274 // because we use the framecount while dumping the stack event
275 for(int i=m_FrameCount; i<ETW::SamplingLog::s_MaxStackSize; i++)
276 {
277 m_EBPStack[i] = 0;
278 }
279 // This is for consumers to work correctly because the number of
280 // frames in the manifest file is specified to be 2
281 if(m_FrameCount < 2)
282 m_FrameCount = 2;
283
284 *frameCount = m_FrameCount;
285 *Stack = (PVOID *)m_EBPStack;
286 return stackwalkStatus;
287};
288
289/*************************************/
290/* Function to save the stack on the current thread */
291/*************************************/
292ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::SaveCurrentStack(int skipTopNFrames)
293{
294 CONTRACTL
295 {
296 NOTHROW;
297 GC_NOTRIGGER;
298 MODE_ANY;
299 SO_TOLERANT;
300 }
301 CONTRACTL_END;
302
303 if (!IsGarbageCollectorFullyInitialized())
304 {
305 // If the GC isn't ready yet, then there won't be any interesting
306 // managed code on the stack to walk. Plus, the stack walk itself may
307 // hit problems (e.g., when calling into the code manager) if it's run
308 // too early during startup.
309 return ETW::SamplingLog::UnInitialized;
310 }
311#ifndef DACCESS_COMPILE
312#ifdef _TARGET_AMD64_
313 if (RtlVirtualUnwind_Unsafe == NULL)
314 {
315 // We haven't even set up the RtlVirtualUnwind function pointer yet,
316 // so it's too early to try stack walking.
317 return ETW::SamplingLog::UnInitialized;
318 }
319#endif // _TARGET_AMD64_
320 Thread *pThread = GetThread();
321 if (pThread == NULL)
322 {
323 return ETW::SamplingLog::UnInitialized;
324 }
325 // The thread should not have a hijack set up or we can't walk the stack.
326 if (pThread->m_State & Thread::TS_Hijacked) {
327 return ETW::SamplingLog::UnInitialized;
328 }
329 if (pThread->IsEtwStackWalkInProgress())
330 {
331 return ETW::SamplingLog::InProgress;
332 }
333 pThread->MarkEtwStackWalkInProgress();
334 EX_TRY
335 {
336#ifdef _TARGET_X86_
337 CallStackFrame *currentEBP = GetEbp();
338 CallStackFrame *lastEBP = NULL;
339
340 // The EBP stack walk below is meant to be extremely fast. It does not attempt to protect
341 // against cases of stack corruption. *BUT* it does need to validate a "sane" EBP chain.
342
343 // Ensure the EBP in the starting frame is "reasonable" (i.e. above the address of a local)
344 if ((SIZE_T) currentEBP > (SIZE_T)&currentEBP)
345 {
346 while(currentEBP)
347 {
348 lastEBP = currentEBP;
349 currentEBP = currentEBP->m_Next;
350
351 // Check for stack upper limit; we don't check the lower limit on each iteration
352 // (we did it at the top) and each subsequent value in the loop is larger than
353 // the previous (see the check "currentEBP < lastEBP" below)
354 if((SIZE_T)currentEBP > (SIZE_T)Thread::GetStackUpperBound())
355 {
356 break;
357 }
358
359 // If we have a too small address, we are probably bad
360 if((SIZE_T)currentEBP < (SIZE_T)0x10000)
361 break;
362
363 if((SIZE_T)currentEBP < (SIZE_T)lastEBP)
364 {
365 break;
366 }
367
368 // Skip the top N frames
369 if(skipTopNFrames) {
370 skipTopNFrames--;
371 continue;
372 }
373
374 // Save the Return Address for symbol decoding
375 Append(lastEBP->m_ReturnAddress);
376 }
377 }
378#else
379 CONTEXT ctx;
380 ClrCaptureContext(&ctx);
381 UINT_PTR ControlPc = 0;
382 UINT_PTR CurrentSP = 0, PrevSP = 0;
383
384 while(1)
385 {
386 // Unwind to the caller
387 ControlPc = Thread::VirtualUnwindCallFrame(&ctx);
388
389 // This is to take care of recursion
390 CurrentSP = (UINT_PTR)GetSP(&ctx);
391
392 // when to break from this loop
393 if ( ControlPc == 0 || ( PrevSP == CurrentSP ) )
394 {
395 break;
396 }
397
398 // Skip the top N frames
399 if ( skipTopNFrames ) {
400 skipTopNFrames--;
401 continue;
402 }
403
404 // Add the stack frame to the list
405 Append(ControlPc);
406
407 PrevSP = CurrentSP;
408 }
409#endif //_TARGET_X86_
410 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
411 pThread->MarkEtwStackWalkCompleted();
412#endif //!DACCESS_COMPILE
413
414 return ETW::SamplingLog::Completed;
415}
416
417#endif // !defined(FEATURE_PAL)
418#endif // !FEATURE_REDHAWK
419
420/****************************************************************************/
421/* Methods that are called from the runtime */
422/****************************************************************************/
423
424/****************************************************************************/
425/* Methods for rundown events */
426/****************************************************************************/
427
428/***************************************************************************/
429/* This function should be called from the event tracing callback routine
430 when the private CLR provider is enabled */
431/***************************************************************************/
432
433#ifndef FEATURE_REDHAWK
434
435VOID ETW::GCLog::GCSettingsEvent()
436{
437 if (GCHeapUtilities::IsGCHeapInitialized())
438 {
439 if (ETW_TRACING_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
440 GCSettings))
441 {
442 ETW::GCLog::ETW_GC_INFO Info;
443
444 Info.GCSettings.ServerGC = GCHeapUtilities::IsServerHeap ();
445 Info.GCSettings.SegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (false);
446 Info.GCSettings.LargeObjectSegmentSize = GCHeapUtilities::GetGCHeap()->GetValidSegmentSize (true);
447 FireEtwGCSettings_V1(Info.GCSettings.SegmentSize, Info.GCSettings.LargeObjectSegmentSize, Info.GCSettings.ServerGC, GetClrInstanceId());
448 }
449 GCHeapUtilities::GetGCHeap()->DiagTraceGCSegments();
450 }
451};
452
453#endif // !FEATURE_REDHAWK
454
455
456//---------------------------------------------------------------------------------------
457// Code for sending GC heap object events is generally the same for both FEATURE_REDHAWK
458// and !FEATURE_REDHAWK builds
459//---------------------------------------------------------------------------------------
460
461bool s_forcedGCInProgress = false;
462class ForcedGCHolder
463{
464public:
465 ForcedGCHolder() { LIMITED_METHOD_CONTRACT; s_forcedGCInProgress = true; }
466 ~ForcedGCHolder() { LIMITED_METHOD_CONTRACT; s_forcedGCInProgress = false; }
467};
468
469BOOL ETW::GCLog::ShouldWalkStaticsAndCOMForEtw()
470{
471 LIMITED_METHOD_CONTRACT;
472
473 return s_forcedGCInProgress &&
474 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
475 TRACE_LEVEL_INFORMATION,
476 CLR_GCHEAPDUMP_KEYWORD);
477}
478
479// Simple helpers called by the GC to decide whether it needs to do a walk of heap
480// objects and / or roots.
481
482BOOL ETW::GCLog::ShouldWalkHeapObjectsForEtw()
483{
484 LIMITED_METHOD_CONTRACT;
485 return s_forcedGCInProgress &&
486 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
487 TRACE_LEVEL_INFORMATION,
488 CLR_GCHEAPDUMP_KEYWORD);
489}
490
491BOOL ETW::GCLog::ShouldWalkHeapRootsForEtw()
492{
493 LIMITED_METHOD_CONTRACT;
494 return s_forcedGCInProgress &&
495 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
496 TRACE_LEVEL_INFORMATION,
497 CLR_GCHEAPDUMP_KEYWORD);
498}
499
500BOOL ETW::GCLog::ShouldTrackMovementForEtw()
501{
502 LIMITED_METHOD_CONTRACT;
503 return ETW_TRACING_CATEGORY_ENABLED(
504 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
505 TRACE_LEVEL_INFORMATION,
506 CLR_GCHEAPSURVIVALANDMOVEMENT_KEYWORD);
507}
508
509// Batches the list of moved/surviving references for the GCBulkMovedObjectRanges /
510// GCBulkSurvivingObjectRanges events
511struct EtwGcMovementContext
512{
513public:
514 // An instance of EtwGcMovementContext is dynamically allocated and stored
515 // inside of MovedReferenceContextForEtwAndProfapi, which in turn is dynamically
516 // allocated and pointed to by a profiling_context pointer created by the GC on the stack.
517 // This is used to batch and send GCBulkSurvivingObjectRanges events and
518 // GCBulkMovedObjectRanges events. This method is passed a pointer to
519 // MovedReferenceContextForEtwAndProfapi::pctxEtw; if non-NULL it gets returned;
520 // else, a new EtwGcMovementContext is allocated, stored in that pointer, and
521 // then returned. Callers should test for NULL, which can be returned if out of
522 // memory
523 static EtwGcMovementContext * GetOrCreateInGCContext(EtwGcMovementContext ** ppContext)
524 {
525 LIMITED_METHOD_CONTRACT;
526
527 _ASSERTE(ppContext != NULL);
528
529 EtwGcMovementContext * pContext = *ppContext;
530 if (pContext == NULL)
531 {
532 pContext = new (nothrow) EtwGcMovementContext;
533 *ppContext = pContext;
534 }
535 return pContext;
536 }
537
538 EtwGcMovementContext() :
539 iCurBulkSurvivingObjectRanges(0),
540 iCurBulkMovedObjectRanges(0)
541 {
542 LIMITED_METHOD_CONTRACT;
543 Clear();
544 }
545
546 // Resets structure for reuse on construction, and after each flush.
547 // (Intentionally leave iCurBulk* as is, since they persist across flushes within a GC.)
548 void Clear()
549 {
550 LIMITED_METHOD_CONTRACT;
551 cBulkSurvivingObjectRanges = 0;
552 cBulkMovedObjectRanges = 0;
553 ZeroMemory(rgGCBulkSurvivingObjectRanges, sizeof(rgGCBulkSurvivingObjectRanges));
554 ZeroMemory(rgGCBulkMovedObjectRanges, sizeof(rgGCBulkMovedObjectRanges));
555 }
556
557 //---------------------------------------------------------------------------------------
558 // GCBulkSurvivingObjectRanges
559 //---------------------------------------------------------------------------------------
560
561 // Sequence number for each GCBulkSurvivingObjectRanges event
562 UINT iCurBulkSurvivingObjectRanges;
563
564 // Number of surviving object ranges currently filled out in rgGCBulkSurvivingObjectRanges array
565 UINT cBulkSurvivingObjectRanges;
566
567 // Struct array containing the primary data for each GCBulkSurvivingObjectRanges
568 // event. Fix the size so the total event stays well below the 64K limit (leaving
569 // lots of room for non-struct fields that come before the values data)
570 EventStructGCBulkSurvivingObjectRangesValue rgGCBulkSurvivingObjectRanges[
571 (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkSurvivingObjectRangesValue)];
572
573 //---------------------------------------------------------------------------------------
574 // GCBulkMovedObjectRanges
575 //---------------------------------------------------------------------------------------
576
577 // Sequence number for each GCBulkMovedObjectRanges event
578 UINT iCurBulkMovedObjectRanges;
579
580 // Number of Moved object ranges currently filled out in rgGCBulkMovedObjectRanges array
581 UINT cBulkMovedObjectRanges;
582
583 // Struct array containing the primary data for each GCBulkMovedObjectRanges
584 // event. Fix the size so the total event stays well below the 64K limit (leaving
585 // lots of room for non-struct fields that come before the values data)
586 EventStructGCBulkMovedObjectRangesValue rgGCBulkMovedObjectRanges[
587 (cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkMovedObjectRangesValue)];
588};
589
590// Contains above struct for ETW, plus extra info (opaque to us) used by the profiling
591// API to track its own information.
592struct MovedReferenceContextForEtwAndProfapi
593{
594 // An instance of MovedReferenceContextForEtwAndProfapi is dynamically allocated and
595 // pointed to by a profiling_context pointer created by the GC on the stack. This is used to
596 // batch and send GCBulkSurvivingObjectRanges events and GCBulkMovedObjectRanges
597 // events and the corresponding callbacks for profapi profilers. This method is
598 // passed a pointer to a MovedReferenceContextForEtwAndProfapi; if non-NULL it gets
599 // returned; else, a new MovedReferenceContextForEtwAndProfapi is allocated, stored
600 // in that pointer, and then returned. Callers should test for NULL, which can be
601 // returned if out of memory
602 static MovedReferenceContextForEtwAndProfapi * CreateInGCContext(LPVOID pvContext)
603 {
604 LIMITED_METHOD_CONTRACT;
605
606 _ASSERTE(pvContext != NULL);
607
608 MovedReferenceContextForEtwAndProfapi * pContext = *(MovedReferenceContextForEtwAndProfapi **) pvContext;
609
610 // Shouldn't be called if the context was already created. Perhaps someone made
611 // one too many BeginMovedReferences calls, or didn't have an EndMovedReferences
612 // in between?
613 _ASSERTE(pContext == NULL);
614
615 pContext = new (nothrow) MovedReferenceContextForEtwAndProfapi;
616 *(MovedReferenceContextForEtwAndProfapi **) pvContext = pContext;
617
618 return pContext;
619 }
620
621
622 MovedReferenceContextForEtwAndProfapi() :
623 pctxProfAPI(NULL),
624 pctxEtw(NULL)
625
626 {
627 LIMITED_METHOD_CONTRACT;
628 }
629
630 LPVOID pctxProfAPI;
631 EtwGcMovementContext * pctxEtw;
632};
633
634
635//---------------------------------------------------------------------------------------
636//
637// Called by the GC for each moved or surviving reference that it encounters. This
638// batches the info into our context's buffer, and flushes that buffer to ETW as it fills
639// up.
640//
641// Arguments:
642// * pbMemBlockStart - Start of moved/surviving block
643// * pbMemBlockEnd - Next pointer after end of moved/surviving block
644// * cbRelocDistance - How far did the block move? (0 for non-compacted / surviving
645// references; negative if moved to earlier addresses)
646// * profilingContext - Where our context is stored
647// * fCompacting - Is this a compacting GC? Used to decide whether to send the moved
648// or surviving event
649//
650
651// static
652void ETW::GCLog::MovedReference(
653 BYTE * pbMemBlockStart,
654 BYTE * pbMemBlockEnd,
655 ptrdiff_t cbRelocDistance,
656 size_t profilingContext,
657 BOOL fCompacting,
658 BOOL fAllowProfApiNotification /* = TRUE */)
659{
660 CONTRACTL
661 {
662 NOTHROW;
663 GC_NOTRIGGER;
664 MODE_ANY;
665 CAN_TAKE_LOCK; // EEToProfInterfaceImpl::AllocateMovedReferencesData takes lock
666 }
667 CONTRACTL_END;
668
669 MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi =
670 (MovedReferenceContextForEtwAndProfapi *) profilingContext;
671 if (pCtxForEtwAndProfapi == NULL)
672 {
673 _ASSERTE(!"MovedReference() encountered a NULL profilingContext");
674 return;
675 }
676
677#ifdef PROFILING_SUPPORTED
678 // ProfAPI
679 if (fAllowProfApiNotification)
680 {
681 BEGIN_PIN_PROFILER(CORProfilerTrackGC());
682 g_profControlBlock.pProfInterface->MovedReference(pbMemBlockStart,
683 pbMemBlockEnd,
684 cbRelocDistance,
685 &(pCtxForEtwAndProfapi->pctxProfAPI),
686 fCompacting);
687 END_PIN_PROFILER();
688 }
689#endif // PROFILING_SUPPORTED
690
691 // ETW
692
693 if (!ShouldTrackMovementForEtw())
694 return;
695
696 EtwGcMovementContext * pContext =
697 EtwGcMovementContext::GetOrCreateInGCContext(&pCtxForEtwAndProfapi->pctxEtw);
698 if (pContext == NULL)
699 return;
700
701 if (fCompacting)
702 {
703 // Moved references
704
705 _ASSERTE(pContext->cBulkMovedObjectRanges < _countof(pContext->rgGCBulkMovedObjectRanges));
706 EventStructGCBulkMovedObjectRangesValue * pValue =
707 &pContext->rgGCBulkMovedObjectRanges[pContext->cBulkMovedObjectRanges];
708 pValue->OldRangeBase = pbMemBlockStart;
709 pValue->NewRangeBase = pbMemBlockStart + cbRelocDistance;
710 pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart;
711 pContext->cBulkMovedObjectRanges++;
712
713 // If buffer is now full, empty it into ETW
714 if (pContext->cBulkMovedObjectRanges == _countof(pContext->rgGCBulkMovedObjectRanges))
715 {
716 FireEtwGCBulkMovedObjectRanges(
717 pContext->iCurBulkMovedObjectRanges,
718 pContext->cBulkMovedObjectRanges,
719 GetClrInstanceId(),
720 sizeof(pContext->rgGCBulkMovedObjectRanges[0]),
721 &pContext->rgGCBulkMovedObjectRanges[0]);
722
723 pContext->iCurBulkMovedObjectRanges++;
724 pContext->Clear();
725 }
726 }
727 else
728 {
729 // Surviving references
730
731 _ASSERTE(pContext->cBulkSurvivingObjectRanges < _countof(pContext->rgGCBulkSurvivingObjectRanges));
732 EventStructGCBulkSurvivingObjectRangesValue * pValue =
733 &pContext->rgGCBulkSurvivingObjectRanges[pContext->cBulkSurvivingObjectRanges];
734 pValue->RangeBase = pbMemBlockStart;
735 pValue->RangeLength = pbMemBlockEnd - pbMemBlockStart;
736 pContext->cBulkSurvivingObjectRanges++;
737
738 // If buffer is now full, empty it into ETW
739 if (pContext->cBulkSurvivingObjectRanges == _countof(pContext->rgGCBulkSurvivingObjectRanges))
740 {
741 FireEtwGCBulkSurvivingObjectRanges(
742 pContext->iCurBulkSurvivingObjectRanges,
743 pContext->cBulkSurvivingObjectRanges,
744 GetClrInstanceId(),
745 sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]),
746 &pContext->rgGCBulkSurvivingObjectRanges[0]);
747
748 pContext->iCurBulkSurvivingObjectRanges++;
749 pContext->Clear();
750 }
751 }
752}
753
754
755//---------------------------------------------------------------------------------------
756//
757// Called by the GC just before it begins enumerating plugs. Gives us a chance to
758// allocate our context structure, to allow us to batch plugs before firing events
759// for them
760//
761// Arguments:
762// * pProfilingContext - Points to location on stack (in GC function) where we can
763// store a pointer to the context we allocate
764//
765
766// static
767VOID ETW::GCLog::BeginMovedReferences(size_t * pProfilingContext)
768{
769 LIMITED_METHOD_CONTRACT;
770
771 MovedReferenceContextForEtwAndProfapi::CreateInGCContext(LPVOID(pProfilingContext));
772}
773
774
775//---------------------------------------------------------------------------------------
776//
777// Called by the GC at the end of a heap walk to give us a place to flush any remaining
778// buffers of data to ETW or the profapi profiler
779//
780// Arguments:
781// profilingContext - Our context we built up during the heap walk
782//
783
784// static
785VOID ETW::GCLog::EndMovedReferences(size_t profilingContext, BOOL fAllowProfApiNotification /* = TRUE */)
786{
787 CONTRACTL
788 {
789 NOTHROW;
790 GC_NOTRIGGER;
791 MODE_ANY;
792 CAN_TAKE_LOCK;
793 }
794 CONTRACTL_END;
795
796 MovedReferenceContextForEtwAndProfapi * pCtxForEtwAndProfapi = (MovedReferenceContextForEtwAndProfapi *) profilingContext;
797 if (pCtxForEtwAndProfapi == NULL)
798 {
799 _ASSERTE(!"EndMovedReferences() encountered a NULL profilingContext");
800 return;
801 }
802
803#ifdef PROFILING_SUPPORTED
804 // ProfAPI
805 if (fAllowProfApiNotification)
806 {
807 BEGIN_PIN_PROFILER(CORProfilerTrackGC());
808 g_profControlBlock.pProfInterface->EndMovedReferences(&(pCtxForEtwAndProfapi->pctxProfAPI));
809 END_PIN_PROFILER();
810 }
811#endif //PROFILING_SUPPORTED
812
813 // ETW
814
815 if (!ShouldTrackMovementForEtw())
816 return;
817
818 // If context isn't already set up for us, then we haven't been collecting any data
819 // for ETW events.
820 EtwGcMovementContext * pContext = pCtxForEtwAndProfapi->pctxEtw;
821 if (pContext == NULL)
822 return;
823
824 // Flush any remaining moved or surviving range data
825
826 if (pContext->cBulkMovedObjectRanges > 0)
827 {
828 FireEtwGCBulkMovedObjectRanges(
829 pContext->iCurBulkMovedObjectRanges,
830 pContext->cBulkMovedObjectRanges,
831 GetClrInstanceId(),
832 sizeof(pContext->rgGCBulkMovedObjectRanges[0]),
833 &pContext->rgGCBulkMovedObjectRanges[0]);
834 }
835
836 if (pContext->cBulkSurvivingObjectRanges > 0)
837 {
838 FireEtwGCBulkSurvivingObjectRanges(
839 pContext->iCurBulkSurvivingObjectRanges,
840 pContext->cBulkSurvivingObjectRanges,
841 GetClrInstanceId(),
842 sizeof(pContext->rgGCBulkSurvivingObjectRanges[0]),
843 &pContext->rgGCBulkSurvivingObjectRanges[0]);
844 }
845
846 pCtxForEtwAndProfapi->pctxEtw = NULL;
847 delete pContext;
848}
849
850/***************************************************************************/
851/* This implements the public runtime provider's GCHeapCollectKeyword. It
852 performs a full, gen-2, blocking GC. */
853/***************************************************************************/
854VOID ETW::GCLog::ForceGC(LONGLONG l64ClientSequenceNumber)
855{
856 CONTRACTL
857 {
858 NOTHROW;
859 GC_TRIGGERS;
860 MODE_ANY;
861 }
862 CONTRACTL_END;
863
864#ifndef FEATURE_REDHAWK
865 if (!IsGarbageCollectorFullyInitialized())
866 return;
867#endif // FEATURE_REDHAWK
868
869 InterlockedExchange64(&s_l64LastClientSequenceNumber, l64ClientSequenceNumber);
870
871 ForceGCForDiagnostics();
872}
873
874//---------------------------------------------------------------------------------------
875//
876// Helper to fire the GCStart event. Figures out which version of GCStart to fire, and
877// includes the client sequence number, if available.
878//
879// Arguments:
880// pGcInfo - ETW_GC_INFO containing details from GC about this collection
881//
882
883// static
884VOID ETW::GCLog::FireGcStart(ETW_GC_INFO * pGcInfo)
885{
886 LIMITED_METHOD_CONTRACT;
887
888 if (ETW_TRACING_CATEGORY_ENABLED(
889 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
890 TRACE_LEVEL_INFORMATION,
891 CLR_GC_KEYWORD))
892 {
893 // If the controller specified a client sequence number for us to log with this
894 // GCStart, then retrieve it
895 LONGLONG l64ClientSequenceNumberToLog = 0;
896 if ((s_l64LastClientSequenceNumber != 0) &&
897 (pGcInfo->GCStart.Depth == GCHeapUtilities::GetGCHeap()->GetMaxGeneration()) &&
898 (pGcInfo->GCStart.Reason == ETW_GC_INFO::GC_INDUCED))
899 {
900 l64ClientSequenceNumberToLog = InterlockedExchange64(&s_l64LastClientSequenceNumber, 0);
901 }
902
903 FireEtwGCStart_V2(pGcInfo->GCStart.Count, pGcInfo->GCStart.Depth, pGcInfo->GCStart.Reason, pGcInfo->GCStart.Type, GetClrInstanceId(), l64ClientSequenceNumberToLog);
904 }
905}
906
907//---------------------------------------------------------------------------------------
908//
909// Contains code common to profapi and ETW scenarios where the profiler wants to force
910// the CLR to perform a GC. The important work here is to create a managed thread for
911// the current thread BEFORE the GC begins. On both ETW and profapi threads, there may
912// not yet be a managed thread object. But some scenarios require a managed thread
913// object be present (notably if we need to call into Jupiter during the GC).
914//
915// Return Value:
916// HRESULT indicating success or failure
917//
918// Assumptions:
919// Caller should ensure that the EE has fully started up and that the GC heap is
920// initialized enough to actually perform a GC
921//
922
923// static
924HRESULT ETW::GCLog::ForceGCForDiagnostics()
925{
926 CONTRACTL
927 {
928 NOTHROW;
929 GC_TRIGGERS;
930 MODE_ANY;
931 }
932 CONTRACTL_END;
933
934 HRESULT hr = E_FAIL;
935
936#ifndef FEATURE_REDHAWK
937 // Caller should ensure we're past startup.
938 _ASSERTE(IsGarbageCollectorFullyInitialized());
939
940 // In immersive apps the GarbageCollect() call below will call into Jupiter,
941 // which will call back into the runtime to track references. This call
942 // chain would cause a Thread object to be created for this thread while code
943 // higher on the stack owns the ThreadStoreLock. This will lead to asserts
944 // since the ThreadStoreLock is non-reentrant. To avoid this we'll create
945 // the Thread object here instead.
946 if (GetThreadNULLOk() == NULL)
947 {
948 HRESULT hr = E_FAIL;
949 SetupThreadNoThrow(&hr);
950 if (FAILED(hr))
951 return hr;
952 }
953
954 ASSERT_NO_EE_LOCKS_HELD();
955
956 EX_TRY
957 {
958 // Need to switch to cooperative mode as the thread will access managed
959 // references (through Jupiter callbacks).
960 GCX_COOP();
961#endif // FEATURE_REDHAWK
962
963 ForcedGCHolder forcedGCHolder;
964
965 hr = GCHeapUtilities::GetGCHeap()->GarbageCollect(
966 -1, // all generations should be collected
967 false, // low_memory_p
968 collection_blocking);
969
970#ifndef FEATURE_REDHAWK
971 }
972 EX_CATCH { }
973 EX_END_CATCH(RethrowCorruptingExceptions);
974#endif // FEATURE_REDHAWK
975
976 return hr;
977}
978
979
980
981
982
983
984//---------------------------------------------------------------------------------------
985// WalkStaticsAndCOMForETW walks both CCW/RCW objects and static variables.
986//---------------------------------------------------------------------------------------
987
988VOID ETW::GCLog::WalkStaticsAndCOMForETW()
989{
990 CONTRACTL
991 {
992 NOTHROW;
993 GC_TRIGGERS;
994 }
995 CONTRACTL_END;
996
997 EX_TRY
998 {
999 BulkTypeEventLogger typeLogger;
1000
1001 // Walk RCWs/CCWs
1002 BulkComLogger comLogger(&typeLogger);
1003 comLogger.LogAllComObjects();
1004
1005 // Walk static variables
1006 BulkStaticsLogger staticLogger(&typeLogger);
1007 staticLogger.LogAllStatics();
1008
1009 // Ensure all loggers have written all events, fire type logger last to batch events
1010 // (FireBulkComEvent or FireBulkStaticsEvent may queue up additional types).
1011 comLogger.FireBulkComEvent();
1012 staticLogger.FireBulkStaticsEvent();
1013 typeLogger.FireBulkTypeEvent();
1014 }
1015 EX_CATCH
1016 {
1017 }
1018 EX_END_CATCH(SwallowAllExceptions);
1019}
1020
1021
1022//---------------------------------------------------------------------------------------
1023// BulkStaticsLogger: Batches up and logs static variable roots
1024//---------------------------------------------------------------------------------------
1025
1026BulkComLogger::BulkComLogger(BulkTypeEventLogger *typeLogger)
1027 : m_currRcw(0), m_currCcw(0), m_typeLogger(typeLogger), m_etwRcwData(0), m_etwCcwData(0), m_enumResult(0)
1028{
1029 CONTRACTL
1030 {
1031 THROWS;
1032 GC_NOTRIGGER;
1033 MODE_ANY;
1034 }
1035 CONTRACTL_END;
1036
1037 m_etwRcwData = new EventRCWEntry[kMaxRcwCount];
1038 m_etwCcwData = new EventCCWEntry[kMaxCcwCount];
1039}
1040
1041BulkComLogger::~BulkComLogger()
1042{
1043 CONTRACTL
1044 {
1045 NOTHROW;
1046 GC_NOTRIGGER;
1047 MODE_ANY;
1048 }
1049 CONTRACTL_END;
1050
1051 FireBulkComEvent();
1052
1053 if (m_etwRcwData)
1054 delete [] m_etwRcwData;
1055
1056 if (m_etwCcwData)
1057 delete [] m_etwCcwData;
1058
1059 if (m_enumResult)
1060 {
1061 CCWEnumerationEntry *curr = m_enumResult;
1062 while (curr)
1063 {
1064 CCWEnumerationEntry *next = curr->Next;
1065 delete curr;
1066 curr = next;
1067 }
1068 }
1069}
1070
1071void BulkComLogger::FireBulkComEvent()
1072{
1073 WRAPPER_NO_CONTRACT;
1074
1075 FlushRcw();
1076 FlushCcw();
1077}
1078
1079void BulkComLogger::WriteRcw(RCW *pRcw, Object *obj)
1080{
1081 CONTRACTL
1082 {
1083 THROWS;
1084 GC_TRIGGERS;
1085 MODE_ANY;
1086 PRECONDITION(pRcw != NULL);
1087 PRECONDITION(obj != NULL);
1088 }
1089 CONTRACTL_END;
1090
1091 _ASSERTE(m_currRcw < kMaxRcwCount);
1092
1093#ifdef FEATURE_COMINTEROP
1094 EventRCWEntry &rcw = m_etwRcwData[m_currRcw];
1095 rcw.ObjectID = (ULONGLONG)obj;
1096 rcw.TypeID = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1097 rcw.IUnk = (ULONGLONG)pRcw->GetIUnknown_NoAddRef();
1098 rcw.VTable = (ULONGLONG)pRcw->GetVTablePtr();
1099 rcw.RefCount = pRcw->GetRefCount();
1100 rcw.Flags = 0;
1101
1102 if (++m_currRcw >= kMaxRcwCount)
1103 FlushRcw();
1104#endif
1105}
1106
1107void BulkComLogger::FlushRcw()
1108{
1109 CONTRACTL
1110 {
1111 NOTHROW;
1112 GC_NOTRIGGER;
1113 MODE_ANY;
1114 }
1115 CONTRACTL_END;
1116
1117 _ASSERTE(m_currRcw <= kMaxRcwCount);
1118
1119 if (m_currRcw == 0)
1120 return;
1121
1122 if (m_typeLogger)
1123 {
1124 for (int i = 0; i < m_currRcw; ++i)
1125 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, m_etwRcwData[i].TypeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1126 }
1127
1128 unsigned short instance = GetClrInstanceId();
1129
1130#if !defined(FEATURE_PAL)
1131 EVENT_DATA_DESCRIPTOR eventData[3];
1132 EventDataDescCreate(&eventData[0], &m_currRcw, sizeof(const unsigned int));
1133 EventDataDescCreate(&eventData[1], &instance, sizeof(const unsigned short));
1134 EventDataDescCreate(&eventData[2], m_etwRcwData, sizeof(EventRCWEntry) * m_currRcw);
1135
1136 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRCW, _countof(eventData), eventData);
1137#else
1138 ULONG result = FireEtXplatGCBulkRCW(m_currRcw, instance, sizeof(EventRCWEntry) * m_currRcw, m_etwRcwData);
1139#endif // !defined(FEATURE_PAL)
1140
1141 _ASSERTE(result == ERROR_SUCCESS);
1142
1143 m_currRcw = 0;
1144}
1145
1146void BulkComLogger::WriteCcw(ComCallWrapper *pCcw, Object **handle, Object *obj)
1147{
1148 CONTRACTL
1149 {
1150 THROWS;
1151 GC_TRIGGERS;
1152 MODE_ANY;
1153 PRECONDITION(handle != NULL);
1154 PRECONDITION(obj != NULL);
1155 }
1156 CONTRACTL_END;
1157
1158 _ASSERTE(m_currCcw < kMaxCcwCount);
1159
1160#ifdef FEATURE_COMINTEROP
1161 IUnknown *iUnk = NULL;
1162 int refCount = 0;
1163 ULONG jupiterRefCount = 0;
1164 ULONG flags = 0;
1165
1166 if (pCcw)
1167 {
1168 iUnk = pCcw->GetOuter();
1169 if (iUnk == NULL)
1170 iUnk = pCcw->GetBasicIP(true);
1171
1172 refCount = pCcw->GetRefCount();
1173 jupiterRefCount = pCcw->GetJupiterRefCount();
1174
1175 if (pCcw->IsWrapperActive())
1176 flags |= EventCCWEntry::Strong;
1177
1178 if (pCcw->IsPegged())
1179 flags |= EventCCWEntry::Pegged;
1180 }
1181
1182 EventCCWEntry &ccw = m_etwCcwData[m_currCcw++];
1183 ccw.RootID = (ULONGLONG)handle;
1184 ccw.ObjectID = (ULONGLONG)obj;
1185 ccw.TypeID = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1186 ccw.IUnk = (ULONGLONG)iUnk;
1187 ccw.RefCount = refCount;
1188 ccw.JupiterRefCount = jupiterRefCount;
1189 ccw.Flags = flags;
1190
1191 if (m_currCcw >= kMaxCcwCount)
1192 FlushCcw();
1193#endif
1194}
1195
1196void BulkComLogger::FlushCcw()
1197{
1198 CONTRACTL
1199 {
1200 NOTHROW;
1201 GC_NOTRIGGER;
1202 MODE_ANY;
1203 }
1204 CONTRACTL_END;
1205
1206 _ASSERTE(m_currCcw <= kMaxCcwCount);
1207
1208 if (m_currCcw == 0)
1209 return;
1210
1211 if (m_typeLogger)
1212 {
1213 for (int i = 0; i < m_currCcw; ++i)
1214 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, m_etwCcwData[i].TypeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1215 }
1216
1217 unsigned short instance = GetClrInstanceId();
1218
1219#if !defined(FEATURE_PAL)
1220 EVENT_DATA_DESCRIPTOR eventData[3];
1221 EventDataDescCreate(&eventData[0], &m_currCcw, sizeof(const unsigned int));
1222 EventDataDescCreate(&eventData[1], &instance, sizeof(const unsigned short));
1223 EventDataDescCreate(&eventData[2], m_etwCcwData, sizeof(EventCCWEntry) * m_currCcw);
1224
1225 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRootCCW, _countof(eventData), eventData);
1226#else
1227 ULONG result = FireEtXplatGCBulkRootCCW(m_currCcw, instance, sizeof(EventCCWEntry) * m_currCcw, m_etwCcwData);
1228#endif //!defined(FEATURE_PAL)
1229
1230 _ASSERTE(result == ERROR_SUCCESS);
1231
1232 m_currCcw = 0;
1233}
1234
1235void BulkComLogger::LogAllComObjects()
1236{
1237 CONTRACTL
1238 {
1239 THROWS;
1240 GC_TRIGGERS;
1241 MODE_ANY;
1242 }
1243 CONTRACTL_END;
1244
1245#ifdef FEATURE_COMINTEROP
1246 SyncBlockCache *cache = SyncBlockCache::GetSyncBlockCache();
1247 if (cache == NULL)
1248 return;
1249
1250 int count = cache->GetTableEntryCount();
1251 SyncTableEntry *table = SyncTableEntry::GetSyncTableEntry();
1252
1253 for (int i = 0; i < count; ++i)
1254 {
1255 SyncTableEntry &entry = table[i];
1256 Object *obj = entry.m_Object.Load();
1257 if (obj && entry.m_SyncBlock)
1258 {
1259 InteropSyncBlockInfo *interop = entry.m_SyncBlock->GetInteropInfoNoCreate();
1260 if (interop)
1261 {
1262 RCW *rcw = interop->GetRawRCW();
1263 if (rcw)
1264 WriteRcw(rcw, obj);
1265 }
1266 }
1267 }
1268
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);
1273
1274 // Now that we have all of the object handles, we will walk all of the handles and write the
1275 // etw events.
1276 for (CCWEnumerationEntry *curr = m_enumResult; curr; curr = curr->Next)
1277 {
1278 for (int i = 0; i < curr->Count; ++i)
1279 {
1280 Object **handle = curr->Handles[i];
1281
1282 Object *obj = NULL;
1283 if (handle == NULL || (obj = *handle) == 0)
1284 return;
1285
1286 ObjHeader *header = obj->GetHeader();
1287 _ASSERTE(header != NULL);
1288
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;
1292
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();
1297 if (index > 0)
1298 {
1299 SyncBlock *syncBlk = header->PassiveGetSyncBlock();
1300 InteropSyncBlockInfo *interop = syncBlk->GetInteropInfoNoCreate();
1301 if (interop)
1302 ccw = interop->GetCCW();
1303 }
1304
1305 WriteCcw(ccw, handle, obj);
1306 }
1307 }
1308
1309#endif
1310
1311}
1312
1313void BulkComLogger::HandleWalkCallback(Object **handle, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2)
1314{
1315 CONTRACTL
1316 {
1317 THROWS;
1318 GC_NOTRIGGER;
1319 MODE_ANY;
1320 PRECONDITION(param1 != NULL); // Should be the "this" pointer for BulkComLogger.
1321 PRECONDITION(param2 == 0); // This is set by Ref_TraceRefCountHandles.
1322 }
1323 CONTRACTL_END;
1324
1325 // Simple sanity check to ensure the parameters are what we expect them to be.
1326 _ASSERTE(param2 == 0);
1327
1328 if (handle != NULL)
1329 ((BulkComLogger*)param1)->AddCcwHandle(handle);
1330}
1331
1332
1333
1334// Used during CCW enumeration to keep track of all object handles which point to a CCW.
1335void BulkComLogger::AddCcwHandle(Object **handle)
1336{
1337 CONTRACTL
1338 {
1339 THROWS;
1340 GC_NOTRIGGER;
1341 MODE_ANY;
1342 PRECONDITION(handle != NULL);
1343 }
1344 CONTRACTL_END;
1345
1346 if (m_enumResult == NULL)
1347 m_enumResult = new CCWEnumerationEntry;
1348
1349 CCWEnumerationEntry *curr = m_enumResult;
1350 while (curr->Next)
1351 curr = curr->Next;
1352
1353 if (curr->Count == _countof(curr->Handles))
1354 {
1355 curr->Next = new CCWEnumerationEntry;
1356 curr = curr->Next;
1357 }
1358
1359 curr->Handles[curr->Count++] = handle;
1360}
1361
1362
1363
1364
1365//---------------------------------------------------------------------------------------
1366// BulkStaticsLogger: Batches up and logs static variable roots
1367//---------------------------------------------------------------------------------------
1368
1369
1370
1371#include "domainfile.h"
1372
1373BulkStaticsLogger::BulkStaticsLogger(BulkTypeEventLogger *typeLogger)
1374 : m_buffer(0), m_used(0), m_count(0), m_domain(0), m_typeLogger(typeLogger)
1375{
1376 CONTRACTL
1377 {
1378 THROWS;
1379 GC_NOTRIGGER;
1380 MODE_ANY;
1381 }
1382 CONTRACTL_END;
1383
1384 m_buffer = new BYTE[kMaxBytesValues];
1385}
1386
1387BulkStaticsLogger::~BulkStaticsLogger()
1388{
1389 CONTRACTL
1390 {
1391 NOTHROW;
1392 GC_NOTRIGGER;
1393 MODE_ANY;
1394 }
1395 CONTRACTL_END;
1396
1397 if (m_used > 0)
1398 FireBulkStaticsEvent();
1399
1400 if (m_buffer)
1401 delete[] m_buffer;
1402}
1403
1404void BulkStaticsLogger::FireBulkStaticsEvent()
1405{
1406 CONTRACTL
1407 {
1408 NOTHROW;
1409 GC_NOTRIGGER;
1410 MODE_ANY;
1411 }
1412 CONTRACTL_END;
1413
1414 if (m_used <= 0 || m_count <= 0)
1415 return;
1416
1417 _ASSERTE(m_domain != NULL);
1418
1419 unsigned short instance = GetClrInstanceId();
1420 unsigned __int64 appDomain = (unsigned __int64)m_domain;
1421
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);
1428
1429 ULONG result = EventWrite(Microsoft_Windows_DotNETRuntimeHandle, &GCBulkRootStaticVar, _countof(eventData), eventData);
1430#else
1431 ULONG result = FireEtXplatGCBulkRootStaticVar(m_count, appDomain, instance, m_used, m_buffer);
1432#endif //!defined(FEATURE_PAL)
1433
1434 _ASSERTE(result == ERROR_SUCCESS);
1435
1436 m_used = 0;
1437 m_count = 0;
1438}
1439
1440void BulkStaticsLogger::WriteEntry(AppDomain *domain, Object **address, Object *obj, FieldDesc *fieldDesc)
1441{
1442 CONTRACTL
1443 {
1444 NOTHROW;
1445 GC_NOTRIGGER;
1446 MODE_ANY;
1447 PRECONDITION(domain != NULL);
1448 PRECONDITION(address != NULL);
1449 PRECONDITION(obj != NULL);
1450 PRECONDITION(fieldDesc != NULL);
1451 }
1452 CONTRACTL_END;
1453
1454 // Each bulk statics event is for one AppDomain. If we are now inspecting a new domain,
1455 // we need to flush the built up events now.
1456 if (m_domain != domain)
1457 {
1458 if (m_domain != NULL)
1459 FireBulkStaticsEvent();
1460
1461 m_domain = domain;
1462 }
1463
1464 ULONGLONG th = (ULONGLONG)obj->GetTypeHandle().AsTAddr();
1465 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(m_typeLogger, th, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
1466
1467 // We should have at least 512 characters remaining in the buffer here.
1468 int remaining = kMaxBytesValues - m_used;
1469 _ASSERTE(kMaxBytesValues - m_used > 512);
1470
1471 int len = EventStaticEntry::WriteEntry(m_buffer + m_used, remaining, (ULONGLONG)address,
1472 (ULONGLONG)obj, th, 0, fieldDesc);
1473
1474 // 512 bytes was not enough buffer? This shouldn't happen, so we'll skip emitting the
1475 // event on error.
1476 if (len > 0)
1477 {
1478 m_used += len;
1479 m_count++;
1480 }
1481
1482 // When we are close to running out of buffer, emit the event.
1483 if (kMaxBytesValues - m_used < 512)
1484 FireBulkStaticsEvent();
1485}
1486
1487void BulkStaticsLogger::LogAllStatics()
1488{
1489 CONTRACTL
1490 {
1491 NOTHROW;
1492 GC_NOTRIGGER;
1493 MODE_ANY;
1494 }
1495 CONTRACTL_END;
1496
1497 // Enumerate only active app domains (first parameter). We use the unsafe
1498 // iterator here because this method is called under the threadstore lock
1499 // and it's safe to use while the runtime is suspended.
1500 UnsafeAppDomainIterator appIter(TRUE);
1501 appIter.Init();
1502 while (appIter.Next())
1503 {
1504 AppDomain *domain = appIter.GetDomain();
1505
1506 AppDomain::AssemblyIterator assemblyIter = domain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded|kIncludeExecution));
1507 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
1508 while (assemblyIter.Next(pDomainAssembly.This()))
1509 {
1510 // Make sure the assembly is loaded.
1511 if (!pDomainAssembly->IsLoaded())
1512 continue;
1513
1514 CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetAssembly();
1515 DomainModuleIterator modIter = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
1516
1517 while (modIter.Next())
1518 {
1519 // Get the domain module from the module/appdomain pair.
1520 Module *module = modIter.GetModule();
1521 if (module == NULL)
1522 continue;
1523
1524 DomainFile *domainFile = module->FindDomainFile(domain);
1525 if (domainFile == NULL)
1526 continue;
1527
1528 // Ensure the module has fully loaded.
1529 if (!domainFile->IsActive())
1530 continue;
1531
1532 DomainLocalModule *domainModule = module->GetDomainLocalModule(domain);
1533 if (domainModule == NULL)
1534 continue;
1535
1536 // Now iterate all types with
1537 LookupMap<PTR_MethodTable>::Iterator mtIter = module->EnumerateTypeDefs();
1538 while (mtIter.Next())
1539 {
1540 // I don't think mt can be null here, but the dac does a null check...
1541 // IsFullyLoaded should be equivalent to 'GetLoadLevel() == CLASS_LOADED'
1542 MethodTable *mt = mtIter.GetElement();
1543 if (mt == NULL || !mt->IsFullyLoaded())
1544 continue;
1545
1546 EEClass *cls = mt->GetClass();
1547 _ASSERTE(cls != NULL);
1548
1549 if (cls->GetNumStaticFields() <= 0)
1550 continue;
1551
1552 ApproxFieldDescIterator fieldIter(mt, ApproxFieldDescIterator::STATIC_FIELDS);
1553 for (FieldDesc *field = fieldIter.Next(); field != NULL; field = fieldIter.Next())
1554 {
1555 // Don't want thread local
1556 _ASSERTE(field->IsStatic());
1557 if (field->IsSpecialStatic() || field->IsEnCNew())
1558 continue;
1559
1560 // Static valuetype values are boxed.
1561 CorElementType fieldType = field->GetFieldType();
1562 if (fieldType != ELEMENT_TYPE_CLASS && fieldType != ELEMENT_TYPE_VALUETYPE)
1563 continue;
1564
1565 BYTE *base = field->GetBaseInDomainLocalModule(domainModule);
1566 if (base == NULL)
1567 continue;
1568
1569 Object **address = (Object**)field->GetStaticAddressHandle(base);
1570 Object *obj = NULL;
1571 if (address == NULL || ((obj = *address) == NULL))
1572 continue;
1573
1574 WriteEntry(domain, address, *address, field);
1575 } // foreach static field
1576 }
1577 } // foreach domain module
1578 } // foreach domain assembly
1579 } // foreach AppDomain
1580} // BulkStaticsLogger::LogAllStatics
1581
1582
1583
1584//---------------------------------------------------------------------------------------
1585// BulkTypeValue / BulkTypeEventLogger: These take care of batching up types so they can
1586// be logged via ETW in bulk
1587//---------------------------------------------------------------------------------------
1588
1589BulkTypeValue::BulkTypeValue() : cTypeParameters(0)
1590#ifdef FEATURE_REDHAWK
1591, ullSingleTypeParameter(0)
1592#else // FEATURE_REDHAWK
1593, sName()
1594#endif // FEATURE_REDHAWK
1595, rgTypeParameters()
1596{
1597 LIMITED_METHOD_CONTRACT;
1598 ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
1599}
1600
1601//---------------------------------------------------------------------------------------
1602//
1603// Clears a BulkTypeValue so it can be reused after the buffer is flushed to ETW
1604//
1605
1606void BulkTypeValue::Clear()
1607{
1608 CONTRACTL
1609 {
1610 THROWS;
1611 GC_NOTRIGGER;
1612 MODE_ANY;
1613 }
1614 CONTRACTL_END;
1615
1616 ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
1617 cTypeParameters = 0;
1618#ifdef FEATURE_REDHAWK
1619 ullSingleTypeParameter = 0;
1620 rgTypeParameters.Release();
1621#else // FEATURE_REDHAWK
1622 sName.Clear();
1623 rgTypeParameters.Clear();
1624#endif // FEATURE_REDHAWK
1625}
1626
1627//---------------------------------------------------------------------------------------
1628//
1629// Fire an ETW event for all the types we batched so far, and then reset our state
1630// so we can start batching new types at the beginning of the array.
1631//
1632//
1633
1634void BulkTypeEventLogger::FireBulkTypeEvent()
1635{
1636 LIMITED_METHOD_CONTRACT;
1637
1638 if (m_nBulkTypeValueCount == 0)
1639 {
1640 // No types were batched up, so nothing to send
1641 return;
1642 }
1643 UINT16 nClrInstanceID = GetClrInstanceId();
1644
1645 if(m_pBulkTypeEventBuffer == NULL)
1646 {
1647 // The buffer could not be allocated when this object was created, so bail.
1648 return;
1649 }
1650
1651 UINT iSize = 0;
1652
1653 for (int iTypeData = 0; iTypeData < m_nBulkTypeValueCount; iTypeData++)
1654 {
1655 BulkTypeValue& target = m_rgBulkTypeValues[iTypeData];
1656
1657 // Do fixed-size data as one bulk copy
1658 memcpy(
1659 m_pBulkTypeEventBuffer + iSize,
1660 &(target.fixedSizedData),
1661 sizeof(target.fixedSizedData));
1662 iSize += sizeof(target.fixedSizedData);
1663
1664 // Do var-sized data individually per field
1665
1666 LPCWSTR wszName = target.sName.GetUnicode();
1667 if (wszName == NULL)
1668 {
1669 m_pBulkTypeEventBuffer[iSize++] = 0;
1670 m_pBulkTypeEventBuffer[iSize++] = 0;
1671 }
1672 else
1673 {
1674 UINT nameSize = (target.sName.GetCount() + 1) * sizeof(WCHAR);
1675 memcpy(m_pBulkTypeEventBuffer + iSize, wszName, nameSize);
1676 iSize += nameSize;
1677 }
1678
1679 // Type parameter count
1680 ULONG params = target.rgTypeParameters.GetCount();
1681
1682 ULONG *ptrInt = (ULONG*)(m_pBulkTypeEventBuffer + iSize);
1683 *ptrInt = params;
1684 iSize += 4;
1685
1686 target.cTypeParameters = params;
1687
1688 // Type parameter array
1689 if (target.cTypeParameters > 0)
1690 {
1691 memcpy(m_pBulkTypeEventBuffer + iSize, target.rgTypeParameters.GetElements(), sizeof(ULONGLONG) * target.cTypeParameters);
1692 iSize += sizeof(ULONGLONG) * target.cTypeParameters;
1693 }
1694 }
1695
1696 FireEtwBulkType(m_nBulkTypeValueCount, GetClrInstanceId(), iSize, m_pBulkTypeEventBuffer);
1697
1698 // Reset state
1699 m_nBulkTypeValueCount = 0;
1700 m_nBulkTypeValueByteCount = 0;
1701}
1702
1703#ifndef FEATURE_REDHAWK
1704
1705//---------------------------------------------------------------------------------------
1706//
1707// Batches a single type into the array, flushing the array to ETW if it fills up. Most
1708// interaction with the type system (to analyze the type) is done here. This does not
1709// recursively batch up any parameter types (for arrays or generics), but does add their
1710// TypeHandles to the rgTypeParameters array. LogTypeAndParameters is responsible for
1711// initiating any recursive calls to deal with type parameters.
1712//
1713// Arguments:
1714// th - TypeHandle to batch
1715//
1716// Return Value:
1717// Index into array of where this type got batched. -1 if there was a failure.
1718//
1719
1720int BulkTypeEventLogger::LogSingleType(TypeHandle th)
1721{
1722 CONTRACTL
1723 {
1724 NOTHROW;
1725 GC_NOTRIGGER;
1726 MODE_ANY;
1727 CAN_TAKE_LOCK; // some of the type system stuff can take locks
1728 }
1729 CONTRACTL_END;
1730
1731 // If there's no room for another type, flush what we've got
1732 if (m_nBulkTypeValueCount == _countof(m_rgBulkTypeValues))
1733 {
1734 FireBulkTypeEvent();
1735 }
1736
1737 _ASSERTE(m_nBulkTypeValueCount < _countof(m_rgBulkTypeValues));
1738
1739 if (!th.IsTypeDesc() && th.GetMethodTable()->IsArray())
1740 {
1741 _ASSERTE(!"BulkTypeEventLogger::LogSingleType called with MethodTable array");
1742 return -1;
1743 }
1744
1745 BulkTypeValue * pVal = &m_rgBulkTypeValues[m_nBulkTypeValueCount];
1746
1747 // Clear out pVal before filling it out (array elements can get reused if there
1748 // are enough types that we need to flush to multiple events). Clearing the
1749 // contained SBuffer can throw, so deal with exceptions
1750 BOOL fSucceeded = FALSE;
1751 EX_TRY
1752 {
1753 pVal->Clear();
1754 fSucceeded = TRUE;
1755 }
1756 EX_CATCH
1757 {
1758 fSucceeded = FALSE;
1759 }
1760 EX_END_CATCH(RethrowCorruptingExceptions);
1761 if (!fSucceeded)
1762 return -1;
1763
1764 pVal->fixedSizedData.TypeID = (ULONGLONG) th.AsTAddr();
1765 pVal->fixedSizedData.ModuleID = (ULONGLONG) (TADDR) th.GetModule();
1766 pVal->fixedSizedData.TypeNameID = (th.GetMethodTable() == NULL) ? 0 : th.GetCl();
1767 pVal->fixedSizedData.Flags = 0;
1768 pVal->fixedSizedData.CorElementType = (BYTE) th.GetInternalCorElementType();
1769
1770 if (th.IsArray())
1771 {
1772 // Normal typedesc array
1773 pVal->fixedSizedData.Flags |= kEtwTypeFlagsArray;
1774
1775 // Fetch TypeHandle of array elements
1776 fSucceeded = FALSE;
1777 EX_TRY
1778 {
1779 pVal->rgTypeParameters.Append((ULONGLONG) th.AsArray()->GetArrayElementTypeHandle().AsTAddr());
1780 fSucceeded = TRUE;
1781 }
1782 EX_CATCH
1783 {
1784 fSucceeded = FALSE;
1785 }
1786 EX_END_CATCH(RethrowCorruptingExceptions);
1787 if (!fSucceeded)
1788 return -1;
1789 }
1790 else if (th.IsTypeDesc())
1791 {
1792 // Non-array Typedescs
1793 PTR_TypeDesc pTypeDesc = th.AsTypeDesc();
1794 if (pTypeDesc->HasTypeParam())
1795 {
1796 fSucceeded = FALSE;
1797 EX_TRY
1798 {
1799 pVal->rgTypeParameters.Append((ULONGLONG) pTypeDesc->GetTypeParam().AsTAddr());
1800 fSucceeded = TRUE;
1801 }
1802 EX_CATCH
1803 {
1804 fSucceeded = FALSE;
1805 }
1806 EX_END_CATCH(RethrowCorruptingExceptions);
1807 if (!fSucceeded)
1808 return -1;
1809 }
1810 }
1811 else
1812 {
1813 // Non-array MethodTable
1814
1815 PTR_MethodTable pMT = th.AsMethodTable();
1816
1817 // Make CorElementType more specific if this is a string MT
1818 if (pMT->IsString())
1819 {
1820 pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_STRING;
1821 }
1822 else if (pMT->IsObjectClass())
1823 {
1824 pVal->fixedSizedData.CorElementType = ELEMENT_TYPE_OBJECT;
1825 }
1826
1827 // Generic arguments
1828 DWORD cTypeParameters = pMT->GetNumGenericArgs();
1829 if (cTypeParameters > 0)
1830 {
1831 Instantiation inst = pMT->GetInstantiation();
1832 fSucceeded = FALSE;
1833 EX_TRY
1834 {
1835 for (DWORD i=0; i < cTypeParameters; i++)
1836 {
1837 pVal->rgTypeParameters.Append((ULONGLONG) inst[i].AsTAddr());
1838 }
1839 fSucceeded = TRUE;
1840 }
1841 EX_CATCH
1842 {
1843 fSucceeded = FALSE;
1844 }
1845 EX_END_CATCH(RethrowCorruptingExceptions);
1846 if (!fSucceeded)
1847 return -1;
1848 }
1849
1850 if (pMT->HasFinalizer())
1851 {
1852 pVal->fixedSizedData.Flags |= kEtwTypeFlagsFinalizable;
1853 }
1854 if (pMT->IsDelegate())
1855 {
1856 pVal->fixedSizedData.Flags |= kEtwTypeFlagsDelegate;
1857 }
1858 if (pMT->IsComObjectType())
1859 {
1860 pVal->fixedSizedData.Flags |= kEtwTypeFlagsExternallyImplementedCOMObject;
1861 }
1862 }
1863
1864 // If the profiler wants it, construct a name. Always normalize the string (even if
1865 // type names are not requested) so that calls to sName.GetCount() can't throw
1866 EX_TRY
1867 {
1868 if (ETW_TRACING_CATEGORY_ENABLED(
1869 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
1870 TRACE_LEVEL_INFORMATION,
1871 CLR_GCHEAPANDTYPENAMES_KEYWORD))
1872 {
1873 th.GetName(pVal->sName);
1874 }
1875 pVal->sName.Normalize();
1876 }
1877 EX_CATCH
1878 {
1879 // If this failed, the name remains empty, which is ok; the event just
1880 // won't have a name in it.
1881 pVal->sName.Clear();
1882 }
1883 EX_END_CATCH(RethrowCorruptingExceptions);
1884
1885 // Now that we know the full size of this type's data, see if it fits in our
1886 // batch or whether we need to flush
1887
1888 int cbVal = pVal->GetByteCountInEvent();
1889 if (cbVal > kMaxBytesTypeValues)
1890 {
1891 // This type is apparently so huge, it's too big to squeeze into an event, even
1892 // if it were the only type batched in the whole event. Bail
1893 _ASSERTE(!"Type too big to log via ETW");
1894 return -1;
1895 }
1896
1897 if (m_nBulkTypeValueByteCount + cbVal > kMaxBytesTypeValues)
1898 {
1899 // Although this type fits into the array, its size is so big that the entire
1900 // array can't be logged via ETW. So flush the array, and start over by
1901 // calling ourselves--this refetches the type info and puts it at the
1902 // beginning of the array. Since we know this type is small enough to be
1903 // batched into an event on its own, this recursive call will not try to
1904 // call itself again.
1905 FireBulkTypeEvent();
1906 return LogSingleType(th);
1907 }
1908
1909 // The type fits into the batch, so update our state
1910 m_nBulkTypeValueCount++;
1911 m_nBulkTypeValueByteCount += cbVal;
1912 return m_nBulkTypeValueCount - 1; // Index of type we just added
1913}
1914
1915//---------------------------------------------------------------------------------------
1916//
1917// High-level method to batch a type and (recursively) its type parameters, flushing to
1918// ETW as needed. This is called by (static)
1919// ETW::TypeSystemLog::LogTypeAndParametersIfNecessary, which is what clients use to log
1920// type events
1921//
1922// Arguments:
1923// * thAsAddr - Type to batch
1924// * typeLogBehavior - Reminder of whether the type system log lock is held
1925// (useful if we need to recurively call back into TypeSystemLog), and whether
1926// we even care to check if the type was already logged
1927//
1928
1929void BulkTypeEventLogger::LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior)
1930{
1931 CONTRACTL
1932 {
1933 NOTHROW;
1934 GC_NOTRIGGER;
1935 MODE_ANY;
1936 CAN_TAKE_LOCK; // LogSingleType can take locks
1937 }
1938 CONTRACTL_END;
1939
1940 TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr);
1941
1942 // Batch up this type. This grabs useful info about the type, including any
1943 // type parameters it may have, and sticks it in m_rgBulkTypeValues
1944 int iBulkTypeEventData = LogSingleType(th);
1945 if (iBulkTypeEventData == -1)
1946 {
1947 // There was a failure trying to log the type, so don't bother with its type
1948 // parameters
1949 return;
1950 }
1951
1952 // Look at the type info we just batched, so we can get the type parameters
1953 BulkTypeValue * pVal = &m_rgBulkTypeValues[iBulkTypeEventData];
1954
1955 // We're about to recursively call ourselves for the type parameters, so make a
1956 // local copy of their type handles first (else, as we log them we could flush
1957 // and clear out m_rgBulkTypeValues, thus trashing pVal)
1958
1959 StackSArray<ULONGLONG> rgTypeParameters;
1960 DWORD cParams = pVal->rgTypeParameters.GetCount();
1961
1962 BOOL fSucceeded = FALSE;
1963 EX_TRY
1964 {
1965 for (COUNT_T i = 0; i < cParams; i++)
1966 {
1967 rgTypeParameters.Append(pVal->rgTypeParameters[i]);
1968 }
1969 fSucceeded = TRUE;
1970 }
1971 EX_CATCH
1972 {
1973 fSucceeded = FALSE;
1974 }
1975 EX_END_CATCH(RethrowCorruptingExceptions);
1976 if (!fSucceeded)
1977 return;
1978
1979 // Before we recurse, adjust the special-cased type-log behavior that allows a
1980 // top-level type to be logged without lookup, but still requires lookups to avoid
1981 // dupes of type parameters
1982 if (typeLogBehavior == ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLogTopLevelType)
1983 typeLogBehavior = ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime;
1984
1985 // Recursively log any referenced parameter types
1986 for (COUNT_T i=0; i < cParams; i++)
1987 {
1988 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(this, rgTypeParameters[i], typeLogBehavior);
1989 }
1990}
1991
1992#endif // FEATURE_REDHAWK
1993
1994// Holds state that batches of roots, nodes, edges, and types as the GC walks the heap
1995// at the end of a collection.
1996class EtwGcHeapDumpContext
1997{
1998public:
1999 // An instance of EtwGcHeapDumpContext is dynamically allocated and stored inside of
2000 // ProfilingScanContext and ProfilerWalkHeapContext, which are context structures
2001 // that the GC heap walker sends back to the callbacks. This method is passed a
2002 // pointer to ProfilingScanContext::pvEtwContext or
2003 // ProfilerWalkHeapContext::pvEtwContext; if non-NULL it gets returned; else, a new
2004 // EtwGcHeapDumpContext is allocated, stored in that pointer, and then returned.
2005 // Callers should test for NULL, which can be returned if out of memory
2006 static EtwGcHeapDumpContext * GetOrCreateInGCContext(LPVOID * ppvEtwContext)
2007 {
2008 LIMITED_METHOD_CONTRACT;
2009
2010 _ASSERTE(ppvEtwContext != NULL);
2011
2012 EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) *ppvEtwContext;
2013 if (pContext == NULL)
2014 {
2015 pContext = new (nothrow) EtwGcHeapDumpContext;
2016 *ppvEtwContext = pContext;
2017 }
2018 return pContext;
2019 }
2020
2021 EtwGcHeapDumpContext() :
2022 iCurBulkRootEdge(0),
2023 iCurBulkRootConditionalWeakTableElementEdge(0),
2024 iCurBulkNodeEvent(0),
2025 iCurBulkEdgeEvent(0),
2026 bulkTypeEventLogger()
2027 {
2028 LIMITED_METHOD_CONTRACT;
2029 ClearRootEdges();
2030 ClearRootConditionalWeakTableElementEdges();
2031 ClearNodes();
2032 ClearEdges();
2033 }
2034
2035 // These helpers clear the individual buffers, for use after a flush and on
2036 // construction. They intentionally leave the indices (iCur*) alone, since they
2037 // persist across flushes within a GC
2038
2039 void ClearRootEdges()
2040 {
2041 LIMITED_METHOD_CONTRACT;
2042 cGcBulkRootEdges = 0;
2043 ZeroMemory(rgGcBulkRootEdges, sizeof(rgGcBulkRootEdges));
2044 }
2045
2046 void ClearRootConditionalWeakTableElementEdges()
2047 {
2048 LIMITED_METHOD_CONTRACT;
2049 cGCBulkRootConditionalWeakTableElementEdges = 0;
2050 ZeroMemory(rgGCBulkRootConditionalWeakTableElementEdges, sizeof(rgGCBulkRootConditionalWeakTableElementEdges));
2051 }
2052
2053 void ClearNodes()
2054 {
2055 LIMITED_METHOD_CONTRACT;
2056 cGcBulkNodeValues = 0;
2057 ZeroMemory(rgGcBulkNodeValues, sizeof(rgGcBulkNodeValues));
2058 }
2059
2060 void ClearEdges()
2061 {
2062 LIMITED_METHOD_CONTRACT;
2063 cGcBulkEdgeValues = 0;
2064 ZeroMemory(rgGcBulkEdgeValues, sizeof(rgGcBulkEdgeValues));
2065 }
2066
2067 //---------------------------------------------------------------------------------------
2068 // GCBulkRootEdge
2069 //
2070 // A "root edge" is the relationship between a source "GCRootID" (i.e., stack
2071 // variable, handle, static, etc.) and the target "RootedNodeAddress" (the managed
2072 // object that gets rooted).
2073 //
2074 //---------------------------------------------------------------------------------------
2075
2076 // Sequence number for each GCBulkRootEdge event
2077 UINT iCurBulkRootEdge;
2078
2079 // Number of root edges currently filled out in rgGcBulkRootEdges array
2080 UINT cGcBulkRootEdges;
2081
2082 // Struct array containing the primary data for each GCBulkRootEdge event. Fix the size so
2083 // the total event stays well below the 64K
2084 // limit (leaving lots of room for non-struct fields that come before the root edge data)
2085 EventStructGCBulkRootEdgeValue rgGcBulkRootEdges[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootEdgeValue)];
2086
2087
2088 //---------------------------------------------------------------------------------------
2089 // GCBulkRootConditionalWeakTableElementEdge
2090 //
2091 // These describe dependent handles, which simulate an edge connecting a key NodeID
2092 // to a value NodeID.
2093 //
2094 //---------------------------------------------------------------------------------------
2095
2096 // Sequence number for each GCBulkRootConditionalWeakTableElementEdge event
2097 UINT iCurBulkRootConditionalWeakTableElementEdge;
2098
2099 // Number of root edges currently filled out in rgGCBulkRootConditionalWeakTableElementEdges array
2100 UINT cGCBulkRootConditionalWeakTableElementEdges;
2101
2102 // Struct array containing the primary data for each GCBulkRootConditionalWeakTableElementEdge event. Fix the size so
2103 // the total event stays well below the 64K
2104 // limit (leaving lots of room for non-struct fields that come before the root edge data)
2105 EventStructGCBulkRootConditionalWeakTableElementEdgeValue rgGCBulkRootConditionalWeakTableElementEdges
2106 [(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkRootConditionalWeakTableElementEdgeValue)];
2107
2108 //---------------------------------------------------------------------------------------
2109 // GCBulkNode
2110 //
2111 // A "node" is ANY managed object sitting on the heap, including RootedNodeAddresses
2112 // as well as leaf nodes.
2113 //
2114 //---------------------------------------------------------------------------------------
2115
2116 // Sequence number for each GCBulkNode event
2117 UINT iCurBulkNodeEvent;
2118
2119 // Number of nodes currently filled out in rgGcBulkNodeValues array
2120 UINT cGcBulkNodeValues;
2121
2122 // Struct array containing the primary data for each GCBulkNode event. Fix the size so
2123 // the total event stays well below the 64K
2124 // limit (leaving lots of room for non-struct fields that come before the node data)
2125 EventStructGCBulkNodeValue rgGcBulkNodeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkNodeValue)];
2126
2127 //---------------------------------------------------------------------------------------
2128 // GCBulkEdge
2129 //
2130 // An "edge" is the relationship between a source node and its referenced target
2131 // node. Edges are reported in bulk, separately from Nodes, but it is expected that
2132 // the consumer read the Node and Edge streams together. One takes the first node
2133 // from the Node stream, and then reads EdgeCount entries in the Edge stream, telling
2134 // you all of that Node's targets. Then, one takes the next node in the Node stream,
2135 // and reads the next entries in the Edge stream (using this Node's EdgeCount to
2136 // determine how many) to find all of its targets. This continues on until the Node
2137 // and Edge streams have been fully read.
2138 //
2139 // GCBulkRootEdges are not duplicated in the GCBulkEdge events. GCBulkEdge events
2140 // begin at the GCBulkRootEdge.RootedNodeAddress and move forward.
2141 //
2142 //---------------------------------------------------------------------------------------
2143
2144 // Sequence number for each GCBulkEdge event
2145 UINT iCurBulkEdgeEvent;
2146
2147 // Number of nodes currently filled out in rgGcBulkEdgeValues array
2148 UINT cGcBulkEdgeValues;
2149
2150 // Struct array containing the primary data for each GCBulkEdge event. Fix the size so
2151 // the total event stays well below the 64K
2152 // limit (leaving lots of room for non-struct fields that come before the edge data)
2153 EventStructGCBulkEdgeValue rgGcBulkEdgeValues[(cbMaxEtwEvent - 0x100) / sizeof(EventStructGCBulkEdgeValue)];
2154
2155
2156 //---------------------------------------------------------------------------------------
2157 // BulkType
2158 //
2159 // Types are a bit more complicated to batch up, since their data is of varying
2160 // size. BulkTypeEventLogger takes care of the pesky details for us
2161 //---------------------------------------------------------------------------------------
2162
2163 BulkTypeEventLogger bulkTypeEventLogger;
2164};
2165
2166
2167
2168//---------------------------------------------------------------------------------------
2169//
2170// Called during a heap walk for each root reference encountered. Batches up the root in
2171// the ETW context
2172//
2173// Arguments:
2174// * pvHandle - If the root is a handle, this points to the handle
2175// * pRootedNode - Points to object that is rooted
2176// * pSecondaryNodeForDependentHandle - For dependent handles, this is the
2177// secondary object
2178// * fDependentHandle - nonzero iff this is for a dependent handle
2179// * profilingScanContext - The shared profapi/etw context built up during the heap walk.
2180// * dwGCFlags - Bitmask of "GC_"-style flags set by GC
2181// * rootFlags - Bitmask of EtwGCRootFlags describing the root
2182//
2183
2184// static
2185VOID ETW::GCLog::RootReference(
2186 LPVOID pvHandle,
2187 Object * pRootedNode,
2188 Object * pSecondaryNodeForDependentHandle,
2189 BOOL fDependentHandle,
2190 ProfilingScanContext * profilingScanContext,
2191 DWORD dwGCFlags,
2192 DWORD rootFlags)
2193{
2194 LIMITED_METHOD_CONTRACT;
2195
2196 EtwGcHeapDumpContext * pContext =
2197 EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilingScanContext->pvEtwContext);
2198 if (pContext == NULL)
2199 return;
2200
2201 // Determine root kind, root ID, and handle-specific flags
2202 LPVOID pvRootID = NULL;
2203 BYTE nRootKind = (BYTE) profilingScanContext->dwEtwRootKind;
2204 switch (nRootKind)
2205 {
2206 case kEtwGCRootKindStack:
2207#if !defined (FEATURE_REDHAWK) && (defined(GC_PROFILING) || defined (DACCESS_COMPILE))
2208 pvRootID = profilingScanContext->pMD;
2209#endif // !defined (FEATURE_REDHAWK) && (defined(GC_PROFILING) || defined (DACCESS_COMPILE))
2210 break;
2211
2212 case kEtwGCRootKindHandle:
2213 pvRootID = pvHandle;
2214 break;
2215
2216 case kEtwGCRootKindFinalizer:
2217 _ASSERTE(pvRootID == NULL);
2218 break;
2219
2220 case kEtwGCRootKindOther:
2221 default:
2222 _ASSERTE(nRootKind == kEtwGCRootKindOther);
2223 _ASSERTE(pvRootID == NULL);
2224 break;
2225 }
2226
2227 // Convert GC root flags to ETW root flags
2228 if (dwGCFlags & GC_CALL_INTERIOR)
2229 rootFlags |= kEtwGCRootFlagsInterior;
2230 if (dwGCFlags & GC_CALL_PINNED)
2231 rootFlags |= kEtwGCRootFlagsPinning;
2232
2233 // Add root edge to appropriate buffer
2234 if (fDependentHandle)
2235 {
2236 _ASSERTE(pContext->cGCBulkRootConditionalWeakTableElementEdges <
2237 _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges));
2238 EventStructGCBulkRootConditionalWeakTableElementEdgeValue * pRCWTEEdgeValue =
2239 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[pContext->cGCBulkRootConditionalWeakTableElementEdges];
2240 pRCWTEEdgeValue->GCKeyNodeID = pRootedNode;
2241 pRCWTEEdgeValue->GCValueNodeID = pSecondaryNodeForDependentHandle;
2242 pRCWTEEdgeValue->GCRootID = pvRootID;
2243 pContext->cGCBulkRootConditionalWeakTableElementEdges++;
2244
2245 // If RCWTE edge buffer is now full, empty it into ETW
2246 if (pContext->cGCBulkRootConditionalWeakTableElementEdges ==
2247 _countof(pContext->rgGCBulkRootConditionalWeakTableElementEdges))
2248 {
2249 FireEtwGCBulkRootConditionalWeakTableElementEdge(
2250 pContext->iCurBulkRootConditionalWeakTableElementEdge,
2251 pContext->cGCBulkRootConditionalWeakTableElementEdges,
2252 GetClrInstanceId(),
2253 sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]),
2254 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]);
2255
2256 pContext->iCurBulkRootConditionalWeakTableElementEdge++;
2257 pContext->ClearRootConditionalWeakTableElementEdges();
2258 }
2259 }
2260 else
2261 {
2262 _ASSERTE(pContext->cGcBulkRootEdges < _countof(pContext->rgGcBulkRootEdges));
2263 EventStructGCBulkRootEdgeValue * pBulkRootEdgeValue = &pContext->rgGcBulkRootEdges[pContext->cGcBulkRootEdges];
2264 pBulkRootEdgeValue->RootedNodeAddress = pRootedNode;
2265 pBulkRootEdgeValue->GCRootKind = nRootKind;
2266 pBulkRootEdgeValue->GCRootFlag = rootFlags;
2267 pBulkRootEdgeValue->GCRootID = pvRootID;
2268 pContext->cGcBulkRootEdges++;
2269
2270 // If root edge buffer is now full, empty it into ETW
2271 if (pContext->cGcBulkRootEdges == _countof(pContext->rgGcBulkRootEdges))
2272 {
2273 FireEtwGCBulkRootEdge(
2274 pContext->iCurBulkRootEdge,
2275 pContext->cGcBulkRootEdges,
2276 GetClrInstanceId(),
2277 sizeof(pContext->rgGcBulkRootEdges[0]),
2278 &pContext->rgGcBulkRootEdges[0]);
2279
2280 pContext->iCurBulkRootEdge++;
2281 pContext->ClearRootEdges();
2282 }
2283 }
2284}
2285
2286//---------------------------------------------------------------------------------------
2287//
2288// Called during a heap walk for each object reference encountered. Batches up the
2289// corresponding node, edges, and type data for the ETW events.
2290//
2291// Arguments:
2292// * profilerWalkHeapContext - The shared profapi/etw context built up during the heap walk.
2293// * pObjReferenceSource - Object doing the pointing
2294// * typeID - Type of pObjReferenceSource
2295// * fDependentHandle - nonzero iff this is for a dependent handle
2296// * cRefs - Count of objects being pointed to
2297// * rgObjReferenceTargets - Array of objects being pointed to
2298//
2299
2300// static
2301VOID ETW::GCLog::ObjectReference(
2302 ProfilerWalkHeapContext * profilerWalkHeapContext,
2303 Object * pObjReferenceSource,
2304 ULONGLONG typeID,
2305 ULONGLONG cRefs,
2306 Object ** rgObjReferenceTargets)
2307{
2308 CONTRACTL
2309 {
2310 NOTHROW;
2311 GC_NOTRIGGER;
2312 MODE_ANY;
2313
2314 // LogTypeAndParametersIfNecessary can take a lock
2315 CAN_TAKE_LOCK;
2316 }
2317 CONTRACTL_END;
2318
2319 EtwGcHeapDumpContext * pContext =
2320 EtwGcHeapDumpContext::GetOrCreateInGCContext(&profilerWalkHeapContext->pvEtwContext);
2321 if (pContext == NULL)
2322 return;
2323
2324 //---------------------------------------------------------------------------------------
2325 // GCBulkNode events
2326 //---------------------------------------------------------------------------------------
2327
2328 // Add Node (pObjReferenceSource) to buffer
2329 _ASSERTE(pContext->cGcBulkNodeValues < _countof(pContext->rgGcBulkNodeValues));
2330 EventStructGCBulkNodeValue * pBulkNodeValue = &pContext->rgGcBulkNodeValues[pContext->cGcBulkNodeValues];
2331 pBulkNodeValue->Address = pObjReferenceSource;
2332 pBulkNodeValue->Size = pObjReferenceSource->GetSize();
2333 pBulkNodeValue->TypeID = typeID;
2334 pBulkNodeValue->EdgeCount = cRefs;
2335 pContext->cGcBulkNodeValues++;
2336
2337 // If Node buffer is now full, empty it into ETW
2338 if (pContext->cGcBulkNodeValues == _countof(pContext->rgGcBulkNodeValues))
2339 {
2340 FireEtwGCBulkNode(
2341 pContext->iCurBulkNodeEvent,
2342 pContext->cGcBulkNodeValues,
2343 GetClrInstanceId(),
2344 sizeof(pContext->rgGcBulkNodeValues[0]),
2345 &pContext->rgGcBulkNodeValues[0]);
2346
2347 pContext->iCurBulkNodeEvent++;
2348 pContext->ClearNodes();
2349 }
2350
2351 //---------------------------------------------------------------------------------------
2352 // BulkType events
2353 //---------------------------------------------------------------------------------------
2354
2355 // We send type information as necessary--only for nodes, and only for nodes that we
2356 // haven't already sent type info for
2357 if (typeID != 0)
2358 {
2359 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(
2360 &pContext->bulkTypeEventLogger, // Batch up this type with others to minimize events
2361 typeID,
2362
2363 // During heap walk, GC holds the lock for us, so we can directly enter the
2364 // hash to see if the type has already been logged
2365 ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime
2366 );
2367 }
2368
2369 //---------------------------------------------------------------------------------------
2370 // GCBulkEdge events
2371 //---------------------------------------------------------------------------------------
2372
2373 // Add Edges (rgObjReferenceTargets) to buffer. Buffer could fill up before all edges
2374 // are added (it could even fill up multiple times during this one call if there are
2375 // a lot of edges), so empty Edge buffer into ETW as we go along, as many times as we
2376 // need.
2377
2378 for (ULONGLONG i=0; i < cRefs; i++)
2379 {
2380 _ASSERTE(pContext->cGcBulkEdgeValues < _countof(pContext->rgGcBulkEdgeValues));
2381 EventStructGCBulkEdgeValue * pBulkEdgeValue = &pContext->rgGcBulkEdgeValues[pContext->cGcBulkEdgeValues];
2382 pBulkEdgeValue->Value = rgObjReferenceTargets[i];
2383 // FUTURE: ReferencingFieldID
2384 pBulkEdgeValue->ReferencingFieldID = 0;
2385 pContext->cGcBulkEdgeValues++;
2386
2387 // If Edge buffer is now full, empty it into ETW
2388 if (pContext->cGcBulkEdgeValues == _countof(pContext->rgGcBulkEdgeValues))
2389 {
2390 FireEtwGCBulkEdge(
2391 pContext->iCurBulkEdgeEvent,
2392 pContext->cGcBulkEdgeValues,
2393 GetClrInstanceId(),
2394 sizeof(pContext->rgGcBulkEdgeValues[0]),
2395 &pContext->rgGcBulkEdgeValues[0]);
2396
2397 pContext->iCurBulkEdgeEvent++;
2398 pContext->ClearEdges();
2399 }
2400 }
2401}
2402
2403//---------------------------------------------------------------------------------------
2404//
2405// Called by GC at end of heap dump to give us a convenient time to flush any remaining
2406// buffers of data to ETW
2407//
2408// Arguments:
2409// profilerWalkHeapContext - Context containing data we've batched up
2410//
2411
2412// static
2413VOID ETW::GCLog::EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext)
2414{
2415 LIMITED_METHOD_CONTRACT;
2416
2417 // If context isn't already set up for us, then we haven't been collecting any data
2418 // for ETW events.
2419 EtwGcHeapDumpContext * pContext = (EtwGcHeapDumpContext *) profilerWalkHeapContext->pvEtwContext;
2420 if (pContext == NULL)
2421 return;
2422
2423 // If the GC events are enabled, flush any remaining root, node, and / or edge data
2424 if (s_forcedGCInProgress &&
2425 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
2426 TRACE_LEVEL_INFORMATION,
2427 CLR_GCHEAPDUMP_KEYWORD))
2428 {
2429 if (pContext->cGcBulkRootEdges > 0)
2430 {
2431 FireEtwGCBulkRootEdge(
2432 pContext->iCurBulkRootEdge,
2433 pContext->cGcBulkRootEdges,
2434 GetClrInstanceId(),
2435 sizeof(pContext->rgGcBulkRootEdges[0]),
2436 &pContext->rgGcBulkRootEdges[0]);
2437 }
2438
2439 if (pContext->cGCBulkRootConditionalWeakTableElementEdges > 0)
2440 {
2441 FireEtwGCBulkRootConditionalWeakTableElementEdge(
2442 pContext->iCurBulkRootConditionalWeakTableElementEdge,
2443 pContext->cGCBulkRootConditionalWeakTableElementEdges,
2444 GetClrInstanceId(),
2445 sizeof(pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]),
2446 &pContext->rgGCBulkRootConditionalWeakTableElementEdges[0]);
2447 }
2448
2449 if (pContext->cGcBulkNodeValues > 0)
2450 {
2451 FireEtwGCBulkNode(
2452 pContext->iCurBulkNodeEvent,
2453 pContext->cGcBulkNodeValues,
2454 GetClrInstanceId(),
2455 sizeof(pContext->rgGcBulkNodeValues[0]),
2456 &pContext->rgGcBulkNodeValues[0]);
2457 }
2458
2459 if (pContext->cGcBulkEdgeValues > 0)
2460 {
2461 FireEtwGCBulkEdge(
2462 pContext->iCurBulkEdgeEvent,
2463 pContext->cGcBulkEdgeValues,
2464 GetClrInstanceId(),
2465 sizeof(pContext->rgGcBulkEdgeValues[0]),
2466 &pContext->rgGcBulkEdgeValues[0]);
2467 }
2468 }
2469
2470 // Ditto for type events
2471 if (ETW_TRACING_CATEGORY_ENABLED(
2472 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
2473 TRACE_LEVEL_INFORMATION,
2474 CLR_TYPE_KEYWORD))
2475 {
2476 pContext->bulkTypeEventLogger.FireBulkTypeEvent();
2477 }
2478
2479 // Delete any GC state built up in the context
2480 profilerWalkHeapContext->pvEtwContext = NULL;
2481 delete pContext;
2482}
2483
2484
2485//---------------------------------------------------------------------------------------
2486//
2487// Helper to send public finalize object & type events, and private finalize object
2488// event. If Type events are enabled, this will send the Type event for the finalized
2489// objects. It will not be batched with other types (except type parameters, if any),
2490// and will not check if the Type has already been logged (may thus result in dupe
2491// logging of the Type).
2492//
2493// Arguments:
2494// pMT - MT of object getting finalized
2495// pObj - object getting finalized
2496//
2497
2498// static
2499VOID ETW::GCLog::SendFinalizeObjectEvent(MethodTable * pMT, Object * pObj)
2500{
2501 CONTRACTL
2502 {
2503 NOTHROW;
2504 GC_NOTRIGGER;
2505 MODE_ANY;
2506
2507 // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to
2508 CAN_TAKE_LOCK;
2509 }
2510 CONTRACTL_END;
2511
2512 // Send public finalize object event, if it's enabled
2513 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, FinalizeObject))
2514 {
2515 FireEtwFinalizeObject(pMT, pObj, GetClrInstanceId());
2516
2517 // This function checks if type events are enabled; if so, it sends event for
2518 // finalized object's type (and parameter types, if any)
2519 ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(
2520 NULL, // Not batching this type with others
2521 (TADDR) pMT,
2522
2523 // Don't spend the time entering the lock and checking the hash table to see
2524 // if we've already logged the type; just log it (if type events are enabled).
2525 ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog
2526 );
2527 }
2528
2529 // Send private finalize object event, if it's enabled
2530 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PrvFinalizeObject))
2531 {
2532 EX_TRY
2533 {
2534 DefineFullyQualifiedNameForClassWOnStack();
2535 FireEtwPrvFinalizeObject(pMT, pObj, GetClrInstanceId(), GetFullyQualifiedNameForClassNestedAwareW(pMT));
2536 }
2537 EX_CATCH
2538 {
2539 }
2540 EX_END_CATCH(RethrowCorruptingExceptions);
2541 }
2542}
2543
2544
2545DWORD ETW::ThreadLog::GetEtwThreadFlags(Thread * pThread)
2546{
2547 LIMITED_METHOD_CONTRACT;
2548
2549 DWORD dwEtwThreadFlags = 0;
2550
2551 if (pThread->IsThreadPoolThread())
2552 {
2553 dwEtwThreadFlags |= kEtwThreadFlagThreadPoolWorker;
2554 }
2555 if (pThread->IsGCSpecial())
2556 {
2557 dwEtwThreadFlags |= kEtwThreadFlagGCSpecial;
2558 }
2559 if (IsGarbageCollectorFullyInitialized() &&
2560 (pThread == FinalizerThread::GetFinalizerThread()))
2561 {
2562 dwEtwThreadFlags |= kEtwThreadFlagFinalizer;
2563 }
2564
2565 return dwEtwThreadFlags;
2566}
2567
2568VOID ETW::ThreadLog::FireThreadCreated(Thread * pThread)
2569{
2570 LIMITED_METHOD_CONTRACT;
2571
2572 FireEtwThreadCreated(
2573 (ULONGLONG)pThread,
2574 (ULONGLONG)pThread->GetDomain(),
2575 GetEtwThreadFlags(pThread),
2576 pThread->GetThreadId(),
2577 pThread->GetOSThreadId(),
2578 GetClrInstanceId());
2579}
2580
2581VOID ETW::ThreadLog::FireThreadDC(Thread * pThread)
2582{
2583 LIMITED_METHOD_CONTRACT;
2584
2585 FireEtwThreadDC(
2586 (ULONGLONG)pThread,
2587 (ULONGLONG)pThread->GetDomain(),
2588 GetEtwThreadFlags(pThread),
2589 pThread->GetThreadId(),
2590 pThread->GetOSThreadId(),
2591 GetClrInstanceId());
2592}
2593
2594
2595
2596#ifndef FEATURE_REDHAWK
2597
2598// TypeSystemLog implementation
2599//
2600// We keep track of which TypeHandles have been logged, and stats on instances of these
2601// TypeHandles that have been allocated, by a hash table of hash tables. The outer hash
2602// table maps Module*'s to an inner hash table that contains all the TypeLoggingInfos for that
2603// Module*. Arranging things this way makes it easy to deal with Module unloads, as we
2604// can simply remove the corresponding inner hash table from the outer hash table.
2605
2606// The following help define the "inner" hash table: a hash table of TypeLoggingInfos
2607// from a particular Module (key = TypeHandle, value = TypeLoggingInfo.
2608
2609class LoggedTypesFromModuleTraits : public NoRemoveSHashTraits< DefaultSHashTraits<ETW::TypeLoggingInfo> >
2610{
2611public:
2612
2613 // explicitly declare local typedefs for these traits types, otherwise
2614 // the compiler may get confused
2615 typedef NoRemoveSHashTraits< DefaultSHashTraits<ETW::TypeLoggingInfo> > PARENT;
2616 typedef PARENT::element_t element_t;
2617 typedef PARENT::count_t count_t;
2618
2619 typedef TypeHandle key_t;
2620
2621 static key_t GetKey(const element_t &e)
2622 {
2623 LIMITED_METHOD_CONTRACT;
2624 return e.th;
2625 }
2626
2627 static BOOL Equals(key_t k1, key_t k2)
2628 {
2629 LIMITED_METHOD_CONTRACT;
2630 return (k1 == k2);
2631 }
2632
2633 static count_t Hash(key_t k)
2634 {
2635 LIMITED_METHOD_CONTRACT;
2636 return (count_t) k.AsTAddr();
2637 }
2638
2639 static bool IsNull(const element_t &e)
2640 {
2641 LIMITED_METHOD_CONTRACT;
2642 return (e.th.AsTAddr() == NULL);
2643 }
2644
2645 static const element_t Null()
2646 {
2647 LIMITED_METHOD_CONTRACT;
2648 return ETW::TypeLoggingInfo(NULL);
2649 }
2650};
2651typedef SHash<LoggedTypesFromModuleTraits> LoggedTypesFromModuleHash;
2652
2653// The inner hash table is housed inside this class, which acts as an entry in the outer
2654// hash table.
2655class ETW::LoggedTypesFromModule
2656{
2657public:
2658 Module * pModule;
2659 LoggedTypesFromModuleHash loggedTypesFromModuleHash;
2660
2661 // These are used by the outer hash table (mapping Module*'s to instances of
2662 // LoggedTypesFromModule).
2663 static COUNT_T Hash(Module * pModule)
2664 {
2665 LIMITED_METHOD_CONTRACT;
2666 return (COUNT_T) (SIZE_T) pModule;
2667 }
2668 Module * GetKey()
2669 {
2670 LIMITED_METHOD_CONTRACT;
2671 return pModule;
2672 }
2673
2674 LoggedTypesFromModule(Module * pModuleParam) : loggedTypesFromModuleHash()
2675 {
2676 LIMITED_METHOD_CONTRACT;
2677 pModule = pModuleParam;
2678 }
2679
2680 ~LoggedTypesFromModule()
2681 {
2682 LIMITED_METHOD_CONTRACT;
2683 }
2684};
2685
2686// The following define the outer hash table (mapping Module*'s to instances of
2687// LoggedTypesFromModule).
2688
2689class AllLoggedTypesTraits : public DefaultSHashTraits<ETW::LoggedTypesFromModule *>
2690{
2691public:
2692
2693 // explicitly declare local typedefs for these traits types, otherwise
2694 // the compiler may get confused
2695 typedef DefaultSHashTraits<ETW::LoggedTypesFromModule *> PARENT;
2696 typedef PARENT::element_t element_t;
2697 typedef PARENT::count_t count_t;
2698
2699 typedef Module * key_t;
2700
2701 static key_t GetKey(const element_t &e)
2702 {
2703 LIMITED_METHOD_CONTRACT;
2704 return e->pModule;
2705 }
2706
2707 static BOOL Equals(key_t k1, key_t k2)
2708 {
2709 LIMITED_METHOD_CONTRACT;
2710 return (k1 == k2);
2711 }
2712
2713 static count_t Hash(key_t k)
2714 {
2715 LIMITED_METHOD_CONTRACT;
2716 return (count_t) (size_t) k;
2717 }
2718
2719 static bool IsNull(const element_t &e)
2720 {
2721 LIMITED_METHOD_CONTRACT;
2722 return (e == NULL);
2723 }
2724
2725 static element_t Null()
2726 {
2727 LIMITED_METHOD_CONTRACT;
2728 return NULL;
2729 }
2730};
2731
2732typedef SHash<AllLoggedTypesTraits> AllLoggedTypesHash;
2733
2734// The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule) is
2735// housed in this struct, which is dynamically allocated the first time we decide we need
2736// it.
2737struct AllLoggedTypes
2738{
2739public:
2740 // This Crst protects the entire outer & inner hash tables. On a GC heap walk, it
2741 // is entered once for the duration of the walk, so that we can freely access the
2742 // hash tables during the walk. On each object allocation, this Crst must be
2743 // entered individually each time.
2744 static CrstStatic s_cs;
2745
2746 // A thread local copy of the global epoch.
2747 // This value is used by each thread to ensure that the thread local data structures
2748 // are in sync with the global state.
2749 unsigned int nEpoch;
2750
2751 // The outer hash table (mapping Module*'s to instances of LoggedTypesFromModule)
2752 AllLoggedTypesHash allLoggedTypesHash;
2753};
2754
2755
2756CrstStatic AllLoggedTypes::s_cs;
2757AllLoggedTypes * ETW::TypeSystemLog::s_pAllLoggedTypes = NULL;
2758unsigned int ETW::TypeSystemLog::s_nEpoch = 0;
2759BOOL ETW::TypeSystemLog::s_fHeapAllocEventEnabledOnStartup = FALSE;
2760BOOL ETW::TypeSystemLog::s_fHeapAllocHighEventEnabledNow = FALSE;
2761BOOL ETW::TypeSystemLog::s_fHeapAllocLowEventEnabledNow = FALSE;
2762int ETW::TypeSystemLog::s_nCustomMsBetweenEvents = 0;
2763
2764
2765//---------------------------------------------------------------------------------------
2766//
2767// Initializes TypeSystemLog (specifically its crst). Called just before ETW providers
2768// are registered with the OS
2769//
2770// Return Value:
2771// HRESULT indicating success or failure
2772//
2773
2774// static
2775HRESULT ETW::TypeSystemLog::PreRegistrationInit()
2776{
2777 LIMITED_METHOD_CONTRACT;
2778
2779 if (!AllLoggedTypes::s_cs.InitNoThrow(
2780 CrstEtwTypeLogHash,
2781 CRST_UNSAFE_ANYMODE)) // This lock is taken during a GC while walking the heap
2782 {
2783 return E_FAIL;
2784 }
2785
2786 return S_OK;
2787}
2788
2789//---------------------------------------------------------------------------------------
2790//
2791// Initializes TypeSystemLog (specifically its crst). Called just after ETW providers
2792// are registered with the OS
2793//
2794// Return Value:
2795// HRESULT indicating success or failure
2796//
2797
2798// static
2799void ETW::TypeSystemLog::PostRegistrationInit()
2800{
2801 LIMITED_METHOD_CONTRACT;
2802
2803 // Initialize our "current state" BOOLs that remember if low or high allocation
2804 // sampling is turned on
2805 s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD);
2806 s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD);
2807
2808 // Snapshot the current state of the object allocated keyword (on startup), and rely
2809 // on this snapshot for the rest of the process run. Since these events require the
2810 // slow alloc JIT helper to be enabled, and that can only be done on startup, we
2811 // remember in this BOOL that we did so, so that we can prevent the object allocated
2812 // event from being fired if the fast allocation helper were enabled but had to
2813 // degrade down to the slow helper (e.g., thread ran over its allocation limit). This
2814 // keeps things consistent.
2815 s_fHeapAllocEventEnabledOnStartup = (s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow);
2816
2817 if (s_fHeapAllocEventEnabledOnStartup)
2818 {
2819 // Determine if a COMPLUS env var is overriding the frequency for the sampled
2820 // object allocated events
2821
2822 // Config value intentionally typed as string, b/c DWORD intepretation is hard-coded
2823 // to hex, which is not what the user would expect. This way I can force the
2824 // conversion to use decimal.
2825 NewArrayHolder<WCHAR> wszCustomObjectAllocationEventsPerTypePerSec(NULL);
2826 if (FAILED(CLRConfig::GetConfigValue(
2827 CLRConfig::UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec,
2828 &wszCustomObjectAllocationEventsPerTypePerSec)) ||
2829 (wszCustomObjectAllocationEventsPerTypePerSec == NULL))
2830 {
2831 return;
2832 }
2833 LPWSTR endPtr;
2834 DWORD dwCustomObjectAllocationEventsPerTypePerSec = wcstoul(
2835 wszCustomObjectAllocationEventsPerTypePerSec,
2836 &endPtr,
2837 10 // Base 10 conversion
2838 );
2839
2840 if (dwCustomObjectAllocationEventsPerTypePerSec == ULONG_MAX)
2841 dwCustomObjectAllocationEventsPerTypePerSec = 0;
2842 if (dwCustomObjectAllocationEventsPerTypePerSec != 0)
2843 {
2844 // MsBetweenEvents = (1000 ms/sec) / (custom desired events/sec)
2845 s_nCustomMsBetweenEvents = 1000 / dwCustomObjectAllocationEventsPerTypePerSec;
2846 }
2847 }
2848}
2849
2850
2851//---------------------------------------------------------------------------------------
2852//
2853// Update object allocation sampling frequency and / or Type hash table contents based
2854// on what keywords were changed.
2855//
2856
2857// static
2858void ETW::TypeSystemLog::OnKeywordsChanged()
2859{
2860 LIMITED_METHOD_CONTRACT;
2861
2862 // If the desired frequencey for the GCSampledObjectAllocation events has changed,
2863 // update our state.
2864 s_fHeapAllocLowEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCLOW_KEYWORD);
2865 s_fHeapAllocHighEventEnabledNow = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_GCHEAPALLOCHIGH_KEYWORD);
2866
2867 // FUTURE: Would be nice here to log an error event if (s_fHeapAllocLowEventEnabledNow ||
2868 // s_fHeapAllocHighEventEnabledNow), but !s_fHeapAllocEventEnabledOnStartup
2869
2870 // If the type events should be turned off, eliminate the hash tables that tracked
2871 // which types were logged. (If type events are turned back on later, we'll re-log
2872 // them all as we encounter them.) Note that all we can really test for is that the
2873 // Types keyword on the runtime provider is off. Not necessarily that it was on and
2874 // was just turned off with this request. But either way, TypeSystemLog can handle it
2875 // because it is extremely smart.
2876 if (!ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, CLR_TYPE_KEYWORD))
2877 OnTypesKeywordTurnedOff();
2878}
2879
2880
2881//---------------------------------------------------------------------------------------
2882//
2883// Based on keywords alone, determine the what the default sampling rate should be for
2884// object allocation events. (This function does not consider any COMPLUS overrides for
2885// the sampling rate.)
2886//
2887
2888// static
2889int ETW::TypeSystemLog::GetDefaultMsBetweenEvents()
2890{
2891 LIMITED_METHOD_CONTRACT;
2892
2893 // We should only get here if the allocation event is enabled. In spirit, this assert
2894 // is correct, but a race could cause the assert to fire (if someone toggled the
2895 // event off after we decided that the event was on and we started down the path of
2896 // calculating statistics to fire the event). In such a case we'll end up returning
2897 // k_nDefaultMsBetweenEventsLow below, but next time we won't get here as we'll know
2898 // early enough not to fire the event.
2899 //_ASSERTE(IsHeapAllocEventEnabled());
2900
2901 // MsBetweenEvents = (1000 ms/sec) / (desired events/sec)
2902 const int k_nDefaultMsBetweenEventsHigh = 1000 / 100; // 100 events per type per sec
2903 const int k_nDefaultMsBetweenEventsLow = 1000 / 5; // 5 events per type per sec
2904
2905 // If both are set, High takes precedence
2906 if (s_fHeapAllocHighEventEnabledNow)
2907 {
2908 return k_nDefaultMsBetweenEventsHigh;
2909 }
2910 return k_nDefaultMsBetweenEventsLow;
2911}
2912
2913//---------------------------------------------------------------------------------------
2914//
2915// Use this to decide whether to fire the object allocation event
2916//
2917// Return Value:
2918// nonzero iff we should fire the event.
2919//
2920
2921// static
2922BOOL ETW::TypeSystemLog::IsHeapAllocEventEnabled()
2923{
2924 LIMITED_METHOD_CONTRACT;
2925
2926 return
2927 // Only fire the event if it was enabled at startup (and thus the slow-JIT new
2928 // helper is used in all cases)
2929 s_fHeapAllocEventEnabledOnStartup &&
2930
2931 // AND a keyword is still enabled. (Thus people can turn off the event
2932 // whenever they want; but they cannot turn it on unless it was also on at startup.)
2933 (s_fHeapAllocHighEventEnabledNow || s_fHeapAllocLowEventEnabledNow);
2934}
2935
2936//---------------------------------------------------------------------------------------
2937//
2938// Helper that adds (or updates) the TypeLoggingInfo inside the inner hash table passed
2939// in.
2940//
2941// Arguments:
2942// * pLoggedTypesFromModule - Inner hash table to update
2943// * pTypeLoggingInfo - TypeLoggingInfo to store
2944//
2945// Return Value:
2946// nonzero iff the add/replace was successful.
2947//
2948
2949// static
2950BOOL ETW::TypeSystemLog::AddOrReplaceTypeLoggingInfo(ETW::LoggedTypesFromModule * pLoggedTypesFromModule, const ETW::TypeLoggingInfo * pTypeLoggingInfo)
2951{
2952 LIMITED_METHOD_CONTRACT;
2953
2954 _ASSERTE(pLoggedTypesFromModule != NULL);
2955
2956 BOOL fSucceeded = FALSE;
2957 EX_TRY
2958 {
2959 pLoggedTypesFromModule->loggedTypesFromModuleHash.AddOrReplace(*pTypeLoggingInfo);
2960 fSucceeded = TRUE;
2961 }
2962 EX_CATCH
2963 {
2964 fSucceeded = FALSE;
2965 }
2966 EX_END_CATCH(RethrowCorruptingExceptions);
2967
2968 return fSucceeded;
2969}
2970
2971//---------------------------------------------------------------------------------------
2972//
2973// Records stats about the object's allocation, and determines based on those stats whether
2974// to fires the high / low frequency GCSampledObjectAllocation ETW event
2975//
2976// Arguments:
2977// * pObject - Allocated object to log
2978// * th - TypeHandle for the object
2979//
2980
2981// static
2982void ETW::TypeSystemLog::SendObjectAllocatedEvent(Object * pObject)
2983{
2984 CONTRACTL
2985 {
2986 NOTHROW;
2987 GC_NOTRIGGER;
2988 MODE_COOPERATIVE;
2989 }
2990 CONTRACTL_END;
2991
2992 // No-op if the appropriate keywords were not enabled on startup (or we're not yet
2993 // started up)
2994 if (!s_fHeapAllocEventEnabledOnStartup || !g_fEEStarted)
2995 return;
2996
2997 TypeHandle th = pObject->GetTypeHandle();
2998
2999 SIZE_T size = pObject->GetSize();
3000 if (size < MIN_OBJECT_SIZE)
3001 {
3002 size = PtrAlign(size);
3003 }
3004
3005 SIZE_T nTotalSizeForTypeSample = size;
3006 DWORD dwTickNow = GetTickCount();
3007 DWORD dwObjectCountForTypeSample = 0;
3008
3009 // Get stats for type
3010 TypeLoggingInfo typeLoggingInfo(NULL);
3011 LoggedTypesFromModule * pLoggedTypesFromModule = NULL;
3012 BOOL fCreatedNew = FALSE;
3013 typeLoggingInfo = LookupOrCreateTypeLoggingInfo(th, &fCreatedNew, &pLoggedTypesFromModule);
3014 if (typeLoggingInfo.th.IsNull())
3015 return;
3016
3017 // Update stats with current allocation
3018 typeLoggingInfo.dwAllocsSkippedForSample++;
3019 typeLoggingInfo.cbIgnoredSizeForSample += size;
3020
3021 // If both the high and low verbosity keywords are enabled, log all allocations.
3022 if (!(s_fHeapAllocHighEventEnabledNow && s_fHeapAllocLowEventEnabledNow))
3023 {
3024 // Get the number of threads so that we can scale the per-thread sampling data.
3025 // NOTE: We don't do this while holding the thread store lock, so this may not be perfect,
3026 // but it will be close enough.
3027 LONG numThreads = ThreadStore::s_pThreadStore->ThreadCountInEE();
3028
3029 // This is our filter. If we should ignore this alloc, then record our updated
3030 // our stats, and bail without sending the event. Note that we always log objects
3031 // over 10K in size.
3032 if (size < 10000 && typeLoggingInfo.dwAllocsSkippedForSample < (typeLoggingInfo.dwAllocsToSkipPerSample * numThreads))
3033 {
3034 // Update hash table's copy of type logging info with these values. It is not optimal that
3035 // we're doing another hash table lookup here. Could instead have used LookupPtr()
3036 // if it gave us back a non-const pointer, and then we could have updated in-place
3037 AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo);
3038 if (fCreatedNew)
3039 {
3040 // Although we're skipping logging the allocation, we still need to log
3041 // the type (so it's available for resolving future allocation events to
3042 // their types).
3043 //
3044 // (See other call to LogTypeAndParametersIfNecessary further down for
3045 // more comments.)
3046 LogTypeAndParametersIfNecessary(
3047 NULL,
3048 th.AsTAddr(),
3049 kTypeLogBehaviorAlwaysLogTopLevelType);
3050 }
3051 return;
3052 }
3053
3054 // Based on observed allocation stats, adjust our sampling rate for this type
3055
3056 typeLoggingInfo.dwAllocCountInCurrentBucket += typeLoggingInfo.dwAllocsSkippedForSample;
3057 int delta = (dwTickNow - typeLoggingInfo.dwTickOfCurrentTimeBucket) & 0x7FFFFFFF; // make wrap around work.
3058
3059 int nMinAllocPerMSec = typeLoggingInfo.dwAllocCountInCurrentBucket / 16 / numThreads; // This is an underestimation of the true rate.
3060 if (delta >= 16 || (nMinAllocPerMSec > 2 && nMinAllocPerMSec > typeLoggingInfo.flAllocPerMSec * 1.5F))
3061 {
3062 float flNewAllocPerMSec = 0;
3063 if (delta >= 16)
3064 {
3065 // This is the normal case, our allocation rate is under control with the current throttling.
3066 flNewAllocPerMSec = ((float) typeLoggingInfo.dwAllocCountInCurrentBucket) / delta;
3067 // Do a exponential decay window that is 5 * max(16, AllocationInterval)
3068 typeLoggingInfo.flAllocPerMSec = 0.8F * typeLoggingInfo.flAllocPerMSec + 0.2F * flNewAllocPerMSec;
3069 typeLoggingInfo.dwTickOfCurrentTimeBucket = dwTickNow;
3070 typeLoggingInfo.dwAllocCountInCurrentBucket = 0;
3071 }
3072 else
3073 {
3074 flNewAllocPerMSec = (float) nMinAllocPerMSec;
3075 // This means the second clause above is true, which means our sampling rate is too low
3076 // so we need to throttle quickly.
3077 typeLoggingInfo.flAllocPerMSec = flNewAllocPerMSec;
3078 }
3079
3080
3081 // Obey the desired sampling rate, but don't ignore > 1000 allocations per second
3082 // per type
3083 int nDesiredMsBetweenEvents = (s_nCustomMsBetweenEvents == 0) ? GetDefaultMsBetweenEvents() : s_nCustomMsBetweenEvents;
3084 typeLoggingInfo.dwAllocsToSkipPerSample = min((int) (typeLoggingInfo.flAllocPerMSec * nDesiredMsBetweenEvents), 1000);
3085 if (typeLoggingInfo.dwAllocsToSkipPerSample == 1)
3086 typeLoggingInfo.dwAllocsToSkipPerSample = 0;
3087 }
3088 }
3089
3090 // We're logging this sample, so save the values we need into locals, and reset
3091 // our counts for the next sample.
3092 nTotalSizeForTypeSample = typeLoggingInfo.cbIgnoredSizeForSample;
3093 dwObjectCountForTypeSample = typeLoggingInfo.dwAllocsSkippedForSample;
3094 typeLoggingInfo.cbIgnoredSizeForSample = 0;
3095 typeLoggingInfo.dwAllocsSkippedForSample = 0;
3096
3097 // Save updated stats into hash table
3098 if (!AddOrReplaceTypeLoggingInfo(pLoggedTypesFromModule, &typeLoggingInfo))
3099 {
3100 return;
3101 }
3102
3103 // While we're still holding the crst, optionally log any relevant Types now (we may need
3104 // to reconsult the hash in here if there are any type parameters, though we can
3105 // optimize and NOT consult the hash for th itself).
3106 if (fCreatedNew)
3107 {
3108 // We were the ones to add the Type to the hash. So it wasn't there before,
3109 // which means it hasn't been logged yet.
3110 LogTypeAndParametersIfNecessary(
3111
3112 // No BulkTypeEventLogger, as we're not batching during a GC heap walk
3113 NULL,
3114
3115 th.AsTAddr(),
3116
3117 // We've determined the type is not yet logged, so no need to check
3118 kTypeLogBehaviorAlwaysLogTopLevelType);
3119 }
3120
3121 // Now log the allocation
3122 if (s_fHeapAllocHighEventEnabledNow)
3123 {
3124 FireEtwGCSampledObjectAllocationHigh(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId());
3125 }
3126 else
3127 {
3128 FireEtwGCSampledObjectAllocationLow(pObject, (LPVOID) th.AsTAddr(), dwObjectCountForTypeSample, nTotalSizeForTypeSample, GetClrInstanceId());
3129 }
3130}
3131
3132//---------------------------------------------------------------------------------------
3133//
3134// Accessor for global hash table crst
3135//
3136// Return Value:
3137// global hash table crst
3138//
3139
3140// static
3141CrstBase * ETW::TypeSystemLog::GetHashCrst()
3142{
3143 LIMITED_METHOD_CONTRACT;
3144 return &AllLoggedTypes::s_cs;
3145}
3146
3147//---------------------------------------------------------------------------------------
3148//
3149// Outermost level of ETW-type-logging. Clients outside eventtrace.cpp call this to log
3150// a TypeHandle and (recursively) its type parameters when present. This guy then calls
3151// into the appropriate BulkTypeEventLogger to do the batching and logging
3152//
3153// Arguments:
3154// * pBulkTypeEventLogger - If our caller is keeping track of batched types, it
3155// passes this to us so we can use it to batch the current type (GC heap walk
3156// does this). If this is NULL, no batching is going on (e.g., we're called on
3157// object allocation, not a GC heal walk), in which case we create our own
3158// temporary BulkTypeEventLogger.
3159// * thAsAddr - TypeHandle to batch
3160// * typeLogBehavior - Optimization to tell us we don't need to enter the
3161// TypeSystemLog's crst, as the TypeSystemLog's hash table is already protected
3162// by a prior acquisition of the crst by our caller. (Or that we don't even
3163// need to check the hash in the first place.)
3164//
3165
3166// static
3167VOID ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pLogger, ULONGLONG thAsAddr, TypeLogBehavior typeLogBehavior)
3168{
3169 CONTRACTL
3170 {
3171 NOTHROW;
3172 GC_NOTRIGGER;
3173 MODE_ANY;
3174
3175 // LogTypeAndParameters locks, and we take our own lock if typeLogBehavior says to
3176 CAN_TAKE_LOCK;
3177 }
3178 CONTRACTL_END;
3179
3180 if (!ETW_TRACING_CATEGORY_ENABLED(
3181 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3182 TRACE_LEVEL_INFORMATION,
3183 CLR_TYPE_KEYWORD))
3184 {
3185 return;
3186 }
3187
3188 TypeHandle th = TypeHandle::FromTAddr((TADDR) thAsAddr);
3189 if (!th.IsRestored())
3190 {
3191 return;
3192 }
3193
3194 // Check to see if we've already logged this type. If so, bail immediately.
3195 // Otherwise, mark that it's getting logged (by adding it to the hash), and fall
3196 // through to the logging code below. If caller doesn't care, then don't even
3197 // check; just log the type
3198 BOOL fShouldLogType = ((typeLogBehavior == kTypeLogBehaviorAlwaysLog) ||
3199 (typeLogBehavior == kTypeLogBehaviorAlwaysLogTopLevelType)) ?
3200 TRUE :
3201 ShouldLogType(th);
3202 if (!fShouldLogType)
3203 return;
3204
3205 if (pLogger == NULL)
3206 {
3207 // We're not batching this type against previous types (e.g., we're being called
3208 // on object allocate instead of a GC heap walk). So create a temporary logger
3209 // on the stack. If there are generic parameters that need to be logged, then
3210 // at least they'll get batched together with the type
3211 BulkTypeEventLogger logger;
3212 logger.LogTypeAndParameters(thAsAddr, typeLogBehavior);
3213
3214 // Since this logger isn't being used to batch anything else, flush what we have
3215 logger.FireBulkTypeEvent();
3216 }
3217 else
3218 {
3219 // We are batching this type with others (e.g., we're being called at the end of
3220 // a GC on a heap walk). So use the logger our caller set up for us.
3221 pLogger->LogTypeAndParameters(thAsAddr, typeLogBehavior);
3222 }
3223}
3224
3225
3226//---------------------------------------------------------------------------------------
3227//
3228// Ask hash table if we've already logged the type, without first acquiring the lock
3229// (our caller already did this). As a side-effect, a TypeLoggingInfo will be created
3230// for this type (so future calls to this function will return FALSE to avoid dupe type
3231// logging).
3232//
3233// Arguments:
3234// pth - TypeHandle to query
3235//
3236// Return Value:
3237// nonzero iff type should be logged (i.e., not previously logged)
3238//
3239
3240// static
3241BOOL ETW::TypeSystemLog::ShouldLogType(TypeHandle th)
3242{
3243 CONTRACTL
3244 {
3245 NOTHROW;
3246 GC_NOTRIGGER;
3247 MODE_ANY;
3248 CAN_TAKE_LOCK;
3249 }
3250 CONTRACTL_END;
3251
3252
3253 // Check to see if TypeLoggingInfo exists yet for th. If not, creates one and
3254 // adds it to the hash.
3255 BOOL fCreatedNew = FALSE;
3256
3257 // When we have a thread context, default to calling the API that requires one which
3258 // reduces the cost of locking.
3259 if (GetThread() != NULL)
3260 {
3261 LookupOrCreateTypeLoggingInfo(th, &fCreatedNew);
3262 }
3263 else
3264 {
3265 AddTypeToGlobalCacheIfNotExists(th, &fCreatedNew);
3266 }
3267
3268 // Return whether we had to create the TypeLoggingInfo (indicating it was not yet in
3269 // the hash, and thus that we hadn't yet logged the type).
3270 return fCreatedNew;
3271}
3272
3273
3274//---------------------------------------------------------------------------------------
3275//
3276// Helper that returns (creating if necessary) the TypeLoggingInfo in the hash table
3277// corresponding with the specified TypeHandle
3278//
3279// Arguments:
3280// * th - Key to lookup the TypeLoggingInfo
3281// * pfCreatedNew - [out] Points to nonzero iff a new TypeLoggingInfo was created
3282// (i.e., none existed yet in the hash for th).
3283// * ppLoggedTypesFromModule - [out] Points to the inner hash that was used to do
3284// the lookup. (An otpimization so the caller doesn't have to find this again,
3285// if it needs to do further operations on it.)
3286//
3287// Return Value:
3288// TypeLoggingInfo found or created.
3289//
3290//
3291
3292// static
3293ETW::TypeLoggingInfo ETW::TypeSystemLog::LookupOrCreateTypeLoggingInfo(TypeHandle th, BOOL * pfCreatedNew, LoggedTypesFromModule ** ppLoggedTypesFromModule /* = NULL */)
3294{
3295 //LIMITED_METHOD_CONTRACT;
3296 CONTRACTL
3297 {
3298 NOTHROW;
3299 GC_NOTRIGGER;
3300 MODE_ANY;
3301 }
3302 CONTRACTL_END;
3303
3304 _ASSERTE(pfCreatedNew != NULL);
3305
3306 if (ppLoggedTypesFromModule != NULL)
3307 {
3308 *ppLoggedTypesFromModule = NULL;
3309 }
3310
3311 BOOL fSucceeded = FALSE;
3312
3313 Thread *pThread = GetThread();
3314
3315 // Compare the thread local epoch value against the global epoch.
3316 // If the epoch has changed, dump the thread local state and start over.
3317 AllLoggedTypes * pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3318 if((pThreadAllLoggedTypes != NULL) && (pThreadAllLoggedTypes->nEpoch != s_nEpoch))
3319 {
3320 // Set the type hash pointer on the thread to NULL.
3321 pThread->SetAllocationSamplingTable(NULL);
3322
3323 // DeleteTypeHashNoLock will set pThreadAllLoggedTypes to NULL
3324 DeleteTypeHashNoLock(&pThreadAllLoggedTypes);
3325 }
3326
3327 // Create the thread local state if it doesn't exist.
3328 if (pThreadAllLoggedTypes == NULL)
3329 {
3330 pThreadAllLoggedTypes = new (nothrow) AllLoggedTypes;
3331 if (pThreadAllLoggedTypes == NULL)
3332 {
3333 // out of memory. Bail on ETW stuff
3334 *pfCreatedNew = FALSE;
3335 return TypeLoggingInfo(NULL);
3336 }
3337
3338 // Set the epoch so we know we can track when changes to global state occur.
3339 pThreadAllLoggedTypes->nEpoch = s_nEpoch;
3340
3341 // Save the thread local state to the thread.
3342 pThread->SetAllocationSamplingTable(pThreadAllLoggedTypes);
3343 }
3344
3345 BOOL addTypeToGlobalList = FALSE;
3346
3347 // Step 1: go from LoaderModule to hash of types.
3348
3349 Module * pLoaderModule = th.GetLoaderModule();
3350 _ASSERTE(pLoaderModule != NULL);
3351 LoggedTypesFromModule * pLoggedTypesFromModule = pThreadAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule);
3352 if (pLoggedTypesFromModule == NULL)
3353 {
3354 addTypeToGlobalList = TRUE;
3355 pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule);
3356 if (pLoggedTypesFromModule == NULL)
3357 {
3358 // out of memory. Bail on ETW stuff
3359 *pfCreatedNew = FALSE;
3360 return TypeLoggingInfo(NULL);
3361 }
3362
3363 fSucceeded = FALSE;
3364 EX_TRY
3365 {
3366 pThreadAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule);
3367 fSucceeded = TRUE;
3368 }
3369 EX_CATCH
3370 {
3371 fSucceeded = FALSE;
3372 }
3373 EX_END_CATCH(RethrowCorruptingExceptions);
3374 if (!fSucceeded)
3375 {
3376 *pfCreatedNew = FALSE;
3377 return TypeLoggingInfo(NULL);
3378 }
3379 }
3380
3381 if (ppLoggedTypesFromModule != NULL)
3382 {
3383 *ppLoggedTypesFromModule = pLoggedTypesFromModule;
3384 }
3385
3386 // Step 2: From hash of types, see if our TypeHandle is there already
3387 TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th);
3388 if (!typeLoggingInfoPreexisting.th.IsNull())
3389 {
3390 // Type is already hashed, so it's already logged, so we don't need to
3391 // log it again.
3392 *pfCreatedNew = FALSE;
3393 return typeLoggingInfoPreexisting;
3394 }
3395
3396 // We haven't logged this type, so we need to continue with this function to
3397 // log it below. Add it to the hash table first so any recursive calls will
3398 // see that this type is already being taken care of
3399 addTypeToGlobalList = TRUE;
3400 fSucceeded = FALSE;
3401 TypeLoggingInfo typeLoggingInfoNew(th);
3402 EX_TRY
3403 {
3404 pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew);
3405 fSucceeded = TRUE;
3406 }
3407 EX_CATCH
3408 {
3409 fSucceeded = FALSE;
3410 }
3411 EX_END_CATCH(RethrowCorruptingExceptions);
3412 if (!fSucceeded)
3413 {
3414 *pfCreatedNew = FALSE;
3415 return TypeLoggingInfo(NULL);
3416 }
3417
3418 // This is the first time that we've seen this type on this thread, so we should attempt to
3419 // add it to the global list.
3420 if(!AddTypeToGlobalCacheIfNotExists(th, pfCreatedNew))
3421 {
3422 // out of memory or ETW has been disabled. Bail on ETW stuff
3423 *pfCreatedNew = FALSE;
3424 return TypeLoggingInfo(NULL);
3425 }
3426
3427 return typeLoggingInfoNew;
3428}
3429
3430//---------------------------------------------------------------------------------------
3431//
3432// Helper that creates a Type entry in the global type logging cache if one doesn't
3433// already exist.
3434//
3435// Arguments:
3436// * th - Key to lookup or create
3437//
3438// Return Value:
3439// TRUE if the type needed to be added to the cache.
3440//
3441//
3442
3443// static
3444BOOL ETW::TypeSystemLog::AddTypeToGlobalCacheIfNotExists(TypeHandle th, BOOL * pfCreatedNew)
3445{
3446 CONTRACTL
3447 {
3448 NOTHROW;
3449 GC_NOTRIGGER;
3450 MODE_ANY;
3451 }
3452 CONTRACTL_END;
3453
3454 BOOL fSucceeded = FALSE;
3455
3456 {
3457 CrstHolder _crst(GetHashCrst());
3458
3459 // Check if ETW is enabled, and if not, bail here.
3460 // We do this inside of the lock to ensure that we don't immediately
3461 // re-allocate the global type hash after it has been cleaned up.
3462 if (!ETW_TRACING_CATEGORY_ENABLED(
3463 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3464 TRACE_LEVEL_INFORMATION,
3465 CLR_TYPE_KEYWORD))
3466 {
3467 *pfCreatedNew = FALSE;
3468 return fSucceeded;
3469 }
3470
3471 if (s_pAllLoggedTypes == NULL)
3472 {
3473 s_pAllLoggedTypes = new (nothrow) AllLoggedTypes;
3474 if (s_pAllLoggedTypes == NULL)
3475 {
3476 // out of memory. Bail on ETW stuff
3477 *pfCreatedNew = FALSE;
3478 return fSucceeded;
3479 }
3480 }
3481
3482 // Step 1: go from LoaderModule to hash of types.
3483
3484 Module * pLoaderModule = th.GetLoaderModule();
3485 _ASSERTE(pLoaderModule != NULL);
3486 LoggedTypesFromModule * pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pLoaderModule);
3487 if (pLoggedTypesFromModule == NULL)
3488 {
3489 pLoggedTypesFromModule = new (nothrow) LoggedTypesFromModule(pLoaderModule);
3490 if (pLoggedTypesFromModule == NULL)
3491 {
3492 // out of memory. Bail on ETW stuff
3493 *pfCreatedNew = FALSE;
3494 return fSucceeded;
3495 }
3496
3497 fSucceeded = FALSE;
3498 EX_TRY
3499 {
3500 s_pAllLoggedTypes->allLoggedTypesHash.Add(pLoggedTypesFromModule);
3501 fSucceeded = TRUE;
3502 }
3503 EX_CATCH
3504 {
3505 fSucceeded = FALSE;
3506 }
3507 EX_END_CATCH(RethrowCorruptingExceptions);
3508 if (!fSucceeded)
3509 {
3510 *pfCreatedNew = FALSE;
3511 return fSucceeded;
3512 }
3513 }
3514
3515 // Step 2: From hash of types, see if our TypeHandle is there already
3516 TypeLoggingInfo typeLoggingInfoPreexisting = pLoggedTypesFromModule->loggedTypesFromModuleHash.Lookup(th);
3517 if (!typeLoggingInfoPreexisting.th.IsNull())
3518 {
3519 // Type is already hashed, so it's already logged, so we don't need to
3520 // log it again.
3521 *pfCreatedNew = FALSE;
3522 return fSucceeded;
3523 }
3524
3525 // We haven't logged this type, so we need to continue with this function to
3526 // log it below. Add it to the hash table first so any recursive calls will
3527 // see that this type is already being taken care of
3528 fSucceeded = FALSE;
3529 TypeLoggingInfo typeLoggingInfoNew(th);
3530 EX_TRY
3531 {
3532 pLoggedTypesFromModule->loggedTypesFromModuleHash.Add(typeLoggingInfoNew);
3533 fSucceeded = TRUE;
3534 }
3535 EX_CATCH
3536 {
3537 fSucceeded = FALSE;
3538 }
3539 EX_END_CATCH(RethrowCorruptingExceptions);
3540 if (!fSucceeded)
3541 {
3542 *pfCreatedNew = FALSE;
3543 return fSucceeded;
3544 }
3545 } // RELEASE: CrstHolder _crst(GetHashCrst());
3546
3547 *pfCreatedNew = TRUE;
3548 return fSucceeded;
3549
3550}
3551
3552//---------------------------------------------------------------------------------------
3553//
3554// Called when we determine if a module was unloaded, so we can clear out that module's
3555// set of types from our hash table
3556//
3557// Arguments:
3558// pModule - Module getting unloaded
3559//
3560
3561// static
3562VOID ETW::TypeSystemLog::OnModuleUnload(Module * pModule)
3563{
3564 CONTRACTL
3565 {
3566 NOTHROW;
3567 GC_NOTRIGGER;
3568 MODE_ANY;
3569 CAN_TAKE_LOCK;
3570 }
3571 CONTRACTL_END;
3572
3573 // We don't need to do anything if allocation sampling is disabled.
3574 if (!ETW_TRACING_CATEGORY_ENABLED(
3575 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3576 TRACE_LEVEL_INFORMATION,
3577 CLR_TYPE_KEYWORD))
3578 {
3579 return;
3580 }
3581
3582 LoggedTypesFromModule * pLoggedTypesFromModule = NULL;
3583
3584 {
3585 CrstHolder _crst(GetHashCrst());
3586
3587 // We don't need to do anything if the global type hash doesn't contain any data.
3588 if (s_pAllLoggedTypes == NULL)
3589 return;
3590
3591 // Is there a TypesHash for this module?
3592 pLoggedTypesFromModule = s_pAllLoggedTypes->allLoggedTypesHash.Lookup(pModule);
3593 if (pLoggedTypesFromModule == NULL)
3594 return;
3595
3596 // Remove TypesHash from master hash mapping modules to their TypesHash
3597 s_pAllLoggedTypes->allLoggedTypesHash.Remove(pModule);
3598
3599 // Increment the epoch to signal the change to all threads.
3600 s_nEpoch++;
3601 }
3602
3603 // Destruct this TypesHash we just removed
3604 delete pLoggedTypesFromModule;
3605 pLoggedTypesFromModule = NULL;
3606
3607}
3608
3609//---------------------------------------------------------------------------------------
3610//
3611// Same semantics as DeleteTypeHash but assumes that the appropriate lock
3612// has already been acquired.
3613//
3614
3615// static
3616VOID ETW::TypeSystemLog::DeleteTypeHashNoLock(AllLoggedTypes **ppAllLoggedTypes)
3617{
3618 LIMITED_METHOD_CONTRACT;
3619
3620 if(ppAllLoggedTypes == NULL)
3621 {
3622 return;
3623 }
3624
3625 AllLoggedTypes *pAllLoggedTypes = *ppAllLoggedTypes;
3626
3627 if(pAllLoggedTypes == NULL)
3628 {
3629 return;
3630 }
3631
3632 // Destruct each of the per-module TypesHashes
3633 AllLoggedTypesHash * pLoggedTypesHash = &pAllLoggedTypes->allLoggedTypesHash;
3634 for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin();
3635 iter != pLoggedTypesHash->End();
3636 ++iter)
3637 {
3638 LoggedTypesFromModule * pLoggedTypesFromModule = *iter;
3639 delete pLoggedTypesFromModule;
3640 }
3641
3642 // This causes the default ~AllLoggedTypes() to be called, and thus
3643 // ~AllLoggedTypesHash() to be called
3644 delete pAllLoggedTypes;
3645 *ppAllLoggedTypes = NULL;
3646}
3647
3648//---------------------------------------------------------------------------------------
3649//
3650// Called from shutdown to give us the opportunity to dump any sampled object allocation
3651// information before the process shuts down.
3652//
3653
3654// static
3655VOID ETW::TypeSystemLog::FlushObjectAllocationEvents()
3656{
3657 CONTRACTL
3658 {
3659 NOTHROW;
3660 GC_TRIGGERS;
3661 MODE_ANY;
3662 CAN_TAKE_LOCK;
3663 }
3664 CONTRACTL_END;
3665
3666 // If logging is not enabled, then we don't need to do any work.
3667 if (!(s_fHeapAllocLowEventEnabledNow || s_fHeapAllocHighEventEnabledNow))
3668 {
3669 return;
3670 }
3671
3672 AllLoggedTypes * pThreadAllLoggedTypes = NULL;
3673 Thread * pThread = NULL;
3674
3675 // Get the thread store lock.
3676 ThreadStoreLockHolder tsl;
3677
3678 // Iterate over each thread and log any un-logged allocations.
3679 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
3680 {
3681 pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3682 if (pThreadAllLoggedTypes == NULL)
3683 {
3684 continue;
3685 }
3686
3687 DWORD dwAllocsSkippedForSample;
3688 SIZE_T cbIgnoredSizeForSample;
3689
3690 // Iterate over each module.
3691 AllLoggedTypesHash * pLoggedTypesHash = &pThreadAllLoggedTypes->allLoggedTypesHash;
3692 for (AllLoggedTypesHash::Iterator iter = pLoggedTypesHash->Begin();
3693 iter != pLoggedTypesHash->End();
3694 ++iter)
3695 {
3696 // Iterate over each type in the module.
3697 LoggedTypesFromModule * pLoggedTypesFromModule = *iter;
3698 LoggedTypesFromModuleHash * pLoggedTypesFromModuleHash = &pLoggedTypesFromModule->loggedTypesFromModuleHash;
3699 for (LoggedTypesFromModuleHash::Iterator typeIter = pLoggedTypesFromModuleHash->Begin();
3700 typeIter != pLoggedTypesFromModuleHash->End();
3701 ++typeIter)
3702 {
3703 dwAllocsSkippedForSample = typeIter->dwAllocsSkippedForSample;
3704 cbIgnoredSizeForSample = typeIter->cbIgnoredSizeForSample;
3705
3706 // Only write the event if there were allocations that have not been logged.
3707 if (dwAllocsSkippedForSample > 0 || cbIgnoredSizeForSample > 0)
3708 {
3709 // Write the event based on which keyword was specified when ETW was configured.
3710 if (s_fHeapAllocHighEventEnabledNow)
3711 {
3712 FireEtwGCSampledObjectAllocationHigh(NULL, (LPVOID) typeIter->th.AsTAddr(), dwAllocsSkippedForSample, cbIgnoredSizeForSample, GetClrInstanceId());
3713 }
3714 else
3715 {
3716 FireEtwGCSampledObjectAllocationLow(NULL, (LPVOID) typeIter->th.AsTAddr(), dwAllocsSkippedForSample, cbIgnoredSizeForSample, GetClrInstanceId());
3717 }
3718 }
3719 }
3720 }
3721 }
3722}
3723
3724//---------------------------------------------------------------------------------------
3725//
3726// Whenever we detect that the Types keyword is off, this gets called. This eliminates the
3727// global hash tables that tracked which types were logged (if the hash tables had been created
3728// previously). If type events are turned back on later, we'll re-log them all as we
3729// encounter them. Thread local hash tables are destroyed in the Cleanup method, which is
3730// called during GC to ensure that there aren't any races.
3731//
3732
3733// static
3734VOID ETW::TypeSystemLog::OnTypesKeywordTurnedOff()
3735{
3736 CONTRACTL
3737 {
3738 NOTHROW;
3739 GC_NOTRIGGER;
3740 MODE_ANY;
3741 CAN_TAKE_LOCK;
3742 }
3743 CONTRACTL_END;
3744
3745 // Take the global cache lock.
3746 CrstHolder _crst(GetHashCrst());
3747
3748 // Clean-up the global TypeHash if necessary.
3749 if (s_pAllLoggedTypes == NULL)
3750 {
3751 // Even if we don't increment the epoch, but we get into a situation where
3752 // some per thread data has been allocated, it will be cleaned up during the
3753 // next GC because we are guaranteed that s_nEpoch has been incremented at
3754 // least once (to shutdown allocation sampling).
3755 return;
3756 }
3757
3758 // Destruct the global TypeHash
3759 DeleteTypeHashNoLock(&s_pAllLoggedTypes);
3760
3761 // Increment the epoch to signal the change to all threads.
3762 s_nEpoch++;
3763}
3764
3765//---------------------------------------------------------------------------------------
3766//
3767// Clean-up thread local type hashes. This is called from within the GC to ensure that
3768// there are no races. All threads are suspended when this is called.
3769//
3770
3771// static
3772VOID ETW::TypeSystemLog::Cleanup()
3773{
3774 CONTRACTL
3775 {
3776 NOTHROW;
3777 GC_NOTRIGGER;
3778 MODE_ANY;
3779 }
3780 CONTRACTL_END;
3781
3782 // If allocation sampling is enabled, bail here so that we don't delete
3783 // any of the thread local state.
3784 if (ETW_TRACING_CATEGORY_ENABLED(
3785 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3786 TRACE_LEVEL_INFORMATION,
3787 CLR_TYPE_KEYWORD))
3788 {
3789 return;
3790 }
3791
3792 // If logging is disabled but the epoch has not been incremented,
3793 // we haven't ever turned on allocation sampling, so there is nothing
3794 // to clean-up.
3795 if(s_nEpoch <= 0)
3796 {
3797 return;
3798 }
3799
3800 // Iterate over each thread and destruct the per thread caches
3801 AllLoggedTypes * pThreadAllLoggedTypes = NULL;
3802 Thread * pThread = NULL;
3803 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
3804 {
3805 pThreadAllLoggedTypes = pThread->GetAllocationSamplingTable();
3806 if(pThreadAllLoggedTypes == NULL)
3807 {
3808 continue;
3809 }
3810
3811 // Destruct each of the thread local TypesHashes
3812 DeleteTypeHashNoLock(&pThreadAllLoggedTypes);
3813
3814 // Set the thread type hash pointer to NULL
3815 pThread->SetAllocationSamplingTable(NULL);
3816 }
3817}
3818
3819
3820/****************************************************************************/
3821/* Called when ETW is turned ON on an existing process and ModuleRange events are to
3822 be fired */
3823/****************************************************************************/
3824VOID ETW::EnumerationLog::ModuleRangeRundown()
3825{
3826 CONTRACTL {
3827 NOTHROW;
3828 GC_TRIGGERS;
3829 } CONTRACTL_END;
3830
3831 EX_TRY
3832 {
3833 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
3834 TRACE_LEVEL_INFORMATION,
3835 CLR_PERFTRACK_PRIVATE_KEYWORD))
3836 {
3837 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate);
3838 }
3839 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
3840}
3841
3842
3843/****************************************************************************/
3844/* Called when ETW is turned ON on an existing process */
3845/****************************************************************************/
3846VOID ETW::EnumerationLog::StartRundown()
3847{
3848 CONTRACTL {
3849 NOTHROW;
3850 GC_TRIGGERS;
3851 } CONTRACTL_END;
3852
3853 EX_TRY
3854 {
3855 BOOL bIsArmRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3856 TRACE_LEVEL_INFORMATION,
3857 CLR_RUNDOWNAPPDOMAINRESOURCEMANAGEMENT_KEYWORD);
3858 BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3859 TRACE_LEVEL_INFORMATION,
3860 CLR_RUNDOWNPERFTRACK_KEYWORD);
3861 BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(
3862 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3863 TRACE_LEVEL_INFORMATION,
3864 CLR_RUNDOWNTHREADING_KEYWORD);
3865
3866 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3867 TRACE_LEVEL_INFORMATION,
3868 CLR_RUNDOWNJIT_KEYWORD)
3869 ||
3870 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3871 TRACE_LEVEL_INFORMATION,
3872 CLR_RUNDOWNLOADER_KEYWORD)
3873 ||
3874 IsRundownNgenKeywordEnabledAndNotSuppressed()
3875 ||
3876 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3877 TRACE_LEVEL_INFORMATION,
3878 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)
3879 ||
3880 bIsArmRundownEnabled
3881 ||
3882 bIsPerfTrackRundownEnabled
3883 ||
3884 bIsThreadingRundownEnabled)
3885 {
3886 // begin marker event will go to the rundown provider
3887 FireEtwDCStartInit_V1(GetClrInstanceId());
3888
3889 // The rundown flag is expected to be checked in the caller, so no need to check here again
3890 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
3891 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3892 TRACE_LEVEL_INFORMATION,
3893 CLR_RUNDOWNLOADER_KEYWORD))
3894 {
3895 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart;
3896 }
3897 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3898 TRACE_LEVEL_INFORMATION,
3899 CLR_RUNDOWNJIT_KEYWORD))
3900 {
3901 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart;
3902 }
3903 if(IsRundownNgenKeywordEnabledAndNotSuppressed())
3904 {
3905 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart;
3906 }
3907 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
3908 TRACE_LEVEL_INFORMATION,
3909 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD))
3910 {
3911 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap;
3912 }
3913 if(bIsPerfTrackRundownEnabled)
3914 {
3915 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart;
3916 }
3917
3918 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions);
3919
3920 if (bIsArmRundownEnabled)
3921 {
3922 // When an ETW event consumer asks for ARM rundown, that not only enables
3923 // the ETW events, but also causes some minor behavioral changes in the
3924 // CLR, such as gathering CPU usage baselines for each thread right now,
3925 // and also gathering resource usage information later on (keyed off of
3926 // g_fEnableARM, which we'll set right now).
3927 EnableARM();
3928 }
3929
3930 if (bIsArmRundownEnabled || bIsThreadingRundownEnabled)
3931 {
3932 SendThreadRundownEvent();
3933 }
3934
3935 // end marker event will go to the rundown provider
3936 FireEtwDCStartComplete_V1(GetClrInstanceId());
3937 }
3938 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
3939}
3940
3941//---------------------------------------------------------------------------------------
3942//
3943// Simple helper to convert the currently active keywords on the runtime provider into a
3944// bitmask of enumeration options as defined in ETW::EnumerationLog::EnumerationStructs
3945//
3946// Return Value:
3947// ETW::EnumerationLog::EnumerationStructs bitmask corresponding to the currently
3948// active keywords on the runtime provider
3949//
3950
3951// static
3952DWORD ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords()
3953{
3954 LIMITED_METHOD_CONTRACT;
3955
3956 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
3957 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3958 TRACE_LEVEL_INFORMATION,
3959 CLR_LOADER_KEYWORD))
3960 {
3961 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload;
3962 }
3963 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3964 TRACE_LEVEL_INFORMATION,
3965 CLR_JIT_KEYWORD) &&
3966 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3967 TRACE_LEVEL_INFORMATION,
3968 CLR_ENDENUMERATION_KEYWORD))
3969 {
3970 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodUnload;
3971 }
3972 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed() &&
3973 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
3974 TRACE_LEVEL_INFORMATION,
3975 CLR_ENDENUMERATION_KEYWORD))
3976 {
3977 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload;
3978 }
3979
3980 return enumerationOptions;
3981}
3982
3983//---------------------------------------------------------------------------------------
3984//
3985// Executes a flavor of rundown initiated by a CAPTURE_STATE request to
3986// code:#EtwCallback. CAPTURE_STATE is the "ETW-sanctioned" way of performing a
3987// rundown, whereas the CLR's rundown provider was *our* version of this, implemented
3988// before CAPTURE_STATE was standardized.
3989//
3990// When doing a CAPTURE_STATE, the CLR rundown provider is completely unused. Instead,
3991// we pay attention to the runtime keywords active at the time the CAPTURE_STATE was
3992// requested, and enumerate through the appropriate objects (AppDomains, assemblies,
3993// modules, types, methods, threads) and send runtime events for each of them.
3994//
3995// CAPTURE_STATE is intended to be used primarily by PerfTrack. Implementing this form
3996// of rundown allows PerfTrack to be blissfully unaware of the CLR's rundown provider.
3997//
3998
3999// static
4000VOID ETW::EnumerationLog::EnumerateForCaptureState()
4001{
4002 CONTRACTL
4003 {
4004 NOTHROW;
4005 GC_TRIGGERS;
4006 }
4007 CONTRACTL_END;
4008
4009 EX_TRY
4010 {
4011 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO))
4012 {
4013 DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords();
4014
4015 // Send unload events for all remaining domains, including shared domain and
4016 // default domain.
4017 ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions);
4018
4019 // Send thread created events for all currently active threads, if requested
4020 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4021 TRACE_LEVEL_INFORMATION,
4022 CLR_THREADING_KEYWORD))
4023 {
4024 SendThreadRundownEvent();
4025 }
4026 }
4027 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4028}
4029
4030/**************************************************************************************/
4031/* Called when ETW is turned OFF on an existing process .Will be used by the controller for end rundown*/
4032/**************************************************************************************/
4033VOID ETW::EnumerationLog::EndRundown()
4034{
4035 CONTRACTL {
4036 NOTHROW;
4037 GC_TRIGGERS;
4038 } CONTRACTL_END;
4039
4040 EX_TRY
4041 {
4042 BOOL bIsPerfTrackRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4043 TRACE_LEVEL_INFORMATION,
4044 CLR_RUNDOWNPERFTRACK_KEYWORD);
4045 BOOL bIsThreadingRundownEnabled = ETW_TRACING_CATEGORY_ENABLED(
4046 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4047 TRACE_LEVEL_INFORMATION,
4048 CLR_RUNDOWNTHREADING_KEYWORD);
4049 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4050 TRACE_LEVEL_INFORMATION,
4051 CLR_RUNDOWNJIT_KEYWORD)
4052 ||
4053 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4054 TRACE_LEVEL_INFORMATION,
4055 CLR_RUNDOWNLOADER_KEYWORD)
4056 ||
4057 IsRundownNgenKeywordEnabledAndNotSuppressed()
4058 ||
4059 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4060 TRACE_LEVEL_INFORMATION,
4061 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD)
4062 ||
4063 bIsPerfTrackRundownEnabled
4064 ||
4065 bIsThreadingRundownEnabled
4066 )
4067 {
4068 // begin marker event will go to the rundown provider
4069 FireEtwDCEndInit_V1(GetClrInstanceId());
4070
4071 // The rundown flag is expected to be checked in the caller, so no need to check here again
4072 DWORD enumerationOptions=ETW::EnumerationLog::EnumerationStructs::None;
4073 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4074 TRACE_LEVEL_INFORMATION,
4075 CLR_RUNDOWNLOADER_KEYWORD))
4076 {
4077 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd;
4078 }
4079 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4080 TRACE_LEVEL_INFORMATION,
4081 CLR_RUNDOWNJIT_KEYWORD))
4082 {
4083 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd;
4084 }
4085 if(IsRundownNgenKeywordEnabledAndNotSuppressed())
4086 {
4087 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd;
4088 }
4089 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4090 TRACE_LEVEL_INFORMATION,
4091 CLR_RUNDOWNJITTEDMETHODILTONATIVEMAP_KEYWORD))
4092 {
4093 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap;
4094 }
4095 if(bIsPerfTrackRundownEnabled)
4096 {
4097 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd;
4098 }
4099
4100 ETW::EnumerationLog::EnumerationHelper(NULL, NULL, enumerationOptions);
4101
4102 if (bIsThreadingRundownEnabled)
4103 {
4104 SendThreadRundownEvent();
4105 }
4106
4107 // end marker event will go to the rundown provider
4108 FireEtwDCEndComplete_V1(GetClrInstanceId());
4109 }
4110 } EX_CATCH {
4111 STRESS_LOG1(LF_ALWAYS, LL_ERROR, "Exception during Rundown Enumeration, EIP of last AV = %p", g_LastAccessViolationEIP);
4112 } EX_END_CATCH(SwallowAllExceptions);
4113}
4114
4115// #Registration
4116/*++
4117
4118Routine Description:
4119
4120 Registers provider with ETW tracing framework.
4121 This function should not be called more than once, on
4122 Dll Process attach only.
4123 Not thread safe.
4124
4125Arguments:
4126 none
4127
4128Return Value:
4129 Returns the return value from RegisterTraceGuids or EventRegister.
4130
4131--*/
4132
4133void InitializeEventTracing()
4134{
4135 CONTRACTL
4136 {
4137 THROWS;
4138 GC_TRIGGERS;
4139 MODE_ANY;
4140 }
4141 CONTRACTL_END;
4142
4143 // Do startup-only initialization of any state required by the ETW classes before
4144 // events can be fired
4145 HRESULT hr = ETW::TypeSystemLog::PreRegistrationInit();
4146 if (FAILED(hr))
4147 return;
4148
4149#if !defined(FEATURE_PAL)
4150 // Register CLR providers with the OS
4151 if (g_pEtwTracer == NULL)
4152 {
4153 NewHolder <ETW::CEtwTracer> tempEtwTracer (new (nothrow) ETW::CEtwTracer());
4154 if (tempEtwTracer != NULL && tempEtwTracer->Register () == ERROR_SUCCESS)
4155 g_pEtwTracer = tempEtwTracer.Extract ();
4156 }
4157#endif
4158
4159 g_nClrInstanceId = GetRuntimeId() & 0x0000FFFF; // This will give us duplicate ClrInstanceId after UINT16_MAX
4160
4161 // Any classes that need some initialization to happen after we've registered the
4162 // providers can do so now
4163 ETW::TypeSystemLog::PostRegistrationInit();
4164}
4165
4166// Plumbing to funnel event pipe callbacks and ETW callbacks together into a single common
4167// handler, for the purposes of informing the GC of changes to the event state.
4168//
4169// There is one callback for every EventPipe provider and one for all of ETW. The reason
4170// for this is that ETW passes the registration handle of the provider that was enabled
4171// as a field on the "CallbackContext" field of the callback, while EventPipe passes null
4172// unless another token is given to it when the provider is constructed. In the absence of
4173// a suitable token, this implementation has a different callback for every EventPipe provider
4174// that ultimately funnels them all into a common handler.
4175
4176#if defined(FEATURE_PAL)
4177// CLR_GCHEAPCOLLECT_KEYWORD is defined by the generated ETW manifest on Windows.
4178// On non-Windows, we need to make sure that this is defined. Given that we can't change
4179// the value due to compatibility, we specify it here rather than generating defines based on the manifest.
4180#define CLR_GCHEAPCOLLECT_KEYWORD 0x800000
4181#endif // defined(FEATURE_PAL)
4182
4183// CallbackProviderIndex provides a quick identification of which provider triggered the
4184// ETW callback.
4185enum CallbackProviderIndex
4186{
4187 DotNETRuntime = 0,
4188 DotNETRuntimeRundown = 1,
4189 DotNETRuntimeStress = 2,
4190 DotNETRuntimePrivate = 3
4191};
4192
4193// Common handler for all ETW or EventPipe event notifications. Based on the provider that
4194// was enabled/disabled, this implementation forwards the event state change onto GCHeapUtilities
4195// which will inform the GC to update its local state about what events are enabled.
4196VOID EtwCallbackCommon(
4197 CallbackProviderIndex ProviderIndex,
4198 ULONG ControlCode,
4199 UCHAR Level,
4200 ULONGLONG MatchAnyKeyword,
4201 PVOID pFilterData)
4202{
4203 LIMITED_METHOD_CONTRACT;
4204
4205 bool bIsPublicTraceHandle = ProviderIndex == DotNETRuntime;
4206#if !defined(FEATURE_PAL)
4207 static_assert(GCEventLevel_None == TRACE_LEVEL_NONE, "GCEventLevel_None value mismatch");
4208 static_assert(GCEventLevel_Fatal == TRACE_LEVEL_FATAL, "GCEventLevel_Fatal value mismatch");
4209 static_assert(GCEventLevel_Error == TRACE_LEVEL_ERROR, "GCEventLevel_Error value mismatch");
4210 static_assert(GCEventLevel_Warning == TRACE_LEVEL_WARNING, "GCEventLevel_Warning mismatch");
4211 static_assert(GCEventLevel_Information == TRACE_LEVEL_INFORMATION, "GCEventLevel_Information mismatch");
4212 static_assert(GCEventLevel_Verbose == TRACE_LEVEL_VERBOSE, "GCEventLevel_Verbose mismatch");
4213#endif // !defined(FEATURE_PAL)
4214 GCEventKeyword keywords = static_cast<GCEventKeyword>(MatchAnyKeyword);
4215 GCEventLevel level = static_cast<GCEventLevel>(Level);
4216 GCHeapUtilities::RecordEventStateChange(bIsPublicTraceHandle, keywords, level);
4217
4218 // Special check for the runtime provider's GCHeapCollectKeyword. Profilers
4219 // flick this to force a full GC.
4220 if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle &&
4221 ((MatchAnyKeyword & CLR_GCHEAPCOLLECT_KEYWORD) != 0))
4222 {
4223 // Profilers may (optionally) specify extra data in the filter parameter
4224 // to log with the GCStart event.
4225 LONGLONG l64ClientSequenceNumber = 0;
4226#if !defined(FEATURE_PAL)
4227 PEVENT_FILTER_DESCRIPTOR FilterData = (PEVENT_FILTER_DESCRIPTOR)pFilterData;
4228 if ((FilterData != NULL) &&
4229 (FilterData->Type == 1) &&
4230 (FilterData->Size == sizeof(l64ClientSequenceNumber)))
4231 {
4232 l64ClientSequenceNumber = *(LONGLONG *) (FilterData->Ptr);
4233 }
4234#endif // !defined(FEATURE_PAL)
4235 ETW::GCLog::ForceGC(l64ClientSequenceNumber);
4236 }
4237}
4238
4239// Individual callbacks for each EventPipe provider.
4240
4241VOID EventPipeEtwCallbackDotNETRuntimeStress(
4242 _In_ LPCGUID SourceId,
4243 _In_ ULONG ControlCode,
4244 _In_ UCHAR Level,
4245 _In_ ULONGLONG MatchAnyKeyword,
4246 _In_ ULONGLONG MatchAllKeyword,
4247 _In_opt_ EventFilterDescriptor* FilterData,
4248 _Inout_opt_ PVOID CallbackContext)
4249{
4250 LIMITED_METHOD_CONTRACT;
4251
4252 EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData);
4253}
4254
4255VOID EventPipeEtwCallbackDotNETRuntime(
4256 _In_ LPCGUID SourceId,
4257 _In_ ULONG ControlCode,
4258 _In_ UCHAR Level,
4259 _In_ ULONGLONG MatchAnyKeyword,
4260 _In_ ULONGLONG MatchAllKeyword,
4261 _In_opt_ EventFilterDescriptor* FilterData,
4262 _Inout_opt_ PVOID CallbackContext)
4263{
4264 LIMITED_METHOD_CONTRACT;
4265
4266 EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData);
4267}
4268
4269VOID EventPipeEtwCallbackDotNETRuntimeRundown(
4270 _In_ LPCGUID SourceId,
4271 _In_ ULONG ControlCode,
4272 _In_ UCHAR Level,
4273 _In_ ULONGLONG MatchAnyKeyword,
4274 _In_ ULONGLONG MatchAllKeyword,
4275 _In_opt_ EventFilterDescriptor* FilterData,
4276 _Inout_opt_ PVOID CallbackContext)
4277{
4278 LIMITED_METHOD_CONTRACT;
4279
4280 EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData);
4281}
4282
4283VOID EventPipeEtwCallbackDotNETRuntimePrivate(
4284 _In_ LPCGUID SourceId,
4285 _In_ ULONG ControlCode,
4286 _In_ UCHAR Level,
4287 _In_ ULONGLONG MatchAnyKeyword,
4288 _In_ ULONGLONG MatchAllKeyword,
4289 _In_opt_ EventFilterDescriptor* FilterData,
4290 _Inout_opt_ PVOID CallbackContext)
4291{
4292 WRAPPER_NO_CONTRACT;
4293
4294 EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData);
4295}
4296
4297
4298#if !defined(FEATURE_PAL)
4299HRESULT ETW::CEtwTracer::Register()
4300{
4301 WRAPPER_NO_CONTRACT;
4302
4303 EventRegisterMicrosoft_Windows_DotNETRuntime();
4304 EventRegisterMicrosoft_Windows_DotNETRuntimePrivate();
4305 EventRegisterMicrosoft_Windows_DotNETRuntimeRundown();
4306
4307 // Stress Log ETW events are available only on the desktop version of the runtime
4308
4309 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeHandle;
4310 MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimePrivateHandle;
4311 MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context.RegistrationHandle = Microsoft_Windows_DotNETRuntimeRundownHandle;
4312
4313 return S_OK;
4314}
4315
4316// #Unregistration
4317/*++
4318
4319Routine Description:
4320 Unregisters the provider from ETW. This function
4321 should only be called once from DllMain Detach process.
4322 Not thread safe.
4323
4324Arguments:
4325 none
4326
4327Return Value:
4328 Returns ERROR_SUCCESS
4329
4330--*/
4331HRESULT ETW::CEtwTracer::UnRegister()
4332{
4333 LIMITED_METHOD_CONTRACT;
4334 EventUnregisterMicrosoft_Windows_DotNETRuntime();
4335 EventUnregisterMicrosoft_Windows_DotNETRuntimePrivate();
4336 EventUnregisterMicrosoft_Windows_DotNETRuntimeRundown();
4337 return S_OK;
4338}
4339
4340extern "C"
4341{
4342 ETW_INLINE
4343 VOID EtwCallout(REGHANDLE RegHandle,
4344 PCEVENT_DESCRIPTOR Descriptor,
4345 ULONG ArgumentCount,
4346 PEVENT_DATA_DESCRIPTOR EventData)
4347 {
4348 WRAPPER_NO_CONTRACT;
4349 UINT8 providerIndex = 0;
4350 if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4351 providerIndex = 0;
4352 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4353 providerIndex = 1;
4354 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4355 providerIndex = 2;
4356 } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4357 providerIndex = 3;
4358 } else {
4359 _ASSERTE(!"Provider not one of Runtime, Rundown, Private and Stress");
4360 return;
4361 }
4362
4363 // stacks are supposed to be fired for only the events with a bit set in the etwStackSupportedEvents bitmap
4364 if(((etwStackSupportedEvents[providerIndex][Descriptor->Id/8]) &
4365 (1<<(Descriptor->Id%8))) != 0)
4366 {
4367 if(RegHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4368 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, &CLRStackWalk, &CLRStackId);
4369 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4370 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, &CLRStackWalkDCStart, &CLRStackRundownId);
4371 } else if(RegHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4372 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, &CLRStackWalkPrivate, &CLRStackPrivateId);
4373 } else if(RegHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4374 ETW::SamplingLog::SendStackTrace(MICROSOFT_WINDOWS_DOTNETRUNTIME_STRESS_PROVIDER_Context, &CLRStackWalkStress, &CLRStackStressId);
4375 }
4376 }
4377 }
4378}
4379
4380extern "C"
4381{
4382 // #EtwCallback:
4383 // During the build, MC generates the code to register our provider, and to register
4384 // our ETW callback. (This is buried under Intermediates, in a path like
4385 // Intermediate\clr\corguids.nativeproj_1723354836\obj1c\x86\ClrEtwAll.h.) The ETW
4386 // callback is also generated for us by MC. But we can hook into this generated
4387 // callback by #defining MCGEN_PRIVATE_ENABLE_CALLBACK_V2 to be a call to this
4388 // function (EtwCallback), thus causing EtwCallback to get called after the
4389 // MC-generated code executes.
4390 //
4391 // This callback function is called whenever an ETW session is enabled or disabled. A
4392 // callback function needs to be specified when the provider is registered. C style
4393 // callback wrappers are needed during event registration. To handle the callback
4394 // action in this class, we pass "this" during provider registration and modify the
4395 // context to the relevant context in the C callback later.
4396 ETW_INLINE
4397 VOID EtwCallback(
4398 _In_ LPCGUID SourceId,
4399 _In_ ULONG ControlCode,
4400 _In_ UCHAR Level,
4401 _In_ ULONGLONG MatchAnyKeyword,
4402 _In_ ULONGLONG MatchAllKeyword,
4403 _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
4404 _Inout_opt_ PVOID CallbackContext)
4405 {
4406 CONTRACTL {
4407 NOTHROW;
4408 if(g_fEEStarted) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);};
4409 MODE_ANY;
4410 CAN_TAKE_LOCK;
4411 STATIC_CONTRACT_FAULT;
4412 SO_NOT_MAINLINE;
4413 } CONTRACTL_END;
4414
4415 // Mark that we are the special ETWRundown thread. Currently all this does
4416 // is insure that AVs thrown in this thread are treated as normal exceptions.
4417 // This allows us to catch and swallow them. We can do this because we have
4418 // a reasonably strong belief that doing ETW Rundown does not change runtime state
4419 // and thus if an AV happens it is better to simply give up logging ETW and
4420 // instead of terminating the process (which is what we would do normally)
4421 ClrFlsThreadTypeSwitch etwRundownThreadHolder(ThreadType_ETWRundownThread);
4422 PMCGEN_TRACE_CONTEXT context = (PMCGEN_TRACE_CONTEXT)CallbackContext;
4423
4424 BOOLEAN bIsPublicTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeHandle);
4425
4426 BOOLEAN bIsPrivateTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimePrivateHandle);
4427
4428 BOOLEAN bIsRundownTraceHandle = (context->RegistrationHandle==Microsoft_Windows_DotNETRuntimeRundownHandle);
4429
4430 GCEventKeyword keywords = static_cast<GCEventKeyword>(MatchAnyKeyword);
4431 GCEventLevel level = static_cast<GCEventLevel>(Level);
4432 GCHeapUtilities::RecordEventStateChange(!!bIsPublicTraceHandle, keywords, level);
4433
4434 // EventPipeEtwCallback contains some GC eventing functionality shared between EventPipe and ETW.
4435 // Eventually, we'll want to merge these two codepaths whenever we can.
4436 CallbackProviderIndex providerIndex = DotNETRuntime;
4437 if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeHandle) {
4438 providerIndex = DotNETRuntime;
4439 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeRundownHandle) {
4440 providerIndex = DotNETRuntimeRundown;
4441 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimeStressHandle) {
4442 providerIndex = DotNETRuntimeStress;
4443 } else if (context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) {
4444 providerIndex = DotNETRuntimePrivate;
4445 } else {
4446 assert(!"unknown registration handle");
4447 return;
4448 }
4449
4450 EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData);
4451
4452 // TypeSystemLog needs a notification when certain keywords are modified, so
4453 // give it a hook here.
4454 if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle)
4455 {
4456 ETW::TypeSystemLog::OnKeywordsChanged();
4457 }
4458
4459 // A manifest based provider can be enabled to multiple event tracing sessions
4460 // As long as there is atleast 1 enabled session, IsEnabled will be TRUE
4461 // Since classic providers can be enabled to only a single session,
4462 // IsEnabled will be TRUE when it is enabled and FALSE when disabled
4463 BOOL bEnabled =
4464 ((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
4465 (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE));
4466 if(bEnabled)
4467 {
4468 if (bIsPrivateTraceHandle)
4469 {
4470 ETW::GCLog::GCSettingsEvent();
4471 if(g_fEEStarted && !g_fEEShutDown)
4472 {
4473 ETW::EnumerationLog::ModuleRangeRundown();
4474 }
4475 }
4476
4477#ifdef _TARGET_AMD64_
4478 // We only do this on amd64 (NOT ARM, because ARM uses frame based stack crawling)
4479 // If we have turned on the JIT keyword to the INFORMATION setting (needed to get JIT names) then
4480 // we assume that we also want good stack traces so we need to publish unwind information so
4481 // ETW can get at it
4482 if(bIsPublicTraceHandle && ETW_CATEGORY_ENABLED((*context), TRACE_LEVEL_INFORMATION, CLR_RUNDOWNJIT_KEYWORD))
4483 UnwindInfoTable::PublishUnwindInfo(g_fEEStarted != FALSE);
4484#endif
4485
4486 if(g_fEEStarted && !g_fEEShutDown && bIsRundownTraceHandle)
4487 {
4488 // Fire the runtime information event
4489 ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Callback);
4490
4491 // Start and End Method/Module Rundowns
4492 // Used to fire events that we missed since we started the controller after the process started
4493 // flags for immediate start rundown
4494 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4495 TRACE_LEVEL_INFORMATION,
4496 CLR_RUNDOWNSTART_KEYWORD))
4497 ETW::EnumerationLog::StartRundown();
4498
4499 // flags delayed end rundown
4500 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
4501 TRACE_LEVEL_INFORMATION,
4502 CLR_RUNDOWNEND_KEYWORD))
4503 ETW::EnumerationLog::EndRundown();
4504 }
4505
4506 if (g_fEEStarted && !g_fEEShutDown && (ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE))
4507 {
4508 ETW::EnumerationLog::EnumerateForCaptureState();
4509 }
4510 }
4511#ifdef FEATURE_COMINTEROP
4512 if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, CCWRefCountChange))
4513 g_pConfig->SetLogCCWRefCountChangeEnabled(bEnabled != 0);
4514#endif // FEATURE_COMINTEROP
4515
4516 }
4517}
4518#endif // FEATURE_REDHAWK
4519
4520#endif // FEATURE_PAL
4521#ifndef FEATURE_REDHAWK
4522
4523/****************************************************************************/
4524/* This is called by the runtime when an exception is thrown */
4525/****************************************************************************/
4526VOID ETW::ExceptionLog::ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownException, BOOL bIsNewException)
4527{
4528 CONTRACTL {
4529 NOTHROW;
4530 GC_TRIGGERS;
4531 PRECONDITION(GetThread() != NULL);
4532 PRECONDITION(GetThread()->GetThrowable() != NULL);
4533 } CONTRACTL_END;
4534
4535 if(!(bIsReThrownException || bIsNewException))
4536 {
4537 return;
4538 }
4539 if(!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionThrown_V1))
4540 {
4541 return;
4542 }
4543 EX_TRY
4544 {
4545 SString exceptionType(W(""));
4546 LPWSTR exceptionMessage = NULL;
4547 BOOL bIsCLSCompliant=FALSE, bIsCSE=FALSE, bIsNestedException=FALSE, bHasInnerException=FALSE;
4548 UINT16 exceptionFlags=0;
4549 PVOID exceptionEIP=0;
4550
4551 Thread *pThread = GetThread();
4552
4553 struct
4554 {
4555 OBJECTREF exceptionObj;
4556 OBJECTREF innerExceptionObj;
4557 STRINGREF exceptionMessageRef;
4558 } gc;
4559 ZeroMemory(&gc, sizeof(gc));
4560 GCPROTECT_BEGIN(gc);
4561
4562 gc.exceptionObj = pThread->GetThrowable();
4563 gc.innerExceptionObj = ((EXCEPTIONREF)gc.exceptionObj)->GetInnerException();
4564
4565 ThreadExceptionState *pExState = pThread->GetExceptionState();
4566#ifndef WIN64EXCEPTIONS
4567 PTR_ExInfo pExInfo = NULL;
4568#else
4569 PTR_ExceptionTracker pExInfo = NULL;
4570#endif //!WIN64EXCEPTIONS
4571 pExInfo = pExState->GetCurrentExceptionTracker();
4572 _ASSERTE(pExInfo != NULL);
4573 bIsNestedException = (pExInfo->GetPreviousExceptionTracker() != NULL);
4574 bIsCSE = (pExInfo->GetCorruptionSeverity() == ProcessCorrupting);
4575 bIsCLSCompliant = IsException((gc.exceptionObj)->GetMethodTable()) &&
4576 ((gc.exceptionObj)->GetMethodTable() != MscorlibBinder::GetException(kRuntimeWrappedException));
4577
4578 // A rethrown exception is also a nested exception
4579 // but since we have a separate flag for it, lets unset the nested flag
4580 if(bIsReThrownException)
4581 {
4582 bIsNestedException = FALSE;
4583 }
4584 bHasInnerException = (gc.innerExceptionObj) != NULL;
4585
4586 exceptionFlags = ((bHasInnerException ? ETW::ExceptionLog::ExceptionStructs::HasInnerException : 0) |
4587 (bIsNestedException ? ETW::ExceptionLog::ExceptionStructs::IsNestedException : 0) |
4588 (bIsReThrownException ? ETW::ExceptionLog::ExceptionStructs::IsReThrownException : 0) |
4589 (bIsCSE ? ETW::ExceptionLog::ExceptionStructs::IsCSE : 0) |
4590 (bIsCLSCompliant ? ETW::ExceptionLog::ExceptionStructs::IsCLSCompliant : 0));
4591
4592 if (pCf->IsFrameless())
4593 {
4594#ifndef _WIN64
4595 exceptionEIP = (PVOID)pCf->GetRegisterSet()->ControlPC;
4596#else
4597 exceptionEIP = (PVOID)GetIP(pCf->GetRegisterSet()->pContext);
4598#endif //!_WIN64
4599 }
4600 else
4601 {
4602 exceptionEIP = (PVOID)(pCf->GetFrame()->GetIP());
4603 }
4604
4605 // On platforms other than IA64, we are at the instruction after the faulting instruction
4606 // This check has been copied from StackTraceInfo::AppendElement
4607 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && exceptionEIP != 0)
4608 {
4609 exceptionEIP = (PVOID)((UINT_PTR)exceptionEIP - 1);
4610 }
4611
4612 gc.exceptionMessageRef = ((EXCEPTIONREF)gc.exceptionObj)->GetMessage();
4613 TypeHandle exceptionTypeHandle = (gc.exceptionObj)->GetTypeHandle();
4614 exceptionTypeHandle.GetName(exceptionType);
4615 WCHAR *exceptionTypeName = (WCHAR *)exceptionType.GetUnicode();
4616
4617 if(gc.exceptionMessageRef != NULL)
4618 {
4619 exceptionMessage = (gc.exceptionMessageRef)->GetBuffer();
4620 }
4621
4622 HRESULT exceptionHRESULT = ((EXCEPTIONREF)gc.exceptionObj)->GetHResult();
4623
4624 FireEtwExceptionThrown_V1(exceptionTypeName,
4625 exceptionMessage,
4626 exceptionEIP,
4627 exceptionHRESULT,
4628 exceptionFlags,
4629 GetClrInstanceId());
4630 GCPROTECT_END();
4631 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4632}
4633
4634
4635VOID ETW::ExceptionLog::ExceptionThrownEnd()
4636{
4637 CONTRACTL{
4638 NOTHROW;
4639 GC_NOTRIGGER;
4640 } CONTRACTL_END;
4641
4642 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionThrownStop))
4643 {
4644 return;
4645 }
4646
4647 FireEtwExceptionThrownStop();
4648}
4649
4650/****************************************************************************/
4651/* This is called by the runtime when an exception is handled by the runtime */
4652/****************************************************************************/
4653VOID ETW::ExceptionLog::ExceptionCatchBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4654{
4655 CONTRACTL{
4656 NOTHROW;
4657 GC_NOTRIGGER;
4658 } CONTRACTL_END;
4659
4660 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionCatchStart))
4661 {
4662 return;
4663 }
4664
4665 EX_TRY
4666 {
4667 SString methodName;
4668 pMethodDesc->GetFullMethodInfo(methodName);
4669
4670 FireEtwExceptionCatchStart((uint64_t)pEntryEIP,
4671 (uint64_t)pMethodDesc,
4672 methodName.GetUnicode(),
4673 GetClrInstanceId());
4674
4675 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4676}
4677
4678VOID ETW::ExceptionLog::ExceptionCatchEnd()
4679{
4680 CONTRACTL{
4681 NOTHROW;
4682 GC_NOTRIGGER;
4683 } CONTRACTL_END;
4684
4685 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionCatchStop))
4686 {
4687 return;
4688 }
4689
4690 FireEtwExceptionCatchStop();
4691}
4692
4693VOID ETW::ExceptionLog::ExceptionFinallyBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4694{
4695 CONTRACTL{
4696 NOTHROW;
4697 GC_NOTRIGGER;
4698 } CONTRACTL_END;
4699
4700 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFinallyStart))
4701 {
4702 return;
4703 }
4704
4705 EX_TRY
4706 {
4707 SString methodName;
4708 pMethodDesc->GetFullMethodInfo(methodName);
4709
4710 FireEtwExceptionFinallyStart((uint64_t)pEntryEIP,
4711 (uint64_t)pMethodDesc,
4712 methodName.GetUnicode(),
4713 GetClrInstanceId());
4714
4715 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4716}
4717
4718VOID ETW::ExceptionLog::ExceptionFinallyEnd()
4719{
4720 CONTRACTL{
4721 NOTHROW;
4722 GC_NOTRIGGER;
4723 } CONTRACTL_END;
4724
4725 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFinallyStop))
4726 {
4727 return;
4728 }
4729
4730 FireEtwExceptionFinallyStop();
4731}
4732
4733VOID ETW::ExceptionLog::ExceptionFilterBegin(MethodDesc * pMethodDesc, PVOID pEntryEIP)
4734{
4735 CONTRACTL{
4736 NOTHROW;
4737 GC_NOTRIGGER;
4738 } CONTRACTL_END;
4739
4740 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFilterStart))
4741 {
4742 return;
4743 }
4744
4745 EX_TRY
4746 {
4747 SString methodName;
4748 pMethodDesc->GetFullMethodInfo(methodName);
4749
4750 FireEtwExceptionFilterStart((uint64_t)pEntryEIP,
4751 (uint64_t)pMethodDesc,
4752 methodName.GetUnicode(),
4753 GetClrInstanceId());
4754
4755 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
4756}
4757
4758VOID ETW::ExceptionLog::ExceptionFilterEnd()
4759{
4760 CONTRACTL{
4761 NOTHROW;
4762 GC_NOTRIGGER;
4763 } CONTRACTL_END;
4764
4765 if (!ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ExceptionFilterStop))
4766 {
4767 return;
4768 }
4769
4770 FireEtwExceptionFilterStop();
4771}
4772
4773/****************************************************************************/
4774/* This is called by the runtime when a domain is loaded */
4775/****************************************************************************/
4776VOID ETW::LoaderLog::DomainLoadReal(BaseDomain *pDomain, __in_opt LPWSTR wszFriendlyName)
4777{
4778 CONTRACTL {
4779 NOTHROW;
4780 GC_TRIGGERS;
4781 } CONTRACTL_END;
4782
4783 EX_TRY
4784 {
4785 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4786 TRACE_LEVEL_INFORMATION,
4787 CLR_LOADER_KEYWORD))
4788 {
4789 DWORD dwEventOptions = ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad;
4790 ETW::LoaderLog::SendDomainEvent(pDomain, dwEventOptions, wszFriendlyName);
4791 }
4792 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4793}
4794
4795/****************************************************************************/
4796/* This is called by the runtime when an AppDomain is unloaded */
4797/****************************************************************************/
4798VOID ETW::LoaderLog::DomainUnload(AppDomain *pDomain)
4799{
4800 CONTRACTL {
4801 NOTHROW;
4802 GC_TRIGGERS;
4803 } CONTRACTL_END;
4804
4805 EX_TRY
4806 {
4807 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4808 TRACE_LEVEL_INFORMATION,
4809 KEYWORDZERO))
4810 {
4811 DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
4812
4813 // Domain unload also causes type unload events
4814 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4815 TRACE_LEVEL_INFORMATION,
4816 CLR_TYPE_KEYWORD))
4817 {
4818 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
4819 }
4820
4821 ETW::EnumerationLog::EnumerationHelper(NULL, pDomain, enumerationOptions);
4822 }
4823 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4824}
4825
4826/****************************************************************************/
4827/* This is called by the runtime when a LoaderAllocator is unloaded */
4828/****************************************************************************/
4829VOID ETW::LoaderLog::CollectibleLoaderAllocatorUnload(AssemblyLoaderAllocator *pLoaderAllocator)
4830{
4831 CONTRACTL {
4832 NOTHROW;
4833 GC_TRIGGERS;
4834 } CONTRACTL_END;
4835
4836 EX_TRY
4837 {
4838 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4839 TRACE_LEVEL_INFORMATION,
4840 KEYWORDZERO))
4841 {
4842 DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
4843
4844 // Collectible Loader Allocator unload also causes type unload events
4845 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4846 TRACE_LEVEL_INFORMATION,
4847 CLR_TYPE_KEYWORD))
4848 {
4849 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
4850 }
4851
4852 ETW::EnumerationLog::IterateCollectibleLoaderAllocator(pLoaderAllocator, enumerationOptions);
4853 }
4854 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4855}
4856
4857/****************************************************************************/
4858/* This is called by the runtime when the runtime is loaded
4859 Function gets called by both the Callback mechanism and regular ETW events.
4860 Type is used to differentiate whether its a callback or a normal call*/
4861/****************************************************************************/
4862VOID ETW::InfoLog::RuntimeInformation(INT32 type)
4863{
4864 CONTRACTL {
4865 NOTHROW;
4866 GC_TRIGGERS;
4867 } CONTRACTL_END;
4868
4869 EX_TRY {
4870 if((type == ETW::InfoLog::InfoStructs::Normal && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, RuntimeInformationStart))
4871 ||
4872 (type == ETW::InfoLog::InfoStructs::Callback && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context, RuntimeInformationDCStart))
4873 )
4874 {
4875 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W("");
4876 UINT8 startupMode = 0;
4877 UINT startupFlags = 0;
4878 PathString dllPath;
4879 UINT8 Sku = 0;
4880 _ASSERTE(CLRHosted() || g_fEEHostedStartup || // CLR started through one of the Hosting API CLRHosted() returns true if CLR started through the V2 Interface while
4881 // g_fEEHostedStartup is true if CLR is hosted through the V1 API.
4882 g_fEEComActivatedStartup || //CLR started as a COM object
4883 g_fEEOtherStartup ); //In case none of the 4 above mentioned cases are true for example ngen, ildasm then we asssume its a "other" startup
4884
4885 Sku = ETW::InfoLog::InfoStructs::CoreCLR;
4886
4887 //version info for clr.dll
4888 USHORT vmMajorVersion = CLR_MAJOR_VERSION;
4889 USHORT vmMinorVersion = CLR_MINOR_VERSION;
4890 USHORT vmBuildVersion = CLR_BUILD_VERSION;
4891 USHORT vmQfeVersion = CLR_BUILD_VERSION_QFE;
4892
4893 //version info for mscorlib.dll
4894 USHORT bclMajorVersion = VER_ASSEMBLYMAJORVERSION;
4895 USHORT bclMinorVersion = VER_ASSEMBLYMINORVERSION;
4896 USHORT bclBuildVersion = VER_ASSEMBLYBUILD;
4897 USHORT bclQfeVersion = VER_ASSEMBLYBUILD_QFE;
4898
4899 LPCGUID comGUID=&g_EEComObjectGuid;
4900
4901 PCWSTR lpwszCommandLine = W("");
4902
4903
4904
4905 // Determine the startupmode
4906 if (CLRHosted() || g_fEEHostedStartup)
4907 {
4908 //Hosted CLR
4909 startupMode = ETW::InfoLog::InfoStructs::HostedCLR;
4910 }
4911 else if(g_fEEComActivatedStartup)
4912 {
4913 //com activated
4914 startupMode = ETW::InfoLog::InfoStructs::COMActivated;
4915 }
4916 else if(g_fEEOtherStartup)
4917 {
4918 //startup type is other
4919 startupMode = ETW::InfoLog::InfoStructs::Other;
4920 }
4921
4922
4923 // if WszGetModuleFileName fails, we return an empty string
4924 if (!WszGetModuleFileName(GetCLRModule(), dllPath)) {
4925 dllPath.Set(W("\0"));
4926 }
4927
4928
4929 if(type == ETW::InfoLog::InfoStructs::Callback)
4930 {
4931 FireEtwRuntimeInformationDCStart( GetClrInstanceId(),
4932 Sku,
4933 bclMajorVersion,
4934 bclMinorVersion,
4935 bclBuildVersion,
4936 bclQfeVersion,
4937 vmMajorVersion,
4938 vmMinorVersion,
4939 vmBuildVersion,
4940 vmQfeVersion,
4941 startupFlags,
4942 startupMode,
4943 lpwszCommandLine,
4944 comGUID,
4945 dllPath );
4946 }
4947 else
4948 {
4949 FireEtwRuntimeInformationStart( GetClrInstanceId(),
4950 Sku,
4951 bclMajorVersion,
4952 bclMinorVersion,
4953 bclBuildVersion,
4954 bclQfeVersion,
4955 vmMajorVersion,
4956 vmMinorVersion,
4957 vmBuildVersion,
4958 vmQfeVersion,
4959 startupFlags,
4960 startupMode,
4961 lpwszCommandLine,
4962 comGUID,
4963 dllPath );
4964 }
4965 }
4966 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
4967}
4968
4969/* Fires ETW events every time a pdb is dynamically loaded.
4970*
4971* The ETW events correspond to sending parts of the pdb in roughly
4972* 64K sized chunks in order. Additional information sent is as follows:
4973* ModuleID, TotalChunks, Size of Current Chunk, Chunk Number, CLRInstanceID
4974*
4975* Note: The current implementation does not support reflection.emit.
4976* The method will silently return without firing an event.
4977*/
4978
4979VOID ETW::CodeSymbolLog::EmitCodeSymbols(Module* pModule)
4980{
4981#if !defined(FEATURE_PAL) //UNIXTODO: Enable EmitCodeSymbols
4982 CONTRACTL {
4983 NOTHROW;
4984 GC_NOTRIGGER;
4985 MODE_ANY;
4986 SO_NOT_MAINLINE;
4987 }
4988 CONTRACTL_END;
4989
4990
4991 EX_TRY {
4992 if (ETW_TRACING_CATEGORY_ENABLED(
4993 MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
4994 TRACE_LEVEL_VERBOSE,
4995 CLR_CODESYMBOLS_KEYWORD))
4996 {
4997 if (pModule != NULL)
4998 {
4999 UINT16 clrInstanceID = GetClrInstanceId();
5000 UINT64 moduleID = (ModuleID)pModule;
5001 DWORD length = 0;
5002 // We silently exit if pdb is of length 0 instead of sending an event with no pdb bytes
5003 if (CodeSymbolLog::GetInMemorySymbolsLength(pModule, &length) == S_OK && length > 0)
5004 {
5005 // The maximum data size allowed is 64K - (Size of the Event_Header)
5006 // Since the actual size of user data can only be determined at runtime
5007 // we simplify the header size value to be 1000 bytes as a conservative
5008 // estmate.
5009 static const DWORD maxDataSize = 63000;
5010
5011 ldiv_t qr = ldiv(length, maxDataSize);
5012
5013 // We do not allow pdbs of size greater than 2GB for now,
5014 // so totalChunks should fit in 16 bits.
5015 if (qr.quot < UINT16_MAX)
5016 {
5017 // If there are trailing bits in the last chunk, then increment totalChunks by 1
5018 UINT16 totalChunks = (UINT16)(qr.quot + ((qr.rem != 0) ? 1 : 0));
5019 NewArrayHolder<BYTE> chunk(new BYTE[maxDataSize]);
5020 DWORD offset = 0;
5021 for (UINT16 chunkNum = 0; offset < length; chunkNum++)
5022 {
5023 DWORD lengthRead = 0;
5024 // We expect ReadInMemorySymbols to always return maxDataSize sized chunks
5025 // Or it is the last chunk and it is less than maxDataSize.
5026 CodeSymbolLog::ReadInMemorySymbols(pModule, offset, chunk, maxDataSize, &lengthRead);
5027
5028 _ASSERTE(lengthRead == maxDataSize || // Either we are in the first to (n-1)th chunk
5029 (lengthRead < maxDataSize && chunkNum + 1 == totalChunks)); // Or we are in the last chunk
5030
5031 FireEtwCodeSymbols(moduleID, totalChunks, chunkNum, lengthRead, chunk, clrInstanceID);
5032 offset += lengthRead;
5033 }
5034 }
5035 }
5036 }
5037 }
5038 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
5039#endif// !defined(FEATURE_PAL)
5040}
5041
5042/* Returns the length of an in-memory symbol stream
5043*
5044* If the module has in-memory symbols the length of the stream will
5045* be placed in pCountSymbolBytes. If the module doesn't have in-memory
5046* symbols, *pCountSymbolBytes = 0
5047*
5048* Returns S_OK if the length could be determined (even if it is 0)
5049*
5050* Note: The current implementation does not support reflection.emit.
5051* CORPROF_E_MODULE_IS_DYNAMIC will be returned in that case.
5052*
5053* //IMPORTANT NOTE: The desktop code outside the Project K branch
5054* contains copies of this function in the clr\src\vm\proftoeeinterfaceimpl.cpp
5055* file of the desktop version corresponding to the profiler version
5056* of this feature. Anytime that feature/code is ported to Project K
5057* the code below should be appropriately merged so as to avoid
5058* duplication.
5059*/
5060
5061HRESULT ETW::CodeSymbolLog::GetInMemorySymbolsLength(
5062 Module* pModule,
5063 DWORD* pCountSymbolBytes)
5064{
5065 CONTRACTL
5066 {
5067 NOTHROW;
5068 GC_NOTRIGGER;
5069 MODE_ANY;
5070 SO_NOT_MAINLINE;
5071 }
5072 CONTRACTL_END;
5073
5074 HRESULT hr = S_OK;
5075 if (pCountSymbolBytes == NULL)
5076 {
5077 return E_INVALIDARG;
5078 }
5079 *pCountSymbolBytes = 0;
5080
5081 if (pModule == NULL)
5082 {
5083 return E_INVALIDARG;
5084 }
5085 if (pModule->IsBeingUnloaded())
5086 {
5087 return CORPROF_E_DATAINCOMPLETE;
5088 }
5089
5090 //This method would work fine on reflection.emit, but there would be no way to know
5091 //if some other thread was changing the size of the symbols before this method returned.
5092 //Adding events or locks to detect/prevent changes would make the scenario workable
5093 if (pModule->IsReflection())
5094 {
5095 return COR_PRF_MODULE_DYNAMIC;
5096 }
5097
5098 CGrowableStream* pStream = pModule->GetInMemorySymbolStream();
5099 if (pStream == NULL)
5100 {
5101 return S_OK;
5102 }
5103
5104 STATSTG SizeData = { 0 };
5105 hr = pStream->Stat(&SizeData, STATFLAG_NONAME);
5106 if (FAILED(hr))
5107 {
5108 return hr;
5109 }
5110 if (SizeData.cbSize.u.HighPart > 0)
5111 {
5112 return COR_E_OVERFLOW;
5113 }
5114 *pCountSymbolBytes = SizeData.cbSize.u.LowPart;
5115
5116 return S_OK;
5117}
5118
5119/* Reads bytes from an in-memory symbol stream
5120*
5121* This function attempts to read countSymbolBytes of data starting at offset
5122* symbolsReadOffset within the in-memory stream. The data will be copied into
5123* pSymbolBytes which is expected to have countSymbolBytes of space available.
5124* pCountSymbolsBytesRead contains the actual number of bytes read which
5125* may be less than countSymbolBytes if the end of the stream is reached.
5126*
5127* Returns S_OK if a non-zero number of bytes were read.
5128*
5129* Note: The current implementation does not support reflection.emit.
5130* CORPROF_E_MODULE_IS_DYNAMIC will be returned in that case.
5131*
5132* //IMPORTANT NOTE: The desktop code outside the Project K branch
5133* contains copies of this function in the clr\src\vm\proftoeeinterfaceimpl.cpp
5134* file of the desktop version corresponding to the profiler version
5135* of this feature. Anytime that feature/code is ported to Project K
5136* the code below should be appropriately merged so as to avoid
5137* duplication.
5138
5139*/
5140
5141HRESULT ETW::CodeSymbolLog::ReadInMemorySymbols(
5142 Module* pModule,
5143 DWORD symbolsReadOffset,
5144 BYTE* pSymbolBytes,
5145 DWORD countSymbolBytes,
5146 DWORD* pCountSymbolBytesRead)
5147{
5148 CONTRACTL
5149 {
5150 NOTHROW;
5151 GC_NOTRIGGER;
5152 MODE_ANY;
5153 SO_NOT_MAINLINE;
5154 }
5155 CONTRACTL_END;
5156
5157 HRESULT hr = S_OK;
5158 if (pSymbolBytes == NULL)
5159 {
5160 return E_INVALIDARG;
5161 }
5162 if (pCountSymbolBytesRead == NULL)
5163 {
5164 return E_INVALIDARG;
5165 }
5166 *pCountSymbolBytesRead = 0;
5167
5168 if (pModule == NULL)
5169 {
5170 return E_INVALIDARG;
5171 }
5172 if (pModule->IsBeingUnloaded())
5173 {
5174 return CORPROF_E_DATAINCOMPLETE;
5175 }
5176
5177 //This method would work fine on reflection.emit, but there would be no way to know
5178 //if some other thread was changing the size of the symbols before this method returned.
5179 //Adding events or locks to detect/prevent changes would make the scenario workable
5180 if (pModule->IsReflection())
5181 {
5182 return COR_PRF_MODULE_DYNAMIC;
5183 }
5184
5185 CGrowableStream* pStream = pModule->GetInMemorySymbolStream();
5186 if (pStream == NULL)
5187 {
5188 return E_INVALIDARG;
5189 }
5190
5191 STATSTG SizeData = { 0 };
5192 hr = pStream->Stat(&SizeData, STATFLAG_NONAME);
5193 if (FAILED(hr))
5194 {
5195 return hr;
5196 }
5197 if (SizeData.cbSize.u.HighPart > 0)
5198 {
5199 return COR_E_OVERFLOW;
5200 }
5201 DWORD streamSize = SizeData.cbSize.u.LowPart;
5202 if (symbolsReadOffset >= streamSize)
5203 {
5204 return E_INVALIDARG;
5205 }
5206
5207 *pCountSymbolBytesRead = min(streamSize - symbolsReadOffset, countSymbolBytes);
5208 memcpy_s(pSymbolBytes, countSymbolBytes, ((BYTE*)pStream->GetRawBuffer().StartAddress()) + symbolsReadOffset, *pCountSymbolBytesRead);
5209
5210 return S_OK;
5211}
5212
5213/*******************************************************/
5214/* This is called by the runtime when a method is jitted completely */
5215/*******************************************************/
5216VOID ETW::MethodLog::MethodJitted(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, SIZE_T pCode, ReJITID rejitID, BOOL bProfilerRejectedPrecompiledCode, BOOL bReadyToRunRejectedPrecompiledCode)
5217{
5218 CONTRACTL {
5219 NOTHROW;
5220 GC_TRIGGERS;
5221 } CONTRACTL_END;
5222
5223 EX_TRY
5224 {
5225 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5226 TRACE_LEVEL_INFORMATION,
5227 CLR_JIT_KEYWORD))
5228 {
5229 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodLoad, TRUE, namespaceOrClassName, methodName, methodSignature, pCode, rejitID, bProfilerRejectedPrecompiledCode, bReadyToRunRejectedPrecompiledCode);
5230 }
5231
5232 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5233 TRACE_LEVEL_INFORMATION,
5234 CLR_JITTEDMETHODILTONATIVEMAP_KEYWORD))
5235 {
5236 // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy
5237 // data has already been initialized.
5238
5239 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
5240 // or profiler is loaded. So it should always be available.
5241 _ASSERTE(g_pDebugInterface != NULL);
5242 g_pDebugInterface->InitializeLazyDataIfNecessary();
5243
5244 ETW::MethodLog::SendMethodILToNativeMapEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap, pCode, rejitID);
5245 }
5246
5247 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5248}
5249
5250/*************************************************/
5251/* This is called by the runtime when method jitting started */
5252/*************************************************/
5253VOID ETW::MethodLog::MethodJitting(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature)
5254{
5255 CONTRACTL {
5256 NOTHROW;
5257 GC_TRIGGERS;
5258 PRECONDITION(pMethodDesc != NULL);
5259 } CONTRACTL_END;
5260
5261 EX_TRY
5262 {
5263 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5264 TRACE_LEVEL_VERBOSE,
5265 CLR_JIT_KEYWORD))
5266 {
5267 pMethodDesc->GetMethodInfo(*namespaceOrClassName, *methodName, *methodSignature);
5268 ETW::MethodLog::SendMethodJitStartEvent(pMethodDesc, namespaceOrClassName, methodName, methodSignature);
5269 }
5270 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5271}
5272
5273/**********************************************************************/
5274/* This is called by the runtime when a single jit helper method with stub is initialized */
5275/**********************************************************************/
5276VOID ETW::MethodLog::StubInitialized(ULONGLONG ullHelperStartAddress, LPCWSTR pHelperName)
5277{
5278 CONTRACTL {
5279 NOTHROW;
5280 GC_TRIGGERS;
5281 PRECONDITION(ullHelperStartAddress != 0);
5282 } CONTRACTL_END;
5283
5284 EX_TRY
5285 {
5286 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5287 TRACE_LEVEL_INFORMATION,
5288 CLR_JIT_KEYWORD))
5289 {
5290 DWORD dwHelperSize=0;
5291 Stub::RecoverStubAndSize((TADDR)ullHelperStartAddress, &dwHelperSize);
5292 ETW::MethodLog::SendHelperEvent(ullHelperStartAddress, dwHelperSize, pHelperName);
5293 }
5294 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5295}
5296
5297/**********************************************************/
5298/* This is called by the runtime when helpers with stubs are initialized */
5299/**********************************************************/
5300VOID ETW::MethodLog::StubsInitialized(PVOID *pHelperStartAddress, PVOID *pHelperNames, LONG lNoOfHelpers)
5301{
5302 WRAPPER_NO_CONTRACT;
5303
5304 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5305 TRACE_LEVEL_INFORMATION,
5306 CLR_JIT_KEYWORD))
5307 {
5308 for(int i=0; i<lNoOfHelpers; i++)
5309 {
5310 if(pHelperStartAddress[i])
5311 {
5312 StubInitialized((ULONGLONG)pHelperStartAddress[i], (LPCWSTR)pHelperNames[i]);
5313 }
5314 }
5315 }
5316}
5317
5318/****************************************************************************/
5319/* This is called by the runtime when a dynamic method is destroyed */
5320/****************************************************************************/
5321VOID ETW::MethodLog::DynamicMethodDestroyed(MethodDesc *pMethodDesc)
5322{
5323 CONTRACTL {
5324 NOTHROW;
5325 GC_TRIGGERS;
5326 } CONTRACTL_END;
5327
5328 EX_TRY
5329 {
5330 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5331 TRACE_LEVEL_INFORMATION,
5332 CLR_JIT_KEYWORD))
5333 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::JitMethodUnload, TRUE);
5334 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5335}
5336
5337/****************************************************************************/
5338/* This is called by the runtime when a ngen method is restored */
5339/****************************************************************************/
5340VOID ETW::MethodLog::MethodRestored(MethodDesc *pMethodDesc)
5341{
5342 CONTRACTL {
5343 NOTHROW;
5344 GC_TRIGGERS;
5345 } CONTRACTL_END;
5346
5347 EX_TRY
5348 {
5349 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed()
5350 &&
5351 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5352 TRACE_LEVEL_INFORMATION,
5353 CLR_STARTENUMERATION_KEYWORD))
5354 {
5355 ETW::MethodLog::SendMethodEvent(pMethodDesc, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE);
5356 }
5357 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5358}
5359
5360/****************************************************************************/
5361/* This is called by the runtime when a method table is restored */
5362/****************************************************************************/
5363VOID ETW::MethodLog::MethodTableRestored(MethodTable *pMethodTable)
5364{
5365 CONTRACTL {
5366 NOTHROW;
5367 GC_TRIGGERS;
5368 } CONTRACTL_END;
5369 EX_TRY
5370 {
5371 if(IsRuntimeNgenKeywordEnabledAndNotSuppressed()
5372 &&
5373 ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5374 TRACE_LEVEL_INFORMATION,
5375 CLR_STARTENUMERATION_KEYWORD))
5376 {
5377 {
5378 MethodTable::MethodIterator iter(pMethodTable);
5379 for (; iter.IsValid(); iter.Next())
5380 {
5381 MethodDesc *pMD = (MethodDesc *)(iter.GetMethodDesc());
5382 if(pMD && pMD->IsRestored() && pMD->GetMethodTable_NoLogging() == pMethodTable)
5383 ETW::MethodLog::SendMethodEvent(pMD, ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad, FALSE);
5384 }
5385 }
5386 }
5387 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5388}
5389
5390
5391/****************************************************************************/
5392/* This is called by the runtime when a Strong Name Verification Starts */
5393/****************************************************************************/
5394VOID ETW::SecurityLog::StrongNameVerificationStart(DWORD dwInFlags, __in LPWSTR strFullyQualifiedAssemblyName)
5395{
5396 WRAPPER_NO_CONTRACT;
5397}
5398
5399
5400/****************************************************************************/
5401/* This is called by the runtime when a Strong Name Verification Ends */
5402/****************************************************************************/
5403VOID ETW::SecurityLog::StrongNameVerificationStop(DWORD dwInFlags,ULONG result, __in LPWSTR strFullyQualifiedAssemblyName)
5404{
5405 WRAPPER_NO_CONTRACT;
5406}
5407
5408/****************************************************************************/
5409/* This is called by the runtime when field transparency calculations begin */
5410/****************************************************************************/
5411void ETW::SecurityLog::FireFieldTransparencyComputationStart(LPCWSTR wszFieldName,
5412 LPCWSTR wszModuleName,
5413 DWORD dwAppDomain)
5414{
5415 WRAPPER_NO_CONTRACT;
5416 FireEtwFieldTransparencyComputationStart(wszFieldName, wszModuleName, dwAppDomain, GetClrInstanceId());
5417}
5418
5419/****************************************************************************/
5420/* This is called by the runtime when field transparency calculations end */
5421/****************************************************************************/
5422void ETW::SecurityLog::FireFieldTransparencyComputationEnd(LPCWSTR wszFieldName,
5423 LPCWSTR wszModuleName,
5424 DWORD dwAppDomain,
5425 BOOL fIsCritical,
5426 BOOL fIsTreatAsSafe)
5427{
5428 WRAPPER_NO_CONTRACT;
5429 FireEtwFieldTransparencyComputationEnd(wszFieldName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5430}
5431
5432/*****************************************************************************/
5433/* This is called by the runtime when method transparency calculations begin */
5434/*****************************************************************************/
5435void ETW::SecurityLog::FireMethodTransparencyComputationStart(LPCWSTR wszMethodName,
5436 LPCWSTR wszModuleName,
5437 DWORD dwAppDomain)
5438{
5439 WRAPPER_NO_CONTRACT;
5440 FireEtwMethodTransparencyComputationStart(wszMethodName, wszModuleName, dwAppDomain, GetClrInstanceId());
5441}
5442
5443/*****************************************************************************/
5444/* This is called by the runtime when method transparency calculations end */
5445/********************************************(********************************/
5446void ETW::SecurityLog::FireMethodTransparencyComputationEnd(LPCWSTR wszMethodName,
5447 LPCWSTR wszModuleName,
5448 DWORD dwAppDomain,
5449 BOOL fIsCritical,
5450 BOOL fIsTreatAsSafe)
5451{
5452 WRAPPER_NO_CONTRACT;
5453 FireEtwMethodTransparencyComputationEnd(wszMethodName, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5454}
5455
5456/*****************************************************************************/
5457/* This is called by the runtime when module transparency calculations begin */
5458/*****************************************************************************/
5459void ETW::SecurityLog::FireModuleTransparencyComputationStart(LPCWSTR wszModuleName,
5460 DWORD dwAppDomain)
5461{
5462 WRAPPER_NO_CONTRACT;
5463 FireEtwModuleTransparencyComputationStart(wszModuleName, dwAppDomain, GetClrInstanceId());
5464}
5465
5466/****************************************************************************/
5467/* This is called by the runtime when module transparency calculations end */
5468/****************************************************************************/
5469void ETW::SecurityLog::FireModuleTransparencyComputationEnd(LPCWSTR wszModuleName,
5470 DWORD dwAppDomain,
5471 BOOL fIsAllCritical,
5472 BOOL fIsAllTransparent,
5473 BOOL fIsTreatAsSafe,
5474 BOOL fIsOpportunisticallyCritical,
5475 DWORD dwSecurityRuleSet)
5476{
5477 WRAPPER_NO_CONTRACT;
5478 FireEtwModuleTransparencyComputationEnd(wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsTreatAsSafe, fIsOpportunisticallyCritical, dwSecurityRuleSet, GetClrInstanceId());
5479}
5480
5481/****************************************************************************/
5482/* This is called by the runtime when token transparency calculations begin */
5483/****************************************************************************/
5484void ETW::SecurityLog::FireTokenTransparencyComputationStart(DWORD dwToken,
5485 LPCWSTR wszModuleName,
5486 DWORD dwAppDomain)
5487{
5488 WRAPPER_NO_CONTRACT;
5489 FireEtwTokenTransparencyComputationStart(dwToken, wszModuleName, dwAppDomain, GetClrInstanceId());
5490}
5491
5492/****************************************************************************/
5493/* This is called by the runtime when token transparency calculations end */
5494/****************************************************************************/
5495void ETW::SecurityLog::FireTokenTransparencyComputationEnd(DWORD dwToken,
5496 LPCWSTR wszModuleName,
5497 DWORD dwAppDomain,
5498 BOOL fIsCritical,
5499 BOOL fIsTreatAsSafe)
5500{
5501 WRAPPER_NO_CONTRACT;
5502 FireEtwTokenTransparencyComputationEnd(dwToken, wszModuleName, dwAppDomain, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5503}
5504
5505/*****************************************************************************/
5506/* This is called by the runtime when type transparency calculations begin */
5507/*****************************************************************************/
5508void ETW::SecurityLog::FireTypeTransparencyComputationStart(LPCWSTR wszTypeName,
5509 LPCWSTR wszModuleName,
5510 DWORD dwAppDomain)
5511{
5512 WRAPPER_NO_CONTRACT;
5513 FireEtwTypeTransparencyComputationStart(wszTypeName, wszModuleName, dwAppDomain, GetClrInstanceId());
5514}
5515
5516/****************************************************************************/
5517/* This is called by the runtime when type transparency calculations end */
5518/****************************************************************************/
5519void ETW::SecurityLog::FireTypeTransparencyComputationEnd(LPCWSTR wszTypeName,
5520 LPCWSTR wszModuleName,
5521 DWORD dwAppDomain,
5522 BOOL fIsAllCritical,
5523 BOOL fIsAllTransparent,
5524 BOOL fIsCritical,
5525 BOOL fIsTreatAsSafe)
5526{
5527 WRAPPER_NO_CONTRACT;
5528 FireEtwTypeTransparencyComputationEnd(wszTypeName, wszModuleName, dwAppDomain, fIsAllCritical, fIsAllTransparent, fIsCritical, fIsTreatAsSafe, GetClrInstanceId());
5529}
5530
5531/**********************************************************************************/
5532/* This is called by the runtime when a module is loaded */
5533/* liReportedSharedModule will be 0 when this module is reported for the 1st time */
5534/**********************************************************************************/
5535VOID ETW::LoaderLog::ModuleLoad(Module *pModule, LONG liReportedSharedModule)
5536{
5537 CONTRACTL {
5538 NOTHROW;
5539 GC_TRIGGERS;
5540 } CONTRACTL_END;
5541
5542 EX_TRY
5543 {
5544 DWORD enumerationOptions = ETW::EnumerationLog::EnumerationStructs::None;
5545 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5546 TRACE_LEVEL_INFORMATION,
5547 KEYWORDZERO))
5548 {
5549 BOOL bTraceFlagLoaderSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5550 TRACE_LEVEL_INFORMATION,
5551 CLR_LOADER_KEYWORD);
5552 BOOL bTraceFlagNgenMethodSet = IsRuntimeNgenKeywordEnabledAndNotSuppressed();
5553 BOOL bTraceFlagStartRundownSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5554 TRACE_LEVEL_INFORMATION,
5555 CLR_STARTENUMERATION_KEYWORD);
5556 BOOL bTraceFlagPerfTrackSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
5557 TRACE_LEVEL_INFORMATION,
5558 CLR_PERFTRACK_KEYWORD);
5559
5560 if(liReportedSharedModule == 0)
5561 {
5562
5563 if(bTraceFlagLoaderSet)
5564 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad;
5565 if (bTraceFlagPerfTrackSet)
5566 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad;
5567 if(bTraceFlagNgenMethodSet && bTraceFlagStartRundownSet)
5568 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad;
5569
5570 if(pModule->IsManifest() && bTraceFlagLoaderSet)
5571 ETW::LoaderLog::SendAssemblyEvent(pModule->GetAssembly(), enumerationOptions);
5572
5573 if(bTraceFlagLoaderSet || bTraceFlagPerfTrackSet)
5574 ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad | ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad);
5575
5576 ETW::EnumerationLog::EnumerationHelper(pModule, NULL, enumerationOptions);
5577 }
5578
5579 // we want to report domainmodule events whenever they are loaded in any AppDomain
5580 if(bTraceFlagLoaderSet)
5581 ETW::LoaderLog::SendModuleEvent(pModule, ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad, TRUE);
5582 }
5583
5584 {
5585 BOOL bTraceFlagPerfTrackPrivateSet = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
5586 TRACE_LEVEL_INFORMATION,
5587 CLR_PERFTRACK_PRIVATE_KEYWORD);
5588 if (liReportedSharedModule == 0 && bTraceFlagPerfTrackPrivateSet)
5589 {
5590 enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate;
5591 ETW::LoaderLog::SendModuleRange(pModule, enumerationOptions);
5592 }
5593 }
5594 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5595}
5596
5597/****************************************************************************/
5598/* This is called by the runtime when the process is being shutdown */
5599/****************************************************************************/
5600VOID ETW::EnumerationLog::ProcessShutdown()
5601{
5602 CONTRACTL {
5603 NOTHROW;
5604 GC_TRIGGERS;
5605 } CONTRACTL_END;
5606
5607 EX_TRY
5608 {
5609 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, TRACE_LEVEL_INFORMATION, KEYWORDZERO))
5610 {
5611 DWORD enumerationOptions = GetEnumerationOptionsFromRuntimeKeywords();
5612
5613 // Send unload events for all remaining domains, including shared domain and
5614 // default domain.
5615 ETW::EnumerationLog::EnumerationHelper(NULL /* module filter */, NULL /* domain filter */, enumerationOptions);
5616 }
5617 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
5618}
5619
5620/****************************************************************************/
5621/****************************************************************************/
5622/* Begining of helper functions */
5623/****************************************************************************/
5624/****************************************************************************/
5625
5626/****************************************************************************/
5627/* This routine is used to send a domain load/unload or rundown event */
5628/****************************************************************************/
5629VOID ETW::LoaderLog::SendDomainEvent(BaseDomain *pBaseDomain, DWORD dwEventOptions, LPCWSTR wszFriendlyName)
5630{
5631 CONTRACTL {
5632 THROWS;
5633 GC_TRIGGERS;
5634 } CONTRACTL_END;
5635
5636 if(!pBaseDomain)
5637 return;
5638
5639 PCWSTR szDtraceOutput1=W("");
5640 BOOL bIsAppDomain = pBaseDomain->IsAppDomain();
5641
5642 ULONGLONG ullDomainId = (ULONGLONG)pBaseDomain;
5643 ULONG ulDomainFlags = ETW::LoaderLog::LoaderStructs::DefaultDomain | ETW::LoaderLog::LoaderStructs::ExecutableDomain;
5644
5645 LPCWSTR wsEmptyString = W("");
5646
5647 LPWSTR lpswzDomainName = (LPWSTR)wsEmptyString;
5648
5649 if(wszFriendlyName)
5650 lpswzDomainName = (PWCHAR)wszFriendlyName;
5651 else
5652 lpswzDomainName = (PWCHAR)pBaseDomain->AsAppDomain()->GetFriendlyName();
5653
5654 /* prepare events args for ETW and ETM */
5655 szDtraceOutput1 = (PCWSTR)lpswzDomainName;
5656
5657 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
5658 {
5659 FireEtwAppDomainLoad_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5660 }
5661 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
5662 {
5663 FireEtwAppDomainUnload_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5664 }
5665 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
5666 {
5667 FireEtwAppDomainDCStart_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5668 }
5669 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
5670 {
5671 FireEtwAppDomainDCEnd_V1(ullDomainId, ulDomainFlags, szDtraceOutput1, pBaseDomain->GetId().m_dwId, GetClrInstanceId());
5672 }
5673 else
5674 {
5675 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
5676 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
5677 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
5678 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
5679 }
5680}
5681
5682/********************************************************/
5683/* This routine is used to send thread rundown events when ARM is enabled */
5684/********************************************************/
5685VOID ETW::EnumerationLog::SendThreadRundownEvent()
5686{
5687 CONTRACTL {
5688 THROWS;
5689 GC_TRIGGERS;
5690 } CONTRACTL_END;
5691
5692#ifndef DACCESS_COMPILE
5693 Thread *pThread = NULL;
5694
5695 // Take the thread store lock while we enumerate threads.
5696 ThreadStoreLockHolder tsl;
5697 while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
5698 {
5699 if (pThread->IsUnstarted() || pThread->IsDead())
5700 continue;
5701
5702 // Send thread rundown provider events and thread created runtime provider
5703 // events (depending on which are enabled)
5704 ThreadLog::FireThreadDC(pThread);
5705 ThreadLog::FireThreadCreated(pThread);
5706 }
5707#endif // !DACCESS_COMPILE
5708}
5709
5710/****************************************************************************/
5711/* This routine is used to send an assembly load/unload or rundown event ****/
5712/****************************************************************************/
5713
5714VOID ETW::LoaderLog::SendAssemblyEvent(Assembly *pAssembly, DWORD dwEventOptions)
5715{
5716 CONTRACTL {
5717 THROWS;
5718 GC_TRIGGERS;
5719 } CONTRACTL_END;
5720
5721 if(!pAssembly)
5722 return;
5723
5724 PCWSTR szDtraceOutput1=W("");
5725 BOOL bIsDynamicAssembly = pAssembly->IsDynamic();
5726 BOOL bIsCollectibleAssembly = pAssembly->IsCollectible();
5727 BOOL bHasNativeImage = pAssembly->GetManifestFile()->HasNativeImage();
5728 BOOL bIsReadyToRun = pAssembly->GetManifestFile()->IsILImageReadyToRun();
5729
5730 ULONGLONG ullAssemblyId = (ULONGLONG)pAssembly;
5731 ULONGLONG ullDomainId = (ULONGLONG)pAssembly->GetDomain();
5732 ULONGLONG ullBindingID = 0;
5733 ULONG ulAssemblyFlags = ((bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicAssembly : 0) |
5734 (bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeAssembly : 0) |
5735 (bIsCollectibleAssembly ? ETW::LoaderLog::LoaderStructs::CollectibleAssembly : 0) |
5736 (bIsReadyToRun ? ETW::LoaderLog::LoaderStructs::ReadyToRunAssembly : 0));
5737
5738 SString sAssemblyPath;
5739 pAssembly->GetDisplayName(sAssemblyPath);
5740 LPWSTR lpszAssemblyPath = (LPWSTR)sAssemblyPath.GetUnicode();
5741
5742/* prepare events args for ETW and ETM */
5743 szDtraceOutput1 = (PCWSTR)lpszAssemblyPath;
5744
5745 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
5746 {
5747 FireEtwAssemblyLoad_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5748 }
5749 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
5750 {
5751 FireEtwAssemblyUnload_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5752 }
5753 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
5754 {
5755 FireEtwAssemblyDCStart_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5756 }
5757 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
5758 {
5759 FireEtwAssemblyDCEnd_V1(ullAssemblyId, ullDomainId, ullBindingID, ulAssemblyFlags, szDtraceOutput1, GetClrInstanceId());
5760 }
5761 else
5762 {
5763 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
5764 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
5765 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
5766 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
5767 }
5768}
5769
5770ETW_INLINE
5771 ULONG
5772 ETW::LoaderLog::SendModuleRange(
5773 __in Module *pModule,
5774 __in DWORD dwEventOptions)
5775
5776{
5777 ULONG Result = ERROR_SUCCESS;
5778
5779
5780 // do not fire the ETW event when:
5781 // 1. We did not load the native image
5782 // 2. We do not have IBC data for the native image
5783 if( !pModule || !pModule->HasNativeImage() || !pModule->IsIbcOptimized() )
5784 {
5785 return Result;
5786 }
5787
5788 // get information about the hot sections from the native image that has been loaded
5789 COUNT_T cbSizeOfSectionTable;
5790 CORCOMPILE_VIRTUAL_SECTION_INFO* pVirtualSectionsTable = (CORCOMPILE_VIRTUAL_SECTION_INFO* )pModule->GetNativeImage()->GetVirtualSectionsTable(&cbSizeOfSectionTable);
5791
5792 COUNT_T RangeCount = cbSizeOfSectionTable/sizeof(CORCOMPILE_VIRTUAL_SECTION_INFO);
5793
5794 // if we do not have any hot ranges, we do not fire the ETW event
5795
5796 // Figure out the rest of the event data
5797 UINT16 ClrInstanceId = GetClrInstanceId();
5798 UINT64 ModuleID = (ULONGLONG)(TADDR) pModule;
5799
5800 for (COUNT_T i = 0; i < RangeCount; ++i)
5801 {
5802 DWORD rangeBegin = pVirtualSectionsTable[i].VirtualAddress;
5803 DWORD rangeSize = pVirtualSectionsTable[i].Size;
5804 DWORD sectionType = pVirtualSectionsTable[i].SectionType;
5805
5806 UINT8 ibcType = VirtualSectionData::IBCType(sectionType);
5807 UINT8 rangeType = VirtualSectionData::RangeType(sectionType);
5808 UINT16 virtualSectionType = VirtualSectionData::VirtualSectionType(sectionType);
5809 BOOL isIBCProfiledColdSection = VirtualSectionData::IsIBCProfiledColdSection(sectionType);
5810 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad)
5811 {
5812 if (isIBCProfiledColdSection)
5813 Result &= FireEtwModuleRangeLoad(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5814 }
5815 else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart)
5816 {
5817 if (isIBCProfiledColdSection)
5818 Result &= FireEtwModuleRangeDCStart(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5819 }
5820 else if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd)
5821 {
5822 if (isIBCProfiledColdSection)
5823 Result &= FireEtwModuleRangeDCEnd(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType);
5824 }
5825 // Fire private events if they are requested.
5826 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate)
5827 {
5828 Result &= FireEtwModuleRangeLoadPrivate(ClrInstanceId, ModuleID, rangeBegin, rangeSize, rangeType, ibcType, virtualSectionType);
5829 }
5830 }
5831 return Result;
5832}
5833
5834//---------------------------------------------------------------------------------------
5835//
5836// Helper that takes a module, and returns the managed and native PDB information
5837// corresponding to that module. Used by the routine that fires the module load / unload
5838// events.
5839//
5840// Arguments:
5841// * pModule - Module to examine
5842// * pCvInfoIL - [out] CV_INFO_PDB70 corresponding to managed PDB for this module
5843// (the last debug directory entry in the PE File), if it exists. If it doesn't
5844// exist, this is zeroed out.
5845// * pCvInfoNative - [out] CV_INFO_PDB70 corresponding to native NGEN PDB for this
5846// module (the next-to-last debug directory entry in the PE File), if it exists.
5847// If it doesn't exist, this is zeroed out.
5848//
5849// Notes:
5850// * This method only understands the CV_INFO_PDB70 / RSDS format. If the format
5851// changes, this function will act as if there are no debug directory entries.
5852// Module load / unload events will still be fired, but all PDB info will be
5853// zeroed out.
5854// * The raw data in the PE file's debug directory entries are assumed to be
5855// untrusted, and reported sizes of buffers are verified against their data.
5856//
5857
5858static void GetCodeViewInfo(Module * pModule, CV_INFO_PDB70 * pCvInfoIL, CV_INFO_PDB70 * pCvInfoNative)
5859{
5860 LIMITED_METHOD_CONTRACT;
5861
5862 _ASSERTE (pModule != NULL);
5863 _ASSERTE (pCvInfoIL != NULL);
5864 _ASSERTE (pCvInfoNative != NULL);
5865
5866 ZeroMemory(pCvInfoIL, sizeof(*pCvInfoIL));
5867 ZeroMemory(pCvInfoNative, sizeof(*pCvInfoNative));
5868
5869 PTR_PEFile pPEFile = pModule->GetFile();
5870 _ASSERTE(pPEFile != NULL);
5871
5872 PTR_PEImageLayout pLayout = NULL;
5873 if (pPEFile->HasNativeImage())
5874 {
5875 pLayout = pPEFile->GetLoadedNative();
5876 }
5877 else if (pPEFile->HasOpenedILimage())
5878 {
5879 pLayout = pPEFile->GetLoadedIL();
5880 }
5881
5882 if (pLayout == NULL)
5883 {
5884 // This can happen for reflection-loaded modules
5885 return;
5886 }
5887
5888 if (!pLayout->HasNTHeaders())
5889 {
5890 // Without NT headers, we'll have a tough time finding the debug directory
5891 // entries. This can happen for nlp files.
5892 return;
5893 }
5894
5895 if (!pLayout->HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG))
5896 return;
5897
5898 COUNT_T cbDebugEntries;
5899 IMAGE_DEBUG_DIRECTORY * rgDebugEntries =
5900 (IMAGE_DEBUG_DIRECTORY *) pLayout->GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugEntries);
5901
5902 if (cbDebugEntries < sizeof(IMAGE_DEBUG_DIRECTORY))
5903 return;
5904
5905 // Since rgDebugEntries is an array of IMAGE_DEBUG_DIRECTORYs, cbDebugEntries
5906 // should be a multiple of sizeof(IMAGE_DEBUG_DIRECTORY).
5907 if (cbDebugEntries % sizeof(IMAGE_DEBUG_DIRECTORY) != 0)
5908 return;
5909
5910 // Temporary storage for a CV_INFO_PDB70 and its size (which could be less than
5911 // sizeof(CV_INFO_PDB70); see below).
5912 struct PdbInfo
5913 {
5914 CV_INFO_PDB70 * m_pPdb70;
5915 ULONG m_cbPdb70;
5916 };
5917
5918 // Iterate through all debug directory entries. The very last one will be the
5919 // managed PDB entry. The next to last one (if it exists) will be the (native) NGEN
5920 // PDB entry. Treat raw bytes we read as untrusted.
5921 PdbInfo pdbInfoLast = {0};
5922 PdbInfo pdbInfoNextToLast = {0};
5923 int cEntries = cbDebugEntries / sizeof(IMAGE_DEBUG_DIRECTORY);
5924 for (int i = 0; i < cEntries; i++)
5925 {
5926 if (rgDebugEntries[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW)
5927 continue;
5928
5929 // Get raw data pointed to by this IMAGE_DEBUG_DIRECTORY
5930
5931 // Some compilers set PointerToRawData but not AddressOfRawData as they put the
5932 // data at the end of the file in an unmapped part of the file
5933 RVA rvaOfRawData = (rgDebugEntries[i].AddressOfRawData != NULL) ?
5934 rgDebugEntries[i].AddressOfRawData :
5935 pLayout->OffsetToRva(rgDebugEntries[i].PointerToRawData);
5936
5937 ULONG cbDebugData = rgDebugEntries[i].SizeOfData;
5938 if (cbDebugData < (offsetof(CV_INFO_PDB70, magic) + sizeof(((CV_INFO_PDB70*)0)->magic)))
5939 {
5940 // raw data too small to contain magic number at expected spot, so its format
5941 // is not recognizeable. Skip
5942 continue;
5943 }
5944
5945 if (!pLayout->CheckRva(rvaOfRawData, cbDebugData))
5946 {
5947 // Memory claimed to belong to the raw data does not fit.
5948 // IMAGE_DEBUG_DIRECTORY is outright corrupt. Do not include PDB info in
5949 // event at all.
5950 return;
5951 }
5952
5953 // Verify the magic number is as expected
5954 CV_INFO_PDB70 * pPdb70 = (CV_INFO_PDB70 *) pLayout->GetRvaData(rvaOfRawData);
5955 if (pPdb70->magic != CV_SIGNATURE_RSDS)
5956 {
5957 // Unrecognized magic number. Skip
5958 continue;
5959 }
5960
5961 // From this point forward, the format should adhere to the expected layout of
5962 // CV_INFO_PDB70. If we find otherwise, then assume the IMAGE_DEBUG_DIRECTORY is
5963 // outright corrupt, and do not include PDB info in event at all. The caller will
5964 // still fire the module event, but have zeroed-out / empty PDB fields.
5965
5966 // Verify sane size of raw data
5967 if (cbDebugData > sizeof(CV_INFO_PDB70))
5968 return;
5969
5970 // cbDebugData actually can be < sizeof(CV_INFO_PDB70), since the "path" field
5971 // can be truncated to its actual data length (i.e., fewer than MAX_LONGPATH chars
5972 // may be present in the PE file). In some cases, though, cbDebugData will
5973 // include all MAX_LONGPATH chars even though path gets null-terminated well before
5974 // the MAX_LONGPATH limit.
5975
5976 // Gotta have at least one byte of the path
5977 if (cbDebugData < offsetof(CV_INFO_PDB70, path) + sizeof(char))
5978 return;
5979
5980 // How much space is available for the path?
5981 size_t cchPathMaxIncludingNullTerminator = (cbDebugData - offsetof(CV_INFO_PDB70, path)) / sizeof(char);
5982 _ASSERTE(cchPathMaxIncludingNullTerminator >= 1); // Guaranteed above
5983
5984 // Verify path string fits inside the declared size
5985 size_t cchPathActualExcludingNullTerminator = strnlen(pPdb70->path, cchPathMaxIncludingNullTerminator);
5986 if (cchPathActualExcludingNullTerminator == cchPathMaxIncludingNullTerminator)
5987 {
5988 // This is how strnlen indicates failure--it couldn't find the null
5989 // terminator within the buffer size specified
5990 return;
5991 }
5992
5993 // Looks valid. Remember it.
5994 pdbInfoNextToLast = pdbInfoLast;
5995 pdbInfoLast.m_pPdb70 = pPdb70;
5996 pdbInfoLast.m_cbPdb70 = cbDebugData;
5997 }
5998
5999 // Return whatever we found
6000
6001 if (pdbInfoLast.m_pPdb70 != NULL)
6002 {
6003 // The last guy is the IL (managed) PDB info
6004 _ASSERTE(pdbInfoLast.m_cbPdb70 <= sizeof(*pCvInfoIL)); // Guaranteed by checks above
6005 memcpy(pCvInfoIL, pdbInfoLast.m_pPdb70, pdbInfoLast.m_cbPdb70);
6006 }
6007
6008 if (pdbInfoNextToLast.m_pPdb70 != NULL)
6009 {
6010 // The next-to-last guy is the NGEN (native) PDB info
6011 _ASSERTE(pdbInfoNextToLast.m_cbPdb70 <= sizeof(*pCvInfoNative)); // Guaranteed by checks above
6012 memcpy(pCvInfoNative, pdbInfoNextToLast.m_pPdb70, pdbInfoNextToLast.m_cbPdb70);
6013 }
6014}
6015
6016
6017//---------------------------------------------------------------------------------------
6018//
6019// send a module load/unload or rundown event and domainmodule load and rundown event
6020//
6021// Arguments:
6022// * pModule - Module loading or unloading
6023// * dwEventOptions - Bitmask of which events to fire
6024// * bFireDomainModuleEvents - nonzero if we are to fire DomainModule events; zero
6025// if we are to fire Module events
6026//
6027VOID ETW::LoaderLog::SendModuleEvent(Module *pModule, DWORD dwEventOptions, BOOL bFireDomainModuleEvents)
6028{
6029 CONTRACTL {
6030 THROWS;
6031 GC_TRIGGERS;
6032 } CONTRACTL_END;
6033
6034 if(!pModule)
6035 return;
6036
6037 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W("");
6038 BOOL bIsDynamicAssembly = pModule->GetAssembly()->IsDynamic();
6039 BOOL bHasNativeImage = FALSE;
6040#ifdef FEATURE_PREJIT
6041 bHasNativeImage = pModule->HasNativeImage();
6042#endif // FEATURE_PREJIT
6043 BOOL bIsManifestModule = pModule->IsManifest();
6044 ULONGLONG ullAppDomainId = 0; // This is used only with DomainModule events
6045 ULONGLONG ullModuleId = (ULONGLONG)(TADDR) pModule;
6046 ULONGLONG ullAssemblyId = (ULONGLONG)pModule->GetAssembly();
6047 BOOL bIsIbcOptimized = FALSE;
6048 if(bHasNativeImage)
6049 {
6050 bIsIbcOptimized = pModule->IsIbcOptimized();
6051 }
6052 BOOL bIsReadyToRun = pModule->IsReadyToRun();
6053 ULONG ulReservedFlags = 0;
6054 ULONG ulFlags = ((bHasNativeImage ? ETW::LoaderLog::LoaderStructs::NativeModule : 0) |
6055 (bIsDynamicAssembly ? ETW::LoaderLog::LoaderStructs::DynamicModule : 0) |
6056 (bIsManifestModule ? ETW::LoaderLog::LoaderStructs::ManifestModule : 0) |
6057 (bIsIbcOptimized ? ETW::LoaderLog::LoaderStructs::IbcOptimized : 0) |
6058 (bIsReadyToRun ? ETW::LoaderLog::LoaderStructs::ReadyToRunModule : 0));
6059
6060 // Grab PDB path, guid, and age for managed PDB and native (NGEN) PDB when
6061 // available. Any failures are not fatal. The corresponding PDB info will remain
6062 // zeroed out, and that's what we'll include in the event.
6063 CV_INFO_PDB70 cvInfoIL = {0};
6064 CV_INFO_PDB70 cvInfoNative = {0};
6065 GetCodeViewInfo(pModule, &cvInfoIL, &cvInfoNative);
6066
6067 PWCHAR ModuleILPath=(PWCHAR)W(""), ModuleNativePath=(PWCHAR)W("");
6068
6069 if(bFireDomainModuleEvents)
6070 {
6071 if(pModule->GetDomain()->IsSharedDomain()) // for shared domains, we do not fire domainmodule event
6072 return;
6073 ullAppDomainId = (ULONGLONG)pModule->FindDomainAssembly(pModule->GetDomain()->AsAppDomain())->GetAppDomain();
6074 }
6075
6076 LPCWSTR pEmptyString = W("");
6077 SString moduleName = W("");
6078
6079 if(!bIsDynamicAssembly)
6080 {
6081 ModuleILPath = (PWCHAR)pModule->GetAssembly()->GetManifestFile()->GetILimage()->GetPath().GetUnicode();
6082 ModuleNativePath = (PWCHAR)pEmptyString;
6083
6084#ifdef FEATURE_PREJIT
6085 if(bHasNativeImage)
6086 ModuleNativePath = (PWCHAR)pModule->GetNativeImage()->GetPath().GetUnicode();
6087#endif // FEATURE_PREJIT
6088 }
6089
6090 // if we do not have a module path yet, we put the module name
6091 if(bIsDynamicAssembly || ModuleILPath==NULL || wcslen(ModuleILPath) <= 2)
6092 {
6093 moduleName.SetUTF8(pModule->GetSimpleName());
6094 ModuleILPath = (PWCHAR)moduleName.GetUnicode();
6095 ModuleNativePath = (PWCHAR)pEmptyString;
6096 }
6097
6098 /* prepare events args for ETW and ETM */
6099 szDtraceOutput1 = (PCWSTR)ModuleILPath;
6100 szDtraceOutput2 = (PCWSTR)ModuleNativePath;
6101
6102 // Convert PDB paths to UNICODE
6103 StackSString managedPdbPath(SString::Utf8, cvInfoIL.path);
6104 StackSString nativePdbPath(SString::Utf8, cvInfoNative.path);
6105
6106 if(bFireDomainModuleEvents)
6107 {
6108 if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad)
6109 {
6110 FireEtwDomainModuleLoad_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6111 }
6112 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
6113 {
6114 FireEtwDomainModuleDCStart_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6115 }
6116 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd)
6117 {
6118 FireEtwDomainModuleDCEnd_V1(ullModuleId, ullAssemblyId, ullAppDomainId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId());
6119 }
6120 else
6121 {
6122 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
6123 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
6124 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd));
6125 }
6126 }
6127 else
6128 {
6129 if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoad))
6130 {
6131 FireEtwModuleLoad_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6132 }
6133 else if(dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
6134 {
6135 FireEtwModuleUnload_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6136 }
6137 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart))
6138 {
6139 FireEtwModuleDCStart_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6140 }
6141 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) || (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd))
6142 {
6143 FireEtwModuleDCEnd_V1_or_V2(ullModuleId, ullAssemblyId, ulFlags, ulReservedFlags, szDtraceOutput1, szDtraceOutput2, GetClrInstanceId(), &cvInfoIL.signature, cvInfoIL.age, managedPdbPath, &cvInfoNative.signature, cvInfoNative.age, nativePdbPath);
6144 }
6145 else
6146 {
6147 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleLoad) ||
6148 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
6149 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
6150 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
6151 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny));
6152
6153 }
6154
6155 if (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeEnabledAny)
6156 {
6157 // Fire ModuleRangeLoad, ModuleRangeDCStart, ModuleRangeDCEnd or ModuleRangeLoadPrivate event for this Module
6158 SendModuleRange(pModule, dwEventOptions);
6159 }
6160 }
6161}
6162
6163/*****************************************************************/
6164/* This routine is used to send an ETW event just before a method starts jitting*/
6165/*****************************************************************/
6166VOID ETW::MethodLog::SendMethodJitStartEvent(MethodDesc *pMethodDesc, SString *namespaceOrClassName, SString *methodName, SString *methodSignature)
6167{
6168 CONTRACTL {
6169 THROWS;
6170 GC_TRIGGERS;
6171 } CONTRACTL_END;
6172
6173 Module *pModule = NULL;
6174 Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID
6175
6176 ULONGLONG ullMethodIdentifier=0;
6177 ULONGLONG ullModuleID=0;
6178 ULONG ulMethodToken=0;
6179 ULONG ulMethodILSize=0;
6180 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""),szDtraceOutput3=W("");
6181
6182 if(pMethodDesc) {
6183 pModule = pMethodDesc->GetModule_NoLogging();
6184
6185 if(!pMethodDesc->IsRestored()) {
6186 return;
6187 }
6188
6189 bool bIsDynamicMethod = pMethodDesc->IsDynamicMethod();
6190 BOOL bIsGenericMethod = FALSE;
6191 if(pMethodDesc->GetMethodTable_NoLogging())
6192 bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging();
6193
6194 ullModuleID = (ULONGLONG)(TADDR) pModule;
6195 ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6196
6197 // Use MethodDesc if Dynamic or Generic methods
6198 if( bIsDynamicMethod || bIsGenericMethod)
6199 {
6200 if(bIsGenericMethod)
6201 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6202 if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0
6203 ulMethodToken = (ULONG)0;
6204 }
6205 else
6206 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6207
6208 if(pMethodDesc->IsIL())
6209 {
6210 COR_ILMETHOD_DECODER::DecoderStatus decoderstatus = COR_ILMETHOD_DECODER::FORMAT_ERROR;
6211 COR_ILMETHOD_DECODER ILHeader(pMethodDesc->GetILHeader(), pMethodDesc->GetMDImport(), &decoderstatus);
6212 ulMethodILSize = (ULONG)ILHeader.GetCodeSize();
6213 }
6214
6215 SString tNamespace, tMethodName, tMethodSignature;
6216 if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty()))
6217 {
6218 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
6219 namespaceOrClassName = &tNamespace;
6220 methodName = &tMethodName;
6221 methodSignature = &tMethodSignature;
6222 }
6223
6224 // fire method information
6225 /* prepare events args for ETW and ETM */
6226 szDtraceOutput1 = (PCWSTR)namespaceOrClassName->GetUnicode();
6227 szDtraceOutput2 = (PCWSTR)methodName->GetUnicode();
6228 szDtraceOutput3 = (PCWSTR)methodSignature->GetUnicode();
6229
6230 FireEtwMethodJittingStarted_V1(ullMethodIdentifier,
6231 ullModuleID,
6232 ulMethodToken,
6233 ulMethodILSize,
6234 szDtraceOutput1,
6235 szDtraceOutput2,
6236 szDtraceOutput3,
6237 GetClrInstanceId());
6238 }
6239}
6240
6241/****************************************************************************/
6242/* This routine is used to send a method load/unload or rundown event */
6243/****************************************************************************/
6244VOID ETW::MethodLog::SendMethodEvent(MethodDesc *pMethodDesc, DWORD dwEventOptions, BOOL bIsJit, SString *namespaceOrClassName, SString *methodName, SString *methodSignature, SIZE_T pCode, ReJITID rejitID, BOOL bProfilerRejectedPrecompiledCode, BOOL bReadyToRunRejectedPrecompiledCode)
6245{
6246 CONTRACTL {
6247 THROWS;
6248 GC_NOTRIGGER;
6249 SO_NOT_MAINLINE;
6250 } CONTRACTL_END;
6251
6252 Module *pModule = NULL;
6253 Module *pLoaderModule = NULL; // This must not be used except for getting the ModuleID
6254 ULONGLONG ullMethodStartAddress=0, ullColdMethodStartAddress=0, ullModuleID=0, ullMethodIdentifier=0;
6255 ULONG ulMethodSize=0, ulColdMethodSize=0, ulMethodToken=0, ulMethodFlags=0, ulColdMethodFlags=0;
6256 PWCHAR pMethodName=NULL, pNamespaceName=NULL, pMethodSignature=NULL;
6257 BOOL bHasNativeImage = FALSE, bShowVerboseOutput = FALSE, bIsDynamicMethod = FALSE, bHasSharedGenericCode = FALSE, bIsGenericMethod = FALSE;
6258 PCWSTR szDtraceOutput1=W(""),szDtraceOutput2=W(""),szDtraceOutput3=W("");
6259
6260 BOOL bIsRundownProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6261 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6262 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
6263 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd));
6264
6265 BOOL bIsRuntimeProvider = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6266 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6267 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
6268 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload));
6269
6270 if (pMethodDesc == NULL)
6271 return;
6272
6273 if(!pMethodDesc->IsRestored())
6274 {
6275 // Forcibly restoring ngen methods can cause all sorts of deadlocks and contract violations
6276 // These events are therefore put under the private provider
6277 if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
6278 TRACE_LEVEL_INFORMATION,
6279 CLR_PRIVATENGENFORCERESTORE_KEYWORD))
6280 {
6281 PERMANENT_CONTRACT_VIOLATION(GCViolation, ReasonNonShippingCode);
6282 pMethodDesc->CheckRestore();
6283 }
6284 else
6285 {
6286 return;
6287 }
6288 }
6289
6290
6291 if(bIsRundownProvider)
6292 {
6293 bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_RUNDOWN_PROVIDER_Context,
6294 TRACE_LEVEL_VERBOSE,
6295 KEYWORDZERO);
6296 }
6297 else if(bIsRuntimeProvider)
6298 {
6299 bShowVerboseOutput = ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
6300 TRACE_LEVEL_VERBOSE,
6301 KEYWORDZERO);
6302 }
6303
6304 pModule = pMethodDesc->GetModule_NoLogging();
6305#ifdef FEATURE_PREJIT
6306 bHasNativeImage = pModule->HasNativeImage();
6307#endif // FEATURE_PREJIT
6308 bIsDynamicMethod = (BOOL)pMethodDesc->IsDynamicMethod();
6309 bHasSharedGenericCode = pMethodDesc->IsSharedByGenericInstantiations();
6310
6311 if(pMethodDesc->GetMethodTable_NoLogging())
6312 bIsGenericMethod = pMethodDesc->HasClassOrMethodInstantiation_NoLogging();
6313
6314 ulMethodFlags = ((ulMethodFlags |
6315 (bHasSharedGenericCode ? ETW::MethodLog::MethodStructs::SharedGenericCode : 0) |
6316 (bIsGenericMethod ? ETW::MethodLog::MethodStructs::GenericMethod : 0) |
6317 (bIsDynamicMethod ? ETW::MethodLog::MethodStructs::DynamicMethod : 0) |
6318 (bIsJit ? ETW::MethodLog::MethodStructs::JittedMethod : 0) |
6319 (bProfilerRejectedPrecompiledCode ? ETW::MethodLog::MethodStructs::ProfilerRejectedPrecompiledCode : 0) |
6320 (bReadyToRunRejectedPrecompiledCode ? ETW::MethodLog::MethodStructs::ReadyToRunRejectedPrecompiledCode : 0)));
6321
6322 // Intentionally set the extent flags (cold vs. hot) only after all the other common
6323 // flags (above) have been set.
6324 ulColdMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::ColdSection; // Method Extent (bits 28, 29, 30, 31)
6325 ulMethodFlags = ulMethodFlags | ETW::MethodLog::MethodStructs::HotSection; // Method Extent (bits 28, 29, 30, 31)
6326
6327 // MethodDesc ==> Code Address ==>JitMananger
6328 TADDR start = pCode ? pCode : PCODEToPINSTR(pMethodDesc->GetNativeCode());
6329 if(start == 0) {
6330 // this method hasn't been jitted
6331 return;
6332 }
6333
6334 // EECodeInfo is technically initialized by a "PCODE", but it can also be initialized
6335 // by a TADDR (i.e., w/out thumb bit set on ARM)
6336 EECodeInfo codeInfo(start);
6337
6338 // MethodToken ==> MethodRegionInfo
6339 IJitManager::MethodRegionInfo methodRegionInfo;
6340 codeInfo.GetMethodRegionInfo(&methodRegionInfo);
6341
6342 ullMethodStartAddress = (ULONGLONG)methodRegionInfo.hotStartAddress;
6343 ulMethodSize = (ULONG)methodRegionInfo.hotSize;
6344
6345 ullModuleID = (ULONGLONG)(TADDR) pModule;
6346 ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6347
6348 // Use MethodDesc if Dynamic or Generic methods
6349 if( bIsDynamicMethod || bIsGenericMethod)
6350 {
6351 bShowVerboseOutput = TRUE;
6352 if(bIsGenericMethod)
6353 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6354 if(bIsDynamicMethod) // if its a generic and a dynamic method, we would set the methodtoken to 0
6355 ulMethodToken = (ULONG)0;
6356 }
6357 else
6358 ulMethodToken = (ULONG)pMethodDesc->GetMemberDef_NoLogging();
6359
6360 if(bHasNativeImage)
6361 {
6362 ullColdMethodStartAddress = (ULONGLONG)methodRegionInfo.coldStartAddress;
6363 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
6364 }
6365
6366 SString tNamespace, tMethodName, tMethodSignature;
6367
6368 // if verbose method load info needed, only then
6369 // find method name and signature and fire verbose method load info
6370 if(bShowVerboseOutput)
6371 {
6372 if(!namespaceOrClassName|| !methodName|| !methodSignature || (methodName->IsEmpty() && namespaceOrClassName->IsEmpty() && methodSignature->IsEmpty()))
6373 {
6374 pMethodDesc->GetMethodInfo(tNamespace, tMethodName, tMethodSignature);
6375 namespaceOrClassName = &tNamespace;
6376 methodName = &tMethodName;
6377 methodSignature = &tMethodSignature;
6378 }
6379 pNamespaceName = (PWCHAR)namespaceOrClassName->GetUnicode();
6380 pMethodName = (PWCHAR)methodName->GetUnicode();
6381 pMethodSignature = (PWCHAR)methodSignature->GetUnicode();
6382 }
6383
6384 BOOL bFireEventForColdSection = (bHasNativeImage && ullColdMethodStartAddress && ulColdMethodSize);
6385
6386 /* prepare events args for ETW and ETM */
6387 szDtraceOutput1 = (PCWSTR)pNamespaceName;
6388 szDtraceOutput2 = (PCWSTR)pMethodName;
6389 szDtraceOutput3 = (PCWSTR)pMethodSignature;
6390
6391 if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6392 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad))
6393 {
6394 if(bShowVerboseOutput)
6395 {
6396 FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier,
6397 ullModuleID,
6398 ullMethodStartAddress,
6399 ulMethodSize,
6400 ulMethodToken,
6401 ulMethodFlags,
6402 szDtraceOutput1,
6403 szDtraceOutput2,
6404 szDtraceOutput3,
6405 GetClrInstanceId(),
6406 rejitID);
6407 }
6408 else
6409 {
6410 FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier,
6411 ullModuleID,
6412 ullMethodStartAddress,
6413 ulMethodSize,
6414 ulMethodToken,
6415 ulMethodFlags,
6416 GetClrInstanceId(),
6417 rejitID);
6418 }
6419 if(bFireEventForColdSection)
6420 {
6421 if(bShowVerboseOutput)
6422 {
6423 FireEtwMethodLoadVerbose_V1_or_V2(ullMethodIdentifier,
6424 ullModuleID,
6425 ullColdMethodStartAddress,
6426 ulColdMethodSize,
6427 ulMethodToken,
6428 ulColdMethodFlags,
6429 szDtraceOutput1,
6430 szDtraceOutput2,
6431 szDtraceOutput3,
6432 GetClrInstanceId(),
6433 rejitID);
6434 }
6435 else
6436 {
6437 FireEtwMethodLoad_V1_or_V2(ullMethodIdentifier,
6438 ullModuleID,
6439 ullColdMethodStartAddress,
6440 ulColdMethodSize,
6441 ulMethodToken,
6442 ulColdMethodFlags,
6443 GetClrInstanceId(),
6444 rejitID);
6445 }
6446 }
6447 }
6448 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6449 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload))
6450 {
6451 if(bShowVerboseOutput)
6452 {
6453 FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier,
6454 ullModuleID,
6455 ullMethodStartAddress,
6456 ulMethodSize,
6457 ulMethodToken,
6458 ulMethodFlags,
6459 szDtraceOutput1,
6460 szDtraceOutput2,
6461 szDtraceOutput3,
6462 GetClrInstanceId(),
6463 rejitID);
6464 }
6465 else
6466 {
6467 FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier,
6468 ullModuleID,
6469 ullMethodStartAddress,
6470 ulMethodSize,
6471 ulMethodToken,
6472 ulMethodFlags,
6473 GetClrInstanceId(),
6474 rejitID);
6475 }
6476 if(bFireEventForColdSection)
6477 {
6478 if(bShowVerboseOutput)
6479 {
6480 FireEtwMethodUnloadVerbose_V1_or_V2(ullMethodIdentifier,
6481 ullModuleID,
6482 ullColdMethodStartAddress,
6483 ulColdMethodSize,
6484 ulMethodToken,
6485 ulColdMethodFlags,
6486 szDtraceOutput1,
6487 szDtraceOutput2,
6488 szDtraceOutput3,
6489 GetClrInstanceId(),
6490 rejitID);
6491 }
6492 else
6493 {
6494 FireEtwMethodUnload_V1_or_V2(ullMethodIdentifier,
6495 ullModuleID,
6496 ullColdMethodStartAddress,
6497 ulColdMethodSize,
6498 ulMethodToken,
6499 ulColdMethodFlags,
6500 GetClrInstanceId(),
6501 rejitID);
6502 }
6503 }
6504 }
6505 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6506 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart))
6507 {
6508 if(bShowVerboseOutput)
6509 {
6510 FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier,
6511 ullModuleID,
6512 ullMethodStartAddress,
6513 ulMethodSize,
6514 ulMethodToken,
6515 ulMethodFlags,
6516 szDtraceOutput1,
6517 szDtraceOutput2,
6518 szDtraceOutput3,
6519 GetClrInstanceId(),
6520 rejitID);
6521 }
6522 else
6523 {
6524 FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier,
6525 ullModuleID,
6526 ullMethodStartAddress,
6527 ulMethodSize,
6528 ulMethodToken,
6529 ulMethodFlags,
6530 GetClrInstanceId(),
6531 rejitID);
6532 }
6533 if(bFireEventForColdSection)
6534 {
6535 if(bShowVerboseOutput)
6536 {
6537 FireEtwMethodDCStartVerbose_V1_or_V2(ullMethodIdentifier,
6538 ullModuleID,
6539 ullColdMethodStartAddress,
6540 ulColdMethodSize,
6541 ulMethodToken,
6542 ulColdMethodFlags,
6543 szDtraceOutput1,
6544 szDtraceOutput2,
6545 szDtraceOutput3,
6546 GetClrInstanceId(),
6547 rejitID);
6548 }
6549 else
6550 {
6551 FireEtwMethodDCStart_V1_or_V2(ullMethodIdentifier,
6552 ullModuleID,
6553 ullColdMethodStartAddress,
6554 ulColdMethodSize,
6555 ulMethodToken,
6556 ulColdMethodFlags,
6557 GetClrInstanceId(),
6558 rejitID);
6559 }
6560 }
6561 }
6562 else if((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6563 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd))
6564 {
6565 if(bShowVerboseOutput)
6566 {
6567 FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier,
6568 ullModuleID,
6569 ullMethodStartAddress,
6570 ulMethodSize,
6571 ulMethodToken,
6572 ulMethodFlags,
6573 szDtraceOutput1,
6574 szDtraceOutput2,
6575 szDtraceOutput3,
6576 GetClrInstanceId(),
6577 rejitID);
6578 }
6579 else
6580 {
6581 FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier,
6582 ullModuleID,
6583 ullMethodStartAddress,
6584 ulMethodSize,
6585 ulMethodToken,
6586 ulMethodFlags,
6587 GetClrInstanceId(),
6588 rejitID);
6589 }
6590 if(bFireEventForColdSection)
6591 {
6592 if(bShowVerboseOutput)
6593 {
6594 FireEtwMethodDCEndVerbose_V1_or_V2(ullMethodIdentifier,
6595 ullModuleID,
6596 ullColdMethodStartAddress,
6597 ulColdMethodSize,
6598 ulMethodToken,
6599 ulColdMethodFlags,
6600 szDtraceOutput1,
6601 szDtraceOutput2,
6602 szDtraceOutput3,
6603 GetClrInstanceId(),
6604 rejitID);
6605 }
6606 else
6607 {
6608 FireEtwMethodDCEnd_V1_or_V2(ullMethodIdentifier,
6609 ullModuleID,
6610 ullColdMethodStartAddress,
6611 ulColdMethodSize,
6612 ulMethodToken,
6613 ulColdMethodFlags,
6614 GetClrInstanceId(),
6615 rejitID);
6616 }
6617 }
6618 }
6619 else
6620 {
6621 _ASSERTE((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad) ||
6622 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload) ||
6623 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart) ||
6624 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd) ||
6625 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
6626 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) ||
6627 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
6628 (dwEventOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd));
6629 }
6630}
6631
6632//---------------------------------------------------------------------------------------
6633//
6634// Fires the IL-to-native map event for JITted methods. This is used for the runtime,
6635// rundown start, and rundown end events that include the il-to-native map information
6636//
6637// Arguments:
6638// pMethodDesc - MethodDesc for which we'll fire the map event
6639// dwEventOptions - Options that tells us, in the rundown case, whether we're
6640// supposed to fire the start or end rundown events.
6641//
6642
6643// static
6644VOID ETW::MethodLog::SendMethodILToNativeMapEvent(MethodDesc * pMethodDesc, DWORD dwEventOptions, SIZE_T pCode, ReJITID rejitID)
6645{
6646 CONTRACTL
6647 {
6648 THROWS;
6649 GC_NOTRIGGER;
6650 SO_NOT_MAINLINE;
6651 }
6652 CONTRACTL_END;
6653
6654 // This is the limit on how big the il-to-native map can get, as measured by number
6655 // of entries in each parallel array (IL offset array and native offset array).
6656 // This number was chosen to ensure the overall event stays under the Windows limit
6657 // of 64K
6658 const USHORT kMapEntriesMax = 7000;
6659
6660 if (pMethodDesc == NULL)
6661 return;
6662
6663 if (pMethodDesc->HasClassOrMethodInstantiation() && pMethodDesc->IsTypicalMethodDefinition())
6664 return;
6665
6666 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
6667 // or profiler is loaded. So it should always be available.
6668 _ASSERTE(g_pDebugInterface != NULL);
6669
6670 ULONGLONG ullMethodIdentifier = (ULONGLONG)pMethodDesc;
6671
6672 USHORT cMap;
6673 NewArrayHolder<UINT> rguiILOffset;
6674 NewArrayHolder<UINT> rguiNativeOffset;
6675
6676 HRESULT hr = g_pDebugInterface->GetILToNativeMappingIntoArrays(
6677 pMethodDesc,
6678 pCode,
6679 kMapEntriesMax,
6680 &cMap,
6681 &rguiILOffset,
6682 &rguiNativeOffset);
6683 if (FAILED(hr))
6684 return;
6685
6686 // Runtime provider.
6687 //
6688 // This macro already checks for the JittedMethodILToNativeMapKeyword before
6689 // choosing to fire the event
6690 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodILToNativeMap) != 0)
6691 {
6692 FireEtwMethodILToNativeMap(
6693 ullMethodIdentifier,
6694 rejitID,
6695 0, // Extent: This event is only sent for JITted (not NGENd) methods, and
6696 // currently there is only one extent (hot) for JITted methods.
6697 cMap,
6698 rguiILOffset,
6699 rguiNativeOffset,
6700 GetClrInstanceId());
6701 }
6702
6703 // Rundown provider
6704 //
6705 // These macros already check for the JittedMethodILToNativeMapRundownKeyword
6706 // before choosing to fire the event--we further check our options to see if we
6707 // should fire the Start and / or End flavor of the event (since the keyword alone
6708 // is insufficient to distinguish these).
6709 //
6710 // (for an explanation of the parameters see the FireEtwMethodILToNativeMap call above)
6711 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap) != 0)
6712 FireEtwMethodDCStartILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId());
6713 if ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap) != 0)
6714 FireEtwMethodDCEndILToNativeMap(ullMethodIdentifier, 0, 0, cMap, rguiILOffset, rguiNativeOffset, GetClrInstanceId());
6715}
6716
6717
6718VOID ETW::MethodLog::SendHelperEvent(ULONGLONG ullHelperStartAddress, ULONG ulHelperSize, LPCWSTR pHelperName)
6719{
6720 WRAPPER_NO_CONTRACT;
6721 if(pHelperName)
6722 {
6723 PCWSTR szDtraceOutput1=W("");
6724 ULONG methodFlags = ETW::MethodLog::MethodStructs::JitHelperMethod; // helper flag set
6725 FireEtwMethodLoadVerbose_V1(ullHelperStartAddress,
6726 0,
6727 ullHelperStartAddress,
6728 ulHelperSize,
6729 0,
6730 methodFlags,
6731 NULL,
6732 pHelperName,
6733 NULL,
6734 GetClrInstanceId());
6735 }
6736}
6737
6738
6739/****************************************************************************/
6740/* This routine sends back method events of type 'dwEventOptions', for all
6741 NGEN methods in pModule */
6742/****************************************************************************/
6743VOID ETW::MethodLog::SendEventsForNgenMethods(Module *pModule, DWORD dwEventOptions)
6744{
6745 CONTRACTL {
6746 THROWS;
6747 GC_TRIGGERS;
6748 } CONTRACTL_END;
6749
6750#ifdef FEATURE_PREJIT
6751 if (!pModule)
6752 return;
6753
6754#ifdef FEATURE_READYTORUN
6755 if (pModule->IsReadyToRun())
6756 {
6757 ReadyToRunInfo::MethodIterator mi(pModule->GetReadyToRunInfo());
6758 while (mi.Next())
6759 {
6760 // Call GetMethodDesc_NoRestore instead of GetMethodDesc to avoid restoring methods at shutdown.
6761 MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc_NoRestore();
6762 if (hotDesc != NULL)
6763 {
6764 ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
6765 }
6766 }
6767
6768 return;
6769 }
6770#endif // FEATURE_READYTORUN
6771 if (pModule->HasNativeImage())
6772 {
6773 MethodIterator mi(pModule);
6774
6775 while (mi.Next())
6776 {
6777 MethodDesc *hotDesc = (MethodDesc *)mi.GetMethodDesc();
6778 ETW::MethodLog::SendMethodEvent(hotDesc, dwEventOptions, FALSE);
6779 }
6780 }
6781#endif // FEATURE_PREJIT
6782}
6783
6784// Called be ETW::MethodLog::SendEventsForJitMethods
6785// Sends the ETW events once our caller determines whether or not rejit locks can be acquired
6786VOID ETW::MethodLog::SendEventsForJitMethodsHelper(BaseDomain *pDomainFilter,
6787 LoaderAllocator *pLoaderAllocatorFilter,
6788 DWORD dwEventOptions,
6789 BOOL fLoadOrDCStart,
6790 BOOL fUnloadOrDCEnd,
6791 BOOL fSendMethodEvent,
6792 BOOL fSendILToNativeMapEvent,
6793 BOOL fGetReJitIDs)
6794{
6795 CONTRACTL{
6796 THROWS;
6797 GC_NOTRIGGER;
6798 } CONTRACTL_END;
6799
6800 EEJitManager::CodeHeapIterator heapIterator(pLoaderAllocatorFilter);
6801 while (heapIterator.Next())
6802 {
6803 MethodDesc * pMD = heapIterator.GetMethod();
6804 if (pMD == NULL)
6805 continue;
6806
6807 TADDR codeStart = heapIterator.GetMethodCode();
6808
6809 // Grab rejitID from the rejit manager. In some cases, such as collectible loader
6810 // allocators, we don't support rejit so we need to short circuit the call.
6811 // This also allows our caller to avoid having to pre-enter the rejit
6812 // manager locks.
6813 // see code:#TableLockHolder
6814 ReJITID rejitID =
6815 fGetReJitIDs ? ReJitManager::GetReJitIdNoLock(pMD, codeStart) : 0;
6816
6817 // There are small windows of time where the heap iterator may come across a
6818 // codeStart that is not yet published to the MethodDesc. This may happen if
6819 // we're JITting the method right now on another thread, and have not completed
6820 // yet. Detect the race, and skip the method if appropriate. (If rejitID is
6821 // nonzero, there is no race, as GetReJitIdNoLock will not return a nonzero
6822 // rejitID if the codeStart has not yet been published for that rejitted version
6823 // of the method.) This check also catches recompilations due to EnC, which we do
6824 // not want to issue events for, in order to ensure xperf's assumption that
6825 // MethodDesc* + ReJITID + extent (hot vs. cold) form a unique key for code
6826 // ranges of methods
6827 if ((rejitID == 0) && (codeStart != PCODEToPINSTR(pMD->GetNativeCode())))
6828 continue;
6829
6830 // When we're called to announce loads, then the methodload event itself must
6831 // precede any supplemental events, so that the method load or method jitting
6832 // event is the first event the profiler sees for that MethodID (and not, say,
6833 // the MethodILToNativeMap event.)
6834 if (fLoadOrDCStart)
6835 {
6836 if (fSendMethodEvent)
6837 {
6838 ETW::MethodLog::SendMethodEvent(
6839 pMD,
6840 dwEventOptions,
6841 TRUE, // bIsJit
6842 NULL, // namespaceOrClassName
6843 NULL, // methodName
6844 NULL, // methodSignature
6845 codeStart,
6846 rejitID);
6847 }
6848 }
6849
6850 // Send any supplemental events requested for this MethodID
6851 if (fSendILToNativeMapEvent)
6852 ETW::MethodLog::SendMethodILToNativeMapEvent(pMD, dwEventOptions, codeStart, rejitID);
6853
6854 // When we're called to announce unloads, then the methodunload event itself must
6855 // come after any supplemental events, so that the method unload event is the
6856 // last event the profiler sees for this MethodID
6857 if (fUnloadOrDCEnd)
6858 {
6859 if (fSendMethodEvent)
6860 {
6861 ETW::MethodLog::SendMethodEvent(
6862 pMD,
6863 dwEventOptions,
6864 TRUE, // bIsJit
6865 NULL, // namespaceOrClassName
6866 NULL, // methodName
6867 NULL, // methodSignature
6868 codeStart,
6869 rejitID);
6870 }
6871 }
6872 }
6873}
6874
6875/****************************************************************************/
6876/* This routine sends back method events of type 'dwEventOptions', for all
6877 JITed methods in either a given LoaderAllocator (if pLoaderAllocatorFilter is non NULL)
6878 or in a given Domain (if pDomainFilter is non NULL) or for
6879 all methods (if both filters are null) */
6880/****************************************************************************/
6881// Code review indicates this method is never called with both filters NULL. Ideally we would
6882// assert this and change the comment above, but given I am making a change late in the release I am being cautious
6883VOID ETW::MethodLog::SendEventsForJitMethods(BaseDomain *pDomainFilter, LoaderAllocator *pLoaderAllocatorFilter, DWORD dwEventOptions)
6884{
6885 CONTRACTL {
6886 NOTHROW;
6887 GC_TRIGGERS;
6888 } CONTRACTL_END;
6889
6890#if !defined(DACCESS_COMPILE)
6891 EX_TRY
6892 {
6893 // This is only called for JITted methods loading xor unloading
6894 BOOL fLoadOrDCStart = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny) != 0);
6895 BOOL fUnloadOrDCEnd = ((dwEventOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny) != 0);
6896 _ASSERTE((fLoadOrDCStart || fUnloadOrDCEnd) && !(fLoadOrDCStart && fUnloadOrDCEnd));
6897
6898 BOOL fSendMethodEvent =
6899 (dwEventOptions &
6900 (ETW::EnumerationLog::EnumerationStructs::JitMethodLoad |
6901 ETW::EnumerationLog::EnumerationStructs::JitMethodDCStart |
6902 ETW::EnumerationLog::EnumerationStructs::JitMethodUnload |
6903 ETW::EnumerationLog::EnumerationStructs::JitMethodDCEnd)) != 0;
6904
6905 BOOL fSendILToNativeMapEvent =
6906 (dwEventOptions &
6907 (ETW::EnumerationLog::EnumerationStructs::MethodDCStartILToNativeMap |
6908 ETW::EnumerationLog::EnumerationStructs::MethodDCEndILToNativeMap)) != 0;
6909
6910 if (fSendILToNativeMapEvent)
6911 {
6912 // The call to SendMethodILToNativeMapEvent assumes that the debugger's lazy
6913 // data has already been initialized, to ensure we don't try to do the lazy init
6914 // while under the implicit, notrigger CodeHeapIterator lock below.
6915
6916 // g_pDebugInterface is initialized on startup on desktop CLR, regardless of whether a debugger
6917 // or profiler is loaded. So it should always be available.
6918 _ASSERTE(g_pDebugInterface != NULL);
6919 g_pDebugInterface->InitializeLazyDataIfNecessary();
6920 }
6921
6922 // #TableLockHolder:
6923 //
6924 // A word about ReJitManager::TableLockHolder... As we enumerate through the functions,
6925 // we may need to grab their ReJITIDs. The ReJitManager grabs its table Crst in order to
6926 // fetch these. However, several other kinds of locks are being taken during this
6927 // enumeration, such as the SystemDomain lock and the EEJitManager::CodeHeapIterator's
6928 // lock. In order to avoid lock-leveling issues, we grab the appropriate ReJitManager
6929 // table locks after SystemDomain and before CodeHeapIterator. In particular, we need to
6930 // grab the SharedDomain's ReJitManager table lock as well as the specific AppDomain's
6931 // ReJitManager table lock for the current AppDomain we're iterating. Why the SharedDomain's
6932 // ReJitManager lock? For any given AppDomain we're iterating over, the MethodDescs we
6933 // find may be managed by that AppDomain's ReJitManger OR the SharedDomain's ReJitManager.
6934 // (This is due to generics and whether given instantiations may be shared based on their
6935 // arguments.) Therefore, we proactively take the SharedDomain's ReJitManager's table
6936 // lock up front, and then individually take the appropriate AppDomain's ReJitManager's
6937 // table lock that corresponds to the domain or module we're currently iterating over.
6938 //
6939
6940 // We only support getting rejit IDs when filtering by domain.
6941 if (pDomainFilter)
6942 {
6943 CodeVersionManager::TableLockHolder lkRejitMgrModule(pDomainFilter->GetCodeVersionManager());
6944 SendEventsForJitMethodsHelper(pDomainFilter,
6945 pLoaderAllocatorFilter,
6946 dwEventOptions,
6947 fLoadOrDCStart,
6948 fUnloadOrDCEnd,
6949 fSendMethodEvent,
6950 fSendILToNativeMapEvent,
6951 TRUE);
6952 }
6953 else
6954 {
6955 SendEventsForJitMethodsHelper(pDomainFilter,
6956 pLoaderAllocatorFilter,
6957 dwEventOptions,
6958 fLoadOrDCStart,
6959 fUnloadOrDCEnd,
6960 fSendMethodEvent,
6961 fSendILToNativeMapEvent,
6962 FALSE);
6963 }
6964 } EX_CATCH{} EX_END_CATCH(SwallowAllExceptions);
6965#endif // !DACCESS_COMPILE
6966}
6967
6968//---------------------------------------------------------------------------------------
6969//
6970// Wrapper around IterateDomain, which locks the AppDomain to be <
6971// STAGE_FINALIZED until the iteration is complete.
6972//
6973// Arguments:
6974// pAppDomain - AppDomain to iterate
6975// enumerationOptions - Flags indicating what to enumerate. Just passed
6976// straight through to IterateDomain
6977//
6978VOID ETW::EnumerationLog::IterateAppDomain(AppDomain * pAppDomain, DWORD enumerationOptions)
6979{
6980 CONTRACTL
6981 {
6982 THROWS;
6983 GC_TRIGGERS;
6984 PRECONDITION(pAppDomain != NULL);
6985 }
6986 CONTRACTL_END;
6987
6988 // Hold the system domain lock during the entire iteration, so we can
6989 // ensure the App Domain does not get finalized until we're all done
6990 SystemDomain::LockHolder lh;
6991
6992 // Now it's safe to do the iteration
6993 IterateDomain(pAppDomain, enumerationOptions);
6994}
6995
6996/********************************************************************************/
6997/* This routine fires ETW events for
6998 Domain,
6999 Assemblies in them,
7000 DomainModule's in them,
7001 Modules in them,
7002 JIT methods in them,
7003 and the NGEN methods in them
7004 based on enumerationOptions.*/
7005/********************************************************************************/
7006VOID ETW::EnumerationLog::IterateDomain(BaseDomain *pDomain, DWORD enumerationOptions)
7007{
7008 CONTRACTL {
7009 THROWS;
7010 GC_TRIGGERS;
7011 PRECONDITION(pDomain != NULL);
7012 } CONTRACTL_END;
7013
7014#if defined(_DEBUG) && !defined(DACCESS_COMPILE)
7015 // Do not call IterateDomain() directly with an AppDomain. Use
7016 // IterateAppDomain(), whch wraps this function with a hold on the
7017 // SystemDomain lock, which ensures pDomain's type data doesn't disappear
7018 // on us.
7019 if (pDomain->IsAppDomain())
7020 {
7021 _ASSERTE(SystemDomain::IsUnderDomainLock());
7022 }
7023#endif // defined(_DEBUG) && !defined(DACCESS_COMPILE)
7024
7025 EX_TRY
7026 {
7027 // DC Start events for Domain
7028 if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7029 {
7030 ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions);
7031 }
7032
7033 // DC End or Unload Jit Method events
7034 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny)
7035 {
7036 ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions);
7037 }
7038
7039 AppDomain::AssemblyIterator assemblyIterator = pDomain->AsAppDomain()->IterateAssembliesEx(
7040 (AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution));
7041 CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
7042 while (assemblyIterator.Next(pDomainAssembly.This()))
7043 {
7044 CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
7045 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7046 {
7047 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7048 }
7049
7050 DomainModuleIterator domainModuleIterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
7051 while (domainModuleIterator.Next())
7052 {
7053 Module * pModule = domainModuleIterator.GetModule();
7054 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7055 }
7056
7057 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7058 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7059 {
7060 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7061 }
7062 }
7063
7064 // DC Start or Load Jit Method events
7065 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny)
7066 {
7067 ETW::MethodLog::SendEventsForJitMethods(pDomain, NULL, enumerationOptions);
7068 }
7069
7070 // DC End or Unload events for Domain
7071 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7072 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7073 {
7074 ETW::LoaderLog::SendDomainEvent(pDomain, enumerationOptions);
7075 }
7076 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7077}
7078
7079
7080/********************************************************************************/
7081/* This routine fires ETW events for
7082 Assembly in LoaderAllocator,
7083 DomainModule's in them,
7084 Modules in them,
7085 JIT methods in them,
7086 and the NGEN methods in them
7087 based on enumerationOptions.*/
7088/********************************************************************************/
7089VOID ETW::EnumerationLog::IterateCollectibleLoaderAllocator(AssemblyLoaderAllocator *pLoaderAllocator, DWORD enumerationOptions)
7090{
7091 CONTRACTL {
7092 THROWS;
7093 GC_TRIGGERS;
7094 PRECONDITION(pLoaderAllocator != NULL);
7095 } CONTRACTL_END;
7096
7097 EX_TRY
7098 {
7099 // Unload Jit Method events
7100 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnload)
7101 {
7102 ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions);
7103 }
7104
7105 // Iterate on all DomainAssembly loaded from the same AssemblyLoaderAllocator
7106 DomainAssemblyIterator domainAssemblyIt = pLoaderAllocator->Id()->GetDomainAssemblyIterator();
7107 while (!domainAssemblyIt.end())
7108 {
7109 Assembly *pAssembly = domainAssemblyIt->GetAssembly(); // TODO: handle iterator
7110
7111 DomainModuleIterator domainModuleIterator = domainAssemblyIt->IterateModules(kModIterIncludeLoaded);
7112 while (domainModuleIterator.Next())
7113 {
7114 Module *pModule = domainModuleIterator.GetModule();
7115 ETW::EnumerationLog::IterateModule(pModule, enumerationOptions);
7116 }
7117
7118 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload)
7119 {
7120 ETW::EnumerationLog::IterateAssembly(pAssembly, enumerationOptions);
7121 }
7122
7123 domainAssemblyIt++;
7124 }
7125
7126 // Load Jit Method events
7127 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoad)
7128 {
7129 ETW::MethodLog::SendEventsForJitMethods(NULL, pLoaderAllocator, enumerationOptions);
7130 }
7131 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7132}
7133
7134/********************************************************************************/
7135/* This routine fires ETW events for Assembly and the DomainModule's in them
7136 based on enumerationOptions.*/
7137/********************************************************************************/
7138VOID ETW::EnumerationLog::IterateAssembly(Assembly *pAssembly, DWORD enumerationOptions)
7139{
7140 CONTRACTL {
7141 THROWS;
7142 GC_TRIGGERS;
7143 PRECONDITION(pAssembly != NULL);
7144 } CONTRACTL_END;
7145
7146 EX_TRY
7147 {
7148 // DC Start events for Assembly
7149 if(enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart)
7150 {
7151 ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions);
7152 }
7153
7154 // DC Start, DCEnd, events for DomainModule
7155 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7156 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart))
7157 {
7158 if(pAssembly->GetDomain()->IsAppDomain())
7159 {
7160 DomainModuleIterator dmIterator = pAssembly->FindDomainAssembly(pAssembly->GetDomain()->AsAppDomain())->IterateModules(kModIterIncludeLoaded);
7161 while (dmIterator.Next())
7162 {
7163 ETW::LoaderLog::SendModuleEvent(dmIterator.GetModule(), enumerationOptions, TRUE);
7164 }
7165 }
7166 }
7167
7168 // DC End or Unload events for Assembly
7169 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7170 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload))
7171 {
7172 ETW::LoaderLog::SendAssemblyEvent(pAssembly, enumerationOptions);
7173 }
7174 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7175}
7176
7177/********************************************************************************/
7178/* This routine fires ETW events for Module, their range information and the NGEN methods in them
7179 based on enumerationOptions.*/
7180/********************************************************************************/
7181VOID ETW::EnumerationLog::IterateModule(Module *pModule, DWORD enumerationOptions)
7182{
7183 CONTRACTL {
7184 THROWS;
7185 GC_TRIGGERS;
7186 PRECONDITION(pModule != NULL);
7187 } CONTRACTL_END;
7188
7189 EX_TRY
7190 {
7191 // DC Start events for Module
7192 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCStart) ||
7193 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCStart))
7194 {
7195 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7196 }
7197
7198 // DC Start or Load or DC End or Unload Ngen Method events
7199 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodLoad) ||
7200 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCStart) ||
7201 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodUnload) ||
7202 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::NgenMethodDCEnd))
7203 {
7204 ETW::MethodLog::SendEventsForNgenMethods(pModule, enumerationOptions);
7205 }
7206
7207 // DC End or Unload events for Module
7208 if((enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleDCEnd) ||
7209 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::DomainAssemblyModuleUnload) ||
7210 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeDCEnd))
7211 {
7212 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7213 }
7214
7215 // If we're logging types, then update the internal Type hash table to account
7216 // for the module's unloading
7217 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::TypeUnload)
7218 {
7219 ETW::TypeSystemLog::OnModuleUnload(pModule);
7220 }
7221
7222 // ModuleRangeLoadPrivate events for module range information from attach/detach scenarios
7223 if (ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context,
7224 TRACE_LEVEL_INFORMATION,
7225 CLR_PERFTRACK_PRIVATE_KEYWORD) &&
7226 (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::ModuleRangeLoadPrivate))
7227 {
7228 ETW::LoaderLog::SendModuleEvent(pModule, enumerationOptions);
7229 }
7230 } EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
7231}
7232
7233//---------------------------------------------------------------------------------------
7234//
7235// This routine sends back domain, assembly, module and method events based on
7236// enumerationOptions.
7237//
7238// Arguments:
7239// * moduleFilter - if non-NULL, events from only moduleFilter module are reported
7240// * domainFilter - if non-NULL, events from only domainFilter domain are reported
7241// * enumerationOptions - Flags from ETW::EnumerationLog::EnumerationStructs which
7242// describe which events should be sent.
7243//
7244// Notes:
7245// * if all filter args are NULL, events from all domains are reported
7246//
7247//
7248
7249// static
7250VOID ETW::EnumerationLog::EnumerationHelper(Module *moduleFilter, BaseDomain *domainFilter, DWORD enumerationOptions)
7251{
7252 CONTRACTL {
7253 THROWS;
7254 GC_TRIGGERS;
7255 } CONTRACTL_END;
7256
7257 // Disable IBC logging during ETW enumeration since we call a lot of functionality
7258 // that does logging and causes problems in the shutdown path due to critical
7259 // section access for IBC logging
7260 IBCLoggingDisabler disableLogging;
7261
7262 if(moduleFilter)
7263 {
7264 // Iteratate modules first because their number is ussualy smaller then the number of methods.
7265 // Thus hitting a timeout due to a large number of methods will not affect modules rundown.tf g
7266 ETW::EnumerationLog::IterateModule(moduleFilter, enumerationOptions);
7267
7268 // As best I can tell from code review, these if statements below are never true. There is
7269 // only one caller to this method that specifies a moduleFilter, ETW::LoaderLog::ModuleLoad.
7270 // That method never specifies these flags. Because it is late in a release cycle I am not
7271 // making a change, but if you see this comment early in the next release cycle consider
7272 // deleting this apparently dead code.
7273
7274 // DC End or Unload Jit Method events from all Domains
7275 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodUnloadOrDCEndAny)
7276 {
7277 ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions);
7278 }
7279
7280 // DC Start or Load Jit Method events from all Domains
7281 if (enumerationOptions & ETW::EnumerationLog::EnumerationStructs::JitMethodLoadOrDCStartAny)
7282 {
7283 ETW::MethodLog::SendEventsForJitMethods(NULL, NULL, enumerationOptions);
7284 }
7285 }
7286 else
7287 {
7288 if(domainFilter)
7289 {
7290 if(domainFilter->IsAppDomain())
7291 {
7292 ETW::EnumerationLog::IterateAppDomain(domainFilter->AsAppDomain(), enumerationOptions);
7293 }
7294 else
7295 {
7296 ETW::EnumerationLog::IterateDomain(domainFilter, enumerationOptions);
7297 }
7298 }
7299 else
7300 {
7301 AppDomainIterator appDomainIterator(FALSE);
7302 while(appDomainIterator.Next())
7303 {
7304 AppDomain *pDomain = appDomainIterator.GetDomain();
7305 if (pDomain != NULL)
7306 {
7307 ETW::EnumerationLog::IterateAppDomain(pDomain, enumerationOptions);
7308 }
7309 }
7310 }
7311 }
7312}
7313
7314#endif // !FEATURE_REDHAWK
7315
7316#ifdef FEATURE_PERFTRACING
7317#include "eventpipe.h"
7318bool EventPipeHelper::Enabled()
7319{
7320 LIMITED_METHOD_CONTRACT;
7321 return EventPipe::Enabled();
7322}
7323#endif // FEATURE_PERFTRACING
7324