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.h
6// Abstract: This module implements Event Tracing support. This includes
7// eventtracebase.h, and adds VM-specific ETW helpers to support features like type
8// logging, allocation logging, and gc heap walk logging.
9//
10
11//
12
13//
14//
15// #EventTracing
16// Windows
17// ETW (Event Tracing for Windows) is a high-performance, low overhead and highly scalable
18// tracing facility provided by the Windows Operating System. ETW is available on Win2K and above. There are
19// four main types of components in ETW: event providers, controllers, consumers, and event trace sessions.
20// An event provider is a logical entity that writes events to ETW sessions. The event provider must register
21// a provider ID with ETW through the registration API. A provider first registers with ETW and writes events
22// from various points in the code by invoking the ETW logging API. When a provider is enabled dynamically by
23// the ETW controller application, calls to the logging API sends events to a specific trace session
24// designated by the controller. Each event sent by the event provider to the trace session consists of a
25// fixed header that includes event metadata and additional variable user-context data. CLR is an event
26// provider.
27
28// Mac
29// DTrace is similar to ETW and has been made to look like ETW at most of the places.
30// For convenience, it is called ETM (Event Tracing for Mac) and exists only on the Mac Leopard OS
31// ============================================================================
32
33#ifndef _VMEVENTTRACE_H_
34#define _VMEVENTTRACE_H_
35
36#include "eventtracebase.h"
37#include "gcinterface.h"
38
39#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
40struct ProfilingScanContext : ScanContext
41{
42 BOOL fProfilerPinned;
43 void * pvEtwContext;
44 void *pHeapId;
45
46 ProfilingScanContext(BOOL fProfilerPinnedParam) : ScanContext()
47 {
48 LIMITED_METHOD_CONTRACT;
49
50 pHeapId = NULL;
51 fProfilerPinned = fProfilerPinnedParam;
52 pvEtwContext = NULL;
53#ifdef FEATURE_CONSERVATIVE_GC
54 // To not confuse GCScan::GcScanRoots
55 promotion = g_pConfig->GetGCConservative();
56#endif
57 }
58};
59#endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
60
61#ifndef FEATURE_REDHAWK
62
63namespace ETW
64{
65 class LoggedTypesFromModule;
66
67 // We keep a hash of these to keep track of:
68 // * Which types have been logged through ETW (so we can avoid logging dupe Type
69 // events), and
70 // * GCSampledObjectAllocation stats to help with "smart sampling" which
71 // dynamically adjusts sampling rate of objects by type.
72 // See code:LoggedTypesFromModuleTraits
73 struct TypeLoggingInfo
74 {
75 public:
76 TypeLoggingInfo(TypeHandle thParam)
77 {
78 Init(thParam);
79 }
80
81 TypeLoggingInfo()
82 {
83 Init(TypeHandle());
84 }
85
86 void Init(TypeHandle thParam)
87 {
88 th = thParam;
89 dwTickOfCurrentTimeBucket = 0;
90 dwAllocCountInCurrentBucket = 0;
91 flAllocPerMSec = 0;
92
93 dwAllocsToSkipPerSample = 0;
94 dwAllocsSkippedForSample = 0;
95 cbIgnoredSizeForSample = 0;
96 };
97
98 // The type this TypeLoggingInfo represents
99 TypeHandle th;
100
101 // Smart sampling
102
103 // These bucket values remember stats of a particular time slice that are used to
104 // help adjust the sampling rate
105 DWORD dwTickOfCurrentTimeBucket;
106 DWORD dwAllocCountInCurrentBucket;
107 float flAllocPerMSec;
108
109 // The number of data points to ignore before taking a "sample" (i.e., logging a
110 // GCSampledObjectAllocation ETW event for this type)
111 DWORD dwAllocsToSkipPerSample;
112
113 // The current number of data points actually ignored for the current sample
114 DWORD dwAllocsSkippedForSample;
115
116 // The current count of bytes of objects of this type actually allocated (and
117 // ignored) for the current sample
118 SIZE_T cbIgnoredSizeForSample;
119 };
120
121 // Class to wrap all type system logic for ETW
122 class TypeSystemLog
123 {
124 private:
125 // Global type hash
126 static AllLoggedTypes *s_pAllLoggedTypes;
127
128 // An unsigned value that gets incremented whenever a global change is made.
129 // When this occurs, threads must synchronize themselves with the global state.
130 // Examples include unloading of modules and disabling of allocation sampling.
131 static unsigned int s_nEpoch;
132
133 // See code:ETW::TypeSystemLog::PostRegistrationInit
134 static BOOL s_fHeapAllocEventEnabledOnStartup;
135 static BOOL s_fHeapAllocHighEventEnabledNow;
136 static BOOL s_fHeapAllocLowEventEnabledNow;
137
138 // If COMPLUS_UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec is set, then
139 // this is used to determine the event frequency, overriding
140 // s_nDefaultMsBetweenEvents above (regardless of which
141 // GCSampledObjectAllocation*Keyword was used)
142 static int s_nCustomMsBetweenEvents;
143
144 public:
145 // This customizes the type logging behavior in LogTypeAndParametersIfNecessary
146 enum TypeLogBehavior
147 {
148 // Take lock, and consult hash table to see if this is the first time we've
149 // encountered the type, in which case, log it
150 kTypeLogBehaviorTakeLockAndLogIfFirstTime,
151
152 // Don't take lock, don't consult hash table. Just log the type. (This is
153 // used in cases when checking for dupe type logging isn't worth it, such as
154 // when logging the finalization of an object.)
155 kTypeLogBehaviorAlwaysLog,
156
157 // When logging the type for GCSampledObjectAllocation events,
158 // we already know we need to log the type (since we already
159 // looked it up in the hash). But we would still need to consult the hash
160 // for any type parameters, so kTypeLogBehaviorAlwaysLog isn't appropriate,
161 // and this is used instead.
162 kTypeLogBehaviorAlwaysLogTopLevelType,
163 };
164
165 static HRESULT PreRegistrationInit();
166 static void PostRegistrationInit();
167 static BOOL IsHeapAllocEventEnabled();
168 static void SendObjectAllocatedEvent(Object * pObject);
169 static CrstBase * GetHashCrst();
170 static VOID LogTypeAndParametersIfNecessary(BulkTypeEventLogger * pBulkTypeEventLogger, ULONGLONG thAsAddr, TypeLogBehavior typeLogBehavior);
171 static VOID OnModuleUnload(Module * pModule);
172 static void OnKeywordsChanged();
173 static void Cleanup();
174 static VOID DeleteTypeHashNoLock(AllLoggedTypes **ppAllLoggedTypes);
175 static VOID FlushObjectAllocationEvents();
176
177 private:
178 static BOOL ShouldLogType(TypeHandle th);
179 static TypeLoggingInfo LookupOrCreateTypeLoggingInfo(TypeHandle th, BOOL * pfCreatedNew, LoggedTypesFromModule ** ppLoggedTypesFromModule = NULL);
180 static BOOL AddTypeToGlobalCacheIfNotExists(TypeHandle th, BOOL * pfCreatedNew);
181 static BOOL AddOrReplaceTypeLoggingInfo(ETW::LoggedTypesFromModule * pLoggedTypesFromModule, const ETW::TypeLoggingInfo * pTypeLoggingInfo);
182 static int GetDefaultMsBetweenEvents();
183 static VOID OnTypesKeywordTurnedOff();
184 };
185
186#endif // FEATURE_REDHAWK
187
188
189 // Class to wrap all GC logic for ETW
190 class GCLog
191 {
192 private:
193 // When WPA triggers a GC, it gives us this unique number to append to our
194 // GCStart event so WPA can correlate the CLR's GC with the JScript GC they
195 // triggered at the same time.
196 //
197 // We set this value when the GC is triggered, and then retrieve the value on the
198 // first subsequent FireGcStart() method call for a full, induced GC, assuming
199 // that that's the GC that WPA triggered. This is imperfect, and if we were in
200 // the act of beginning another full, induced GC (for some other reason), then
201 // we'll attach this sequence number to that GC instead of to the WPA-induced GC,
202 // but who cares? When parsing ETW logs later on, it's indistinguishable if both
203 // GCs really were induced at around the same time.
204#ifdef FEATURE_REDHAWK
205 static volatile LONGLONG s_l64LastClientSequenceNumber;
206#else // FEATURE_REDHAWK
207 static Volatile<LONGLONG> s_l64LastClientSequenceNumber;
208#endif // FEATURE_REDHAWK
209
210 public:
211 typedef union st_GCEventInfo {
212 typedef struct _GenerationInfo {
213 ULONGLONG GenerationSize;
214 ULONGLONG TotalPromotedSize;
215 } GenerationInfo;
216
217 struct {
218 GenerationInfo GenInfo[4]; // the heap info on gen0, gen1, gen2 and the large object heap.
219 ULONGLONG FinalizationPromotedSize; //not available per generation
220 ULONGLONG FinalizationPromotedCount; //not available per generation
221 ULONG PinnedObjectCount;
222 ULONG SinkBlockCount;
223 ULONG GCHandleCount;
224 } HeapStats;
225
226 typedef enum _HeapType {
227 SMALL_OBJECT_HEAP, LARGE_OBJECT_HEAP, READ_ONLY_HEAP
228 } HeapType;
229 struct {
230 ULONGLONG Address;
231 ULONGLONG Size;
232 HeapType Type;
233 } GCCreateSegment;
234
235 struct {
236 ULONGLONG Address;
237 } GCFreeSegment;
238 struct {
239 ULONG Count;
240 ULONG Depth;
241 } GCEnd;
242
243 typedef enum _AllocationKind {
244 AllocationSmall = 0,
245 AllocationLarge
246 }AllocationKind;
247 struct {
248 ULONG Allocation;
249 AllocationKind Kind;
250 } AllocationTick;
251
252 // These values are gotten from the gc_reason
253 // in gcimpl.h
254 typedef enum _GC_REASON {
255 GC_ALLOC_SOH = 0,
256 GC_INDUCED = 1,
257 GC_LOWMEMORY = 2,
258 GC_EMPTY = 3,
259 GC_ALLOC_LOH = 4,
260 GC_OOS_SOH = 5,
261 GC_OOS_LOH = 6,
262 GC_INDUCED_NOFORCE = 7,
263 GC_GCSTRESS = 8,
264 GC_LOWMEMORY_BLOCKING = 9,
265 GC_INDUCED_COMPACTING = 10,
266 GC_LOWMEMORY_HOST = 11
267 } GC_REASON;
268 typedef enum _GC_TYPE {
269 GC_NGC = 0,
270 GC_BGC = 1,
271 GC_FGC = 2
272 } GC_TYPE;
273 typedef enum _GC_ROOT_KIND {
274 GC_ROOT_STACK = 0,
275 GC_ROOT_FQ = 1,
276 GC_ROOT_HANDLES = 2,
277 GC_ROOT_OLDER = 3,
278 GC_ROOT_SIZEDREF = 4,
279 GC_ROOT_OVERFLOW = 5
280 } GC_ROOT_KIND;
281 struct {
282 ULONG Count;
283 ULONG Depth;
284 GC_REASON Reason;
285 GC_TYPE Type;
286 } GCStart;
287
288 struct {
289 ULONG Count; // how many finalizers we called.
290 } GCFinalizers;
291
292 struct {
293 ULONG Reason;
294 // This is only valid when SuspendEE is called by GC (ie, Reason is either
295 // SUSPEND_FOR_GC or SUSPEND_FOR_GC_PREP.
296 ULONG GcCount;
297 } SuspendEE;
298
299 struct {
300 ULONG HeapNum;
301 } GCMark;
302
303 struct {
304 ULONGLONG SegmentSize;
305 ULONGLONG LargeObjectSegmentSize;
306 BOOL ServerGC; // TRUE means it's server GC; FALSE means it's workstation.
307 } GCSettings;
308
309 struct {
310 // The generation that triggered this notification.
311 ULONG Count;
312 // 1 means the notification was due to allocation; 0 means it was due to other factors.
313 ULONG Alloc;
314 } GCFullNotify;
315 } ETW_GC_INFO, *PETW_GC_INFO;
316
317#ifdef FEATURE_EVENT_TRACE
318 static VOID GCSettingsEvent();
319#else
320 static VOID GCSettingsEvent() {};
321#endif // FEATURE_EVENT_TRACE
322
323 static BOOL ShouldWalkHeapObjectsForEtw();
324 static BOOL ShouldWalkHeapRootsForEtw();
325 static BOOL ShouldTrackMovementForEtw();
326 static HRESULT ForceGCForDiagnostics();
327 static VOID ForceGC(LONGLONG l64ClientSequenceNumber);
328 static VOID FireGcStart(ETW_GC_INFO * pGcInfo);
329 static VOID RootReference(
330 LPVOID pvHandle,
331 Object * pRootedNode,
332 Object * pSecondaryNodeForDependentHandle,
333 BOOL fDependentHandle,
334 ProfilingScanContext * profilingScanContext,
335 DWORD dwGCFlags,
336 DWORD rootFlags);
337 static VOID ObjectReference(
338 ProfilerWalkHeapContext * profilerWalkHeapContext,
339 Object * pObjReferenceSource,
340 ULONGLONG typeID,
341 ULONGLONG cRefs,
342 Object ** rgObjReferenceTargets);
343 static BOOL ShouldWalkStaticsAndCOMForEtw();
344 static VOID WalkStaticsAndCOMForETW();
345 static VOID EndHeapDump(ProfilerWalkHeapContext * profilerWalkHeapContext);
346#ifdef FEATURE_EVENT_TRACE
347 static VOID BeginMovedReferences(size_t * pProfilingContext);
348 static VOID MovedReference(BYTE * pbMemBlockStart, BYTE * pbMemBlockEnd, ptrdiff_t cbRelocDistance, size_t profilingContext, BOOL fCompacting, BOOL fAllowProfApiNotification = TRUE);
349 static VOID EndMovedReferences(size_t profilingContext, BOOL fAllowProfApiNotification = TRUE);
350#else
351 // TODO: Need to be implemented for PROFILING_SUPPORTED.
352 static VOID BeginMovedReferences(size_t * pProfilingContext) {};
353 static VOID MovedReference(BYTE * pbMemBlockStart, BYTE * pbMemBlockEnd, ptrdiff_t cbRelocDistance, size_t profilingContext, BOOL fCompacting, BOOL fAllowProfApiNotification = TRUE) {};
354 static VOID EndMovedReferences(size_t profilingContext, BOOL fAllowProfApiNotification = TRUE) {};
355#endif // FEATURE_EVENT_TRACE
356 static VOID SendFinalizeObjectEvent(MethodTable * pMT, Object * pObj);
357 };
358};
359
360
361#endif //_VMEVENTTRACE_H_
362