-
Notifications
You must be signed in to change notification settings - Fork 4.7k
/
eventtrace_bulktype.cpp
401 lines (344 loc) · 13.1 KB
/
eventtrace_bulktype.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include "common.h"
#include "gcenv.h"
#include "eventtrace.h"
#include "eventtrace_etw.h"
#include "rhbinder.h"
#include "slist.h"
#include "runtimeinstance.h"
#include "shash.h"
#include "eventtracebase.h"
#include "eventtracepriv.h"
#include "shash.inl"
#if defined(FEATURE_EVENT_TRACE)
//---------------------------------------------------------------------------------------
// BulkTypeValue / BulkTypeEventLogger: These take care of batching up types so they can
// be logged via ETW in bulk
//---------------------------------------------------------------------------------------
BulkTypeValue::BulkTypeValue()
: cTypeParameters(0)
, ullSingleTypeParameter(0)
{
LIMITED_METHOD_CONTRACT;
ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
}
//---------------------------------------------------------------------------------------
//
// Clears a BulkTypeValue so it can be reused after the buffer is flushed to ETW
//
void BulkTypeValue::Clear()
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
ZeroMemory(&fixedSizedData, sizeof(fixedSizedData));
cTypeParameters = 0;
ullSingleTypeParameter = 0;
}
//---------------------------------------------------------------------------------------
// BulkTypeEventLogger is a helper class to batch up type information and then flush to
// ETW once the event reaches its max # descriptors
//---------------------------------------------------------------------------------------
//
// Fire an ETW event for all the types we batched so far, and then reset our state
// so we can start batching new types at the beginning of the array.
//
void BulkTypeEventLogger::FireBulkTypeEvent()
{
LIMITED_METHOD_CONTRACT;
if (m_nBulkTypeValueCount == 0)
{
// No types were batched up, so nothing to send
return;
}
UINT16 nClrInstanceID = GetClrInstanceId();
if(m_pBulkTypeEventBuffer == NULL)
{
// The buffer could not be allocated when this object was created, so bail.
return;
}
UINT iSize = 0;
for (int iTypeData = 0; iTypeData < m_nBulkTypeValueCount; iTypeData++)
{
BulkTypeValue& target = m_rgBulkTypeValues[iTypeData];
// Do fixed-size data as one bulk copy
memcpy(
m_pBulkTypeEventBuffer + iSize,
&(target.fixedSizedData),
sizeof(target.fixedSizedData));
iSize += sizeof(target.fixedSizedData);
// Do var-sized data individually per field
// No name in event, so just the null terminator
m_pBulkTypeEventBuffer[iSize++] = 0;
m_pBulkTypeEventBuffer[iSize++] = 0;
// Type parameter count
ULONG cTypeParams = target.cTypeParameters;
ULONG *ptrInt = (ULONG*)(m_pBulkTypeEventBuffer + iSize);
*ptrInt = cTypeParams;
iSize += sizeof(ULONG);
// Type parameter array
if (cTypeParams == 1)
{
memcpy(m_pBulkTypeEventBuffer + iSize, &target.ullSingleTypeParameter, sizeof(ULONGLONG) * cTypeParams);
iSize += sizeof(ULONGLONG) * cTypeParams;
}
else if (cTypeParams > 1)
{
ASSERT_UNCONDITIONALLY("unexpected value of cTypeParams greater than 1");
}
}
FireEtwBulkType(m_nBulkTypeValueCount, GetClrInstanceId(), iSize, m_pBulkTypeEventBuffer);
// Reset state
m_nBulkTypeValueCount = 0;
m_nBulkTypeValueByteCount = 0;
}
// We keep a hash of these to keep track of:
// * Which types have been logged through ETW (so we can avoid logging dupe Type
// events), and
// * GCSampledObjectAllocation stats to help with "smart sampling" which
// dynamically adjusts sampling rate of objects by type.
// See code:LoggedTypesFromModuleTraits
class LoggedTypesTraits : public DefaultSHashTraits<MethodTable*>
{
public:
// explicitly declare local typedefs for these traits types, otherwise
// the compiler may get confused
typedef MethodTable* key_t;
static key_t GetKey(const element_t &e)
{
LIMITED_METHOD_CONTRACT;
return e;
}
static BOOL Equals(key_t k1, key_t k2)
{
LIMITED_METHOD_CONTRACT;
return (k1 == k2);
}
static count_t Hash(key_t k)
{
LIMITED_METHOD_CONTRACT;
return (count_t) (uintptr_t) k;
}
static bool IsNull(const element_t &e)
{
LIMITED_METHOD_CONTRACT;
return (e == NULL);
}
static const element_t Null()
{
LIMITED_METHOD_CONTRACT;
return NULL;
}
};
enum class CorElementType : uint8_t
{
ELEMENT_TYPE_END = 0x0,
ELEMENT_TYPE_BOOLEAN = 0x2,
ELEMENT_TYPE_CHAR = 0x3,
ELEMENT_TYPE_I1 = 0x4,
ELEMENT_TYPE_U1 = 0x5,
ELEMENT_TYPE_I2 = 0x6,
ELEMENT_TYPE_U2 = 0x7,
ELEMENT_TYPE_I4 = 0x8,
ELEMENT_TYPE_U4 = 0x9,
ELEMENT_TYPE_I8 = 0xa,
ELEMENT_TYPE_U8 = 0xb,
ELEMENT_TYPE_R4 = 0xc,
ELEMENT_TYPE_R8 = 0xd,
ELEMENT_TYPE_I = 0x18,
ELEMENT_TYPE_U = 0x19,
};
static CorElementType ElementTypeToCorElementType(EETypeElementType elementType)
{
switch (elementType)
{
case EETypeElementType::ElementType_Boolean:
return CorElementType::ELEMENT_TYPE_BOOLEAN;
case EETypeElementType::ElementType_Char:
return CorElementType::ELEMENT_TYPE_CHAR;
case EETypeElementType::ElementType_SByte:
return CorElementType::ELEMENT_TYPE_I1;
case EETypeElementType::ElementType_Byte:
return CorElementType::ELEMENT_TYPE_U1;
case EETypeElementType::ElementType_Int16:
return CorElementType::ELEMENT_TYPE_I2;
case EETypeElementType::ElementType_UInt16:
return CorElementType::ELEMENT_TYPE_U2;
case EETypeElementType::ElementType_Int32:
return CorElementType::ELEMENT_TYPE_I4;
case EETypeElementType::ElementType_UInt32:
return CorElementType::ELEMENT_TYPE_U4;
case EETypeElementType::ElementType_Int64:
return CorElementType::ELEMENT_TYPE_I8;
case EETypeElementType::ElementType_UInt64:
return CorElementType::ELEMENT_TYPE_U8;
case EETypeElementType::ElementType_Single:
return CorElementType::ELEMENT_TYPE_R4;
case EETypeElementType::ElementType_Double:
return CorElementType::ELEMENT_TYPE_R8;
case EETypeElementType::ElementType_IntPtr:
return CorElementType::ELEMENT_TYPE_I;
case EETypeElementType::ElementType_UIntPtr:
return CorElementType::ELEMENT_TYPE_U;
}
return CorElementType::ELEMENT_TYPE_END;
}
// Avoid reporting the same type twice by keeping a hash of logged types.
SHash<LoggedTypesTraits>* s_loggedTypesHash = NULL;
//---------------------------------------------------------------------------------------
//
// Interrogates MethodTable for the info that's interesting to include in the BulkType ETW
// event. Does not recursively call self for type parameters.
//
// Arguments:
// * pEEType - MethodTable to log info about
//
// Return Value:
// Index into internal array where the info got batched. Or -1 if there was a
// failure.
//
int BulkTypeEventLogger::LogSingleType(MethodTable * pEEType)
{
#ifdef MULTIPLE_HEAPS
// We need to add a lock to protect the types hash for Server GC.
ASSERT_UNCONDITIONALLY("Add a lock to protect s_loggedTypesHash access!");
#endif
//Avoid logging the same type twice, but using the hash of loggged types.
if (s_loggedTypesHash == NULL)
s_loggedTypesHash = new SHash<LoggedTypesTraits>();
MethodTable* preexistingType = s_loggedTypesHash->Lookup(pEEType);
if (preexistingType != NULL)
{
return -1;
}
else
{
s_loggedTypesHash->Add(pEEType);
}
// If there's no room for another type, flush what we've got
if (m_nBulkTypeValueCount == _countof(m_rgBulkTypeValues))
{
FireBulkTypeEvent();
}
_ASSERTE(m_nBulkTypeValueCount < _countof(m_rgBulkTypeValues));
BulkTypeValue * pVal = &m_rgBulkTypeValues[m_nBulkTypeValueCount];
// Clear out pVal before filling it out (array elements can get reused if there
// are enough types that we need to flush to multiple events).
pVal->Clear();
pVal->fixedSizedData.TypeID = (ULONGLONG) pEEType;
pVal->fixedSizedData.Flags = kEtwTypeFlagsModuleBaseAddress;
pVal->fixedSizedData.CorElementType = (BYTE)ElementTypeToCorElementType(pEEType->GetElementType());
ULONGLONG * rgTypeParamsForEvent = NULL;
ULONGLONG typeParamForNonGenericType = 0;
// Determine this MethodTable's module.
RuntimeInstance * pRuntimeInstance = GetRuntimeInstance();
// EEType for GC statics are not fully populated and they do not have a valid TypeManager. We will identify them by checking for `ElementType_Unknown`.
// We will not be able to get the osModuleHandle for these
ULONGLONG osModuleHandle = 0;
if (pEEType->GetElementType() != ElementType_Unknown)
{
osModuleHandle = (ULONGLONG) pEEType->GetTypeManagerPtr()->AsTypeManager()->GetOsModuleHandle();
}
pVal->fixedSizedData.ModuleID = osModuleHandle;
if (pEEType->IsParameterizedType())
{
ASSERT(pEEType->IsArray());
// Array
pVal->fixedSizedData.Flags |= kEtwTypeFlagsArray;
pVal->cTypeParameters = 1;
pVal->ullSingleTypeParameter = (ULONGLONG) pEEType->GetRelatedParameterType();
}
else
{
// Note: For generic types, we do not necessarily know the generic parameters.
// So we leave it to the profiler at post-processing time to determine that via
// the PDBs. We'll leave pVal->cTypeParameters as 0, even though there could be
// type parameters.
// Flags
if (pEEType->HasFinalizer())
{
pVal->fixedSizedData.Flags |= kEtwTypeFlagsFinalizable;
}
// Note: Pn runtime knows nothing about delegates, and there are no CCWs/RCWs.
// So no other type flags are applicable to set
}
ULONGLONG rvaType = osModuleHandle == 0 ? 0 : (ULONGLONG(pEEType) - osModuleHandle);
pVal->fixedSizedData.TypeNameID = (DWORD) rvaType;
// Now that we know the full size of this type's data, see if it fits in our
// batch or whether we need to flush
int cbVal = pVal->GetByteCountInEvent();
if (cbVal > kMaxBytesTypeValues)
{
// This type is apparently so huge, it's too big to squeeze into an event, even
// if it were the only type batched in the whole event. Bail
ASSERT(!"Type too big to log via ETW");
return -1;
}
if (m_nBulkTypeValueByteCount + cbVal > kMaxBytesTypeValues)
{
// Although this type fits into the array, its size is so big that the entire
// array can't be logged via ETW. So flush the array, and start over by
// calling ourselves--this refetches the type info and puts it at the
// beginning of the array. Since we know this type is small enough to be
// batched into an event on its own, this recursive call will not try to
// call itself again.
FireBulkTypeEvent();
return LogSingleType(pEEType);
}
// The type fits into the batch, so update our state
m_nBulkTypeValueCount++;
m_nBulkTypeValueByteCount += cbVal;
return m_nBulkTypeValueCount - 1; // Index of type we just added
}
//---------------------------------------------------------------------------------------
//
// Batches up ETW information for a type and pops out to recursively call
// ETW::TypeSystemLog::LogTypeAndParametersIfNecessary for any
// "type parameters". Generics info is not reliably available, so "type parameter"
// really just refers to the type of array elements if thAsAddr is an array.
//
// Arguments:
// * thAsAddr - MethodTable to log
// * typeLogBehavior - Ignored in Redhawk builds
//
void BulkTypeEventLogger::LogTypeAndParameters(uint64_t thAsAddr)
{
MethodTable * pEEType = (MethodTable *) thAsAddr;
// Batch up this type. This grabs useful info about the type, including any
// type parameters it may have, and sticks it in m_rgBulkTypeValues
int iBulkTypeEventData = LogSingleType(pEEType);
if (iBulkTypeEventData == -1)
{
// There was a failure trying to log the type, so don't bother with its type
// parameters
return;
}
// Look at the type info we just batched, so we can get the type parameters
BulkTypeValue * pVal = &m_rgBulkTypeValues[iBulkTypeEventData];
// We're about to recursively call ourselves for the type parameters, so make a
// local copy of their type handles first (else, as we log them we could flush
// and clear out m_rgBulkTypeValues, thus trashing pVal)
DWORD cTypeParams = pVal->cTypeParameters;
if (cTypeParams == 1)
{
LogTypeAndParameters(pVal->ullSingleTypeParameter);
}
else if (cTypeParams > 1)
{
ASSERT_UNCONDITIONALLY("unexpected value of cTypeParams greater than 1");
}
}
void BulkTypeEventLogger::Cleanup()
{
if (s_loggedTypesHash != NULL)
{
delete s_loggedTypesHash;
s_loggedTypesHash = NULL;
}
}
#endif // defined(FEATURE_EVENT_TRACE)