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: eventtracepriv.h
6//
7// Contains some private definitions used by eventrace.cpp, but that aren't needed by
8// clients of eventtrace.cpp, and thus don't belong in eventtrace.h. Also, since
9// inclusions of this file are tightly controlled (basically just by eventtrace.cpp), we
10// can assume some classes are defined that aren't necessarily defined when eventtrace.h
11// is #included (e.g., StackSString and StackSArray).
12//
13
14//
15
16//
17// ============================================================================
18
19#ifndef __EVENTTRACEPRIV_H__
20#define __EVENTTRACEPRIV_H__
21
22#ifndef _countof
23#define _countof(_array) (sizeof(_array)/sizeof(_array[0]))
24#endif
25
26// ETW has a limitation of 64K for TOTAL event Size, however there is overhead associated with
27// the event headers. It is unclear exactly how much that is, but 1K should be sufficiently
28// far away to avoid problems without sacrificing the perf of bulk processing.
29const UINT cbMaxEtwEvent = 63 * 1024;
30
31//---------------------------------------------------------------------------------------
32// C++ copies of ETW structures
33//---------------------------------------------------------------------------------------
34
35// !!!!!!! NOTE !!!!!!!!
36// The EventStruct* structs are described in the ETW manifest event templates, and the
37// LAYOUT MUST MATCH THE MANIFEST EXACTLY!
38// !!!!!!! NOTE !!!!!!!!
39
40#pragma pack(push, 1)
41
42struct EventStructGCBulkRootEdgeValue
43{
44 LPVOID RootedNodeAddress;
45 BYTE GCRootKind;
46 DWORD GCRootFlag;
47 LPVOID GCRootID;
48};
49
50struct EventStructGCBulkRootConditionalWeakTableElementEdgeValue
51{
52 LPVOID GCKeyNodeID;
53 LPVOID GCValueNodeID;
54 LPVOID GCRootID;
55};
56
57struct EventStructGCBulkNodeValue
58{
59 LPVOID Address;
60 ULONGLONG Size;
61 ULONGLONG TypeID;
62 ULONGLONG EdgeCount;
63};
64
65struct EventStructGCBulkEdgeValue
66{
67 LPVOID Value;
68 ULONG ReferencingFieldID;
69};
70
71struct EventStructGCBulkSurvivingObjectRangesValue
72{
73 LPVOID RangeBase;
74 ULONGLONG RangeLength;
75};
76
77struct EventStructGCBulkMovedObjectRangesValue
78{
79 LPVOID OldRangeBase;
80 LPVOID NewRangeBase;
81 ULONGLONG RangeLength;
82};
83
84struct EventStructStaticBulkFixedSizeData
85{
86 ULONGLONG TypeID;
87 ULONGLONG Address;
88 ULONGLONG Value;
89 ULONG Flags;
90};
91
92// This only contains the fixed-size data at the top of each struct in
93// the bulk type event. These fields must still match exactly the initial
94// fields of the struct described in the manifest.
95struct EventStructBulkTypeFixedSizedData
96{
97 ULONGLONG TypeID;
98 ULONGLONG ModuleID;
99 ULONG TypeNameID;
100 ULONG Flags;
101 BYTE CorElementType;
102};
103
104struct EventStaticEntry
105{
106 ULONGLONG GCRootID;
107 ULONGLONG ObjectID;
108 ULONGLONG TypeID;
109 ULONG Flags;
110 wchar_t Name[0];
111
112 // Writes one EventStaticEntry to the buffer specified by ptr. Since we don't actually know how large the event will be,
113 // this write may fail if the remaining buffer is not large enough. This function returns the number of bytes written
114 // on success (return is >= 0), and -1 on failure. If we return -1, the caller is expected to flush the current buffer
115 // and try again.
116 static int WriteEntry(BYTE *ptr, int sizeRemaining, ULONGLONG addr, ULONGLONG obj, ULONGLONG typeId, ULONG flags, FieldDesc *fieldDesc)
117 {
118 WRAPPER_NO_CONTRACT;
119
120 // sizeRemaining must be larger than the structure + 1 wchar for the struct and
121 // null terminator of Name. We will do a better bounds check when we know the
122 // size of the field name.
123 if (sizeRemaining < sizeof(EventStaticEntry) + sizeof(wchar_t))
124 return -1;
125
126 // The location in the structure to write to. We won't actually write here unless we have sufficient buffer.
127 wchar_t *name = (wchar_t *)(ptr + offsetof(EventStaticEntry, Name));
128 int len = 0;
129
130 LPCUTF8 utf8Name = 0;
131 if (SUCCEEDED(fieldDesc->GetName_NoThrow(&utf8Name)))
132 {
133 len = MultiByteToWideChar(CP_ACP, 0, utf8Name, -1, name, sizeRemaining - sizeof(EventStaticEntry));
134 if (len <= 0)
135 {
136 // We will ignore corrupted/bad metadata here and only emit names for fields which are
137 // up to 255 characters (and also don't fit in the buffer).
138 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && sizeRemaining < 256)
139 return -1; // nothing written, insufficient buffer. Flush and try again.
140
141 // If the name is larger than 255 or we have some other error converting the string,
142 // just emit an empty string.
143 len = 1;
144 name[0] = 0;
145 }
146 }
147 else
148 {
149 // Couldn't get the name for some reason, just emit an empty string.
150 len = 1;
151 name[0] = 0;
152 }
153
154 // At this point we should have written something to the name buffer.
155 _ASSERTE(len > 0);
156
157 // At this point we've written the field name (even if it's just an empty string).
158 // Write the rest of the fields to the buffer and return the total size.
159 EventStaticEntry *entry = (EventStaticEntry*)ptr;
160 entry->GCRootID = addr;
161 entry->ObjectID = obj;
162 entry->TypeID = typeId;
163 entry->Flags = flags;
164
165 return sizeof(EventStaticEntry) + len * sizeof(wchar_t);
166 }
167};
168
169struct EventRCWEntry
170{
171 ULONGLONG ObjectID;
172 ULONGLONG TypeID;
173 ULONGLONG IUnk;
174 ULONGLONG VTable;
175 ULONG RefCount;
176 ULONG Flags;
177};
178
179
180struct EventCCWEntry
181{
182 enum CCWFlags
183 {
184 Strong = 0x1,
185 Pegged = 0x2
186 };
187
188 ULONGLONG RootID;
189 ULONGLONG ObjectID;
190 ULONGLONG TypeID;
191 ULONGLONG IUnk;
192 ULONG RefCount;
193 ULONG JupiterRefCount;
194 ULONG Flags;
195};
196
197#pragma pack(pop)
198
199// Represents one instance of the Value struct inside a single BulkType event
200class BulkTypeValue
201{
202public:
203 BulkTypeValue();
204 void Clear();
205
206 // How many bytes will this BulkTypeValue take up when written into the actual ETW
207 // event?
208 int GetByteCountInEvent()
209 {
210 return
211 sizeof(fixedSizedData) +
212 sizeof(cTypeParameters) +
213#ifdef FEATURE_REDHAWK
214 sizeof(WCHAR) + // No name in event, so just the null terminator
215 cTypeParameters * sizeof(ULONGLONG); // Type parameters
216#else
217 (sName.GetCount() + 1) * sizeof(WCHAR) + // Size of name, including null terminator
218 rgTypeParameters.GetCount() * sizeof(ULONGLONG);// Type parameters
219#endif
220 }
221
222 EventStructBulkTypeFixedSizedData fixedSizedData;
223
224 // Below are the remainder of each struct in the bulk type event (i.e., the
225 // variable-sized data). The var-sized fields are copied into the event individually
226 // (not directly), so they don't need to have the same layout as in the ETW manifest
227
228 // This is really a denorm of the size already stored in rgTypeParameters, but we
229 // need a persistent place to stash this away so EventDataDescCreate & EventWrite
230 // have a reliable place to copy it from. This is filled in at the last minute,
231 // when sending the event. (On ProjectN, which doesn't have StackSArray, this is
232 // filled in earlier and used in more places.)
233 ULONG cTypeParameters;
234
235#ifdef FEATURE_REDHAWK
236 // If > 1 type parameter, this is an array of their EEType*'s
237 NewArrayHolder<ULONGLONG> rgTypeParameters;
238
239 // If exactly one type parameter, this is its EEType*. (If != 1 type parameter,
240 // this is 0.)
241 ULONGLONG ullSingleTypeParameter;
242#else // FEATURE_REDHAWK
243 StackSString sName;
244 StackSArray<ULONGLONG> rgTypeParameters;
245#endif // FEATURE_REDHAWK
246};
247
248// Encapsulates all the type event batching we need to do. This is used by
249// ETW::TypeSystemLog, which calls LogTypeAndParameters for each type to be logged.
250// BulkTypeEventLogger will batch each type and its generic type parameters, and flush to
251// ETW as necessary. ETW::TypeSystemLog also calls FireBulkTypeEvent directly to force a
252// flush (e.g., once at end of GC heap traversal, or on each object allocation).
253class BulkTypeEventLogger
254{
255private:
256
257 // The maximum event size, and the size of the buffer that we allocate to hold the event contents.
258 static const size_t kSizeOfEventBuffer = 65536;
259
260 // Estimate of how many bytes we can squeeze in the event data for the value struct
261 // array. (Intentionally overestimate the size of the non-array parts to keep it safe.)
262 static const int kMaxBytesTypeValues = (cbMaxEtwEvent - 0x30);
263
264 // Estimate of how many type value elements we can put into the struct array, while
265 // staying under the ETW event size limit. Note that this is impossible to calculate
266 // perfectly, since each element of the struct array has variable size.
267 //
268 // In addition to the byte-size limit per event, Windows always forces on us a
269 // max-number-of-descriptors per event, which in the case of BulkType, will kick in
270 // far sooner. There's a max number of 128 descriptors allowed per event. 2 are used
271 // for Count + ClrInstanceID. Then 4 per batched value. (Might actually be 3 if there
272 // are no type parameters to log, but let's overestimate at 4 per value).
273 static const int kMaxCountTypeValues = (128 - 2) / 4;
274 // Note: This results in a relatively small batch (about 31 types per event). We
275 // could increase this substantially by creating a single, contiguous buffer, which
276 // would let us max out the number of type values to batch by allowing the byte-size
277 // limit to kick in before the max-descriptor limit. We could esimate that as
278 // follows:
279 //
280 // static const int kMaxCountTypeValues = kMaxBytesTypeValues /
281 // (sizeof(EventStructBulkTypeFixedSizedData) +
282 // 200 * sizeof(WCHAR) + // Assume 199 + 1 terminating-NULL character in type name
283 // sizeof(UINT) + // Type parameter count
284 // 10 * sizeof(ULONGLONG)); // Assume 10 type parameters
285 //
286 // The downside, though, is that we would have to do a lot more copying to fill out
287 // that buffer before sending the event. It's unclear that increasing the batch size
288 // is enough of a win to offset all the extra buffer copying. So for now, we'll keep
289 // the batch size low and avoid extra copying.
290
291 // How many types have we batched?
292 int m_nBulkTypeValueCount;
293
294 // What is the byte size of all the types we've batched?
295 int m_nBulkTypeValueByteCount;
296
297 // List of types we've batched.
298 BulkTypeValue m_rgBulkTypeValues[kMaxCountTypeValues];
299
300 BYTE *m_pBulkTypeEventBuffer;
301
302#ifdef FEATURE_REDHAWK
303 int LogSingleType(EEType * pEEType);
304#else
305 int LogSingleType(TypeHandle th);
306#endif
307
308public:
309 BulkTypeEventLogger() :
310 m_nBulkTypeValueCount(0),
311 m_nBulkTypeValueByteCount(0)
312 , m_pBulkTypeEventBuffer(NULL)
313 {
314 CONTRACTL
315 {
316 NOTHROW;
317 GC_NOTRIGGER;
318 MODE_ANY;
319 }
320 CONTRACTL_END;
321
322 m_pBulkTypeEventBuffer = new (nothrow) BYTE[kSizeOfEventBuffer];
323 }
324
325 ~BulkTypeEventLogger()
326 {
327 CONTRACTL
328 {
329 NOTHROW;
330 GC_NOTRIGGER;
331 MODE_ANY;
332 }
333 CONTRACTL_END;
334
335 delete[] m_pBulkTypeEventBuffer;
336 m_pBulkTypeEventBuffer = NULL;
337 }
338
339 void LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior);
340 void FireBulkTypeEvent();
341};
342
343
344// Does all logging for RCWs and CCWs in the process. We walk RCWs by enumerating all syncblocks in
345// the process and seeing if they have associated interop information. We enumerate all CCWs in the
346// process from the RefCount handles on the handle table.
347class BulkComLogger
348{
349public:
350 // If typeLogger is non-null, we will log out the types via the logger, otherwise no type
351 // information will be logged.
352 BulkComLogger(BulkTypeEventLogger *typeLogger);
353 ~BulkComLogger();
354
355 // Walks all RCW/CCW objects.
356 void LogAllComObjects();
357
358 // Forces a flush of all ETW events not yet fired.
359 void FireBulkComEvent();
360
361private:
362 // Writes one RCW to the RCW buffer. May or may not fire the event.
363 void WriteRcw(RCW *rcw, Object *obj);
364
365 // Writes one CCW to the CCW buffer. May or may not fire the event.
366 void WriteCcw(ComCallWrapper *ccw, Object **handle, Object *obj);
367
368 // Forces a flush of all RCW ETW events not yet fired.
369 void FlushRcw();
370
371 // Forces a flush of all CCW ETW events not yet fired.
372 void FlushCcw();
373
374 // Callback used during handle table enumeration.
375 static void HandleWalkCallback(PTR_UNCHECKED_OBJECTREF pref, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2);
376
377 // Used during CCW enumeration to keep track of all object handles which point to a CCW.
378 void AddCcwHandle(Object **handle);
379
380private:
381 struct CCWEnumerationEntry
382 {
383 CCWEnumerationEntry *Next;
384 int Count;
385 Object **Handles[64];
386
387 CCWEnumerationEntry() : Next(0), Count(0)
388 {
389 }
390 };
391
392private:
393 // The maximum number of RCW/CCW events we can batch up based on the max size of an ETW event.
394 static const int kMaxRcwCount = (cbMaxEtwEvent - 0x30) / sizeof(EventRCWEntry);
395 static const int kMaxCcwCount = (cbMaxEtwEvent - 0x30) / sizeof(EventCCWEntry);
396
397 int m_currRcw; // The current number of batched (but not emitted) RCW events.
398 int m_currCcw; // The current number of batched (but not emitted) CCW events.
399
400 BulkTypeEventLogger *m_typeLogger; // Type logger to emit type data for.
401
402 EventRCWEntry *m_etwRcwData; // RCW buffer.
403 EventCCWEntry *m_etwCcwData; // CCW buffer.
404
405 CCWEnumerationEntry *m_enumResult;
406};
407
408
409// Does bulk static variable ETW logging.
410class BulkStaticsLogger
411{
412public:
413 BulkStaticsLogger(BulkTypeEventLogger *typeLogger);
414 ~BulkStaticsLogger();
415
416 // Walk all static variables in the process and write them to the buffer, firing ETW events
417 // as we reach the max buffer size.
418 void LogAllStatics();
419
420 // Force a flush of the static data, firing an ETW event for any not yet written.
421 void FireBulkStaticsEvent();
422
423private:
424 // Write a single static variable to the log.
425 void WriteEntry(AppDomain *domain, Object **address, Object *obj, FieldDesc *fieldDesc);
426
427private:
428 // The maximum bytes we can emit in the statics buffer.
429 static const int kMaxBytesValues = (cbMaxEtwEvent - 0x30);
430
431 BYTE *m_buffer; // Buffer to queue up statics in
432 int m_used; // The amount of bytes used in m_buffer.
433 int m_count; // The number of statics currently written to m_buffer.
434 AppDomain *m_domain; // The current AppDomain m_buffer contains statics for.
435 BulkTypeEventLogger *m_typeLogger; // The type logger used to emit type data as we encounter it.
436};
437
438
439
440#endif // __EVENTTRACEPRIV_H__
441
442