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. |
29 | const 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 | |
42 | struct EventStructGCBulkRootEdgeValue |
43 | { |
44 | LPVOID RootedNodeAddress; |
45 | BYTE GCRootKind; |
46 | DWORD GCRootFlag; |
47 | LPVOID GCRootID; |
48 | }; |
49 | |
50 | struct EventStructGCBulkRootConditionalWeakTableElementEdgeValue |
51 | { |
52 | LPVOID GCKeyNodeID; |
53 | LPVOID GCValueNodeID; |
54 | LPVOID GCRootID; |
55 | }; |
56 | |
57 | struct EventStructGCBulkNodeValue |
58 | { |
59 | LPVOID Address; |
60 | ULONGLONG Size; |
61 | ULONGLONG TypeID; |
62 | ULONGLONG EdgeCount; |
63 | }; |
64 | |
65 | struct EventStructGCBulkEdgeValue |
66 | { |
67 | LPVOID Value; |
68 | ULONG ReferencingFieldID; |
69 | }; |
70 | |
71 | struct EventStructGCBulkSurvivingObjectRangesValue |
72 | { |
73 | LPVOID RangeBase; |
74 | ULONGLONG RangeLength; |
75 | }; |
76 | |
77 | struct EventStructGCBulkMovedObjectRangesValue |
78 | { |
79 | LPVOID OldRangeBase; |
80 | LPVOID NewRangeBase; |
81 | ULONGLONG RangeLength; |
82 | }; |
83 | |
84 | struct 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. |
95 | struct EventStructBulkTypeFixedSizedData |
96 | { |
97 | ULONGLONG TypeID; |
98 | ULONGLONG ModuleID; |
99 | ULONG TypeNameID; |
100 | ULONG Flags; |
101 | BYTE CorElementType; |
102 | }; |
103 | |
104 | struct 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 | |
169 | struct EventRCWEntry |
170 | { |
171 | ULONGLONG ObjectID; |
172 | ULONGLONG TypeID; |
173 | ULONGLONG IUnk; |
174 | ULONGLONG VTable; |
175 | ULONG RefCount; |
176 | ULONG Flags; |
177 | }; |
178 | |
179 | |
180 | struct 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 |
200 | class BulkTypeValue |
201 | { |
202 | public: |
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). |
253 | class BulkTypeEventLogger |
254 | { |
255 | private: |
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 | |
308 | public: |
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. |
347 | class BulkComLogger |
348 | { |
349 | public: |
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 | |
361 | private: |
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 *, 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 | |
380 | private: |
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 | |
392 | private: |
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. |
410 | class BulkStaticsLogger |
411 | { |
412 | public: |
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 | |
423 | private: |
424 | // Write a single static variable to the log. |
425 | void WriteEntry(AppDomain *domain, Object **address, Object *obj, FieldDesc *fieldDesc); |
426 | |
427 | private: |
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 | |