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) |
40 | struct 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 | |
63 | namespace 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 | |