diff options
Diffstat (limited to 'src/vm/eventtracepriv.h')
-rw-r--r-- | src/vm/eventtracepriv.h | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/src/vm/eventtracepriv.h b/src/vm/eventtracepriv.h new file mode 100644 index 0000000000..0932225133 --- /dev/null +++ b/src/vm/eventtracepriv.h @@ -0,0 +1,414 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// File: eventtracepriv.h +// +// Contains some private definitions used by eventrace.cpp, but that aren't needed by +// clients of eventtrace.cpp, and thus don't belong in eventtrace.h. Also, since +// inclusions of this file are tightly controlled (basically just by eventtrace.cpp), we +// can assume some classes are defined that aren't necessarily defined when eventtrace.h +// is #included (e.g., StackSString and StackSArray). +// + +// + +// +// ============================================================================ + +#ifndef __EVENTTRACEPRIV_H__ +#define __EVENTTRACEPRIV_H__ + +#ifndef _countof +#define _countof(_array) (sizeof(_array)/sizeof(_array[0])) +#endif + +const UINT cbMaxEtwEvent = 64 * 1024; + +//--------------------------------------------------------------------------------------- +// C++ copies of ETW structures +//--------------------------------------------------------------------------------------- + +// !!!!!!! NOTE !!!!!!!! +// The EventStruct* structs are described in the ETW manifest event templates, and the +// LAYOUT MUST MATCH THE MANIFEST EXACTLY! +// !!!!!!! NOTE !!!!!!!! + +#pragma pack(push, 1) + +struct EventStructGCBulkRootEdgeValue +{ + LPVOID RootedNodeAddress; + BYTE GCRootKind; + DWORD GCRootFlag; + LPVOID GCRootID; +}; + +struct EventStructGCBulkRootConditionalWeakTableElementEdgeValue +{ + LPVOID GCKeyNodeID; + LPVOID GCValueNodeID; + LPVOID GCRootID; +}; + +struct EventStructGCBulkNodeValue +{ + LPVOID Address; + ULONGLONG Size; + ULONGLONG TypeID; + ULONGLONG EdgeCount; +}; + +struct EventStructGCBulkEdgeValue +{ + LPVOID Value; + ULONG ReferencingFieldID; +}; + +struct EventStructGCBulkSurvivingObjectRangesValue +{ + LPVOID RangeBase; + ULONGLONG RangeLength; +}; + +struct EventStructGCBulkMovedObjectRangesValue +{ + LPVOID OldRangeBase; + LPVOID NewRangeBase; + ULONGLONG RangeLength; +}; + +struct EventStructStaticBulkFixedSizeData +{ + ULONGLONG TypeID; + ULONGLONG Address; + ULONGLONG Value; + ULONG Flags; +}; + +// This only contains the fixed-size data at the top of each struct in +// the bulk type event. These fields must still match exactly the initial +// fields of the struct described in the manifest. +struct EventStructBulkTypeFixedSizedData +{ + ULONGLONG TypeID; + ULONGLONG ModuleID; + ULONG TypeNameID; + ULONG Flags; + BYTE CorElementType; +}; + +struct EventStaticEntry +{ + ULONGLONG GCRootID; + ULONGLONG ObjectID; + ULONGLONG TypeID; + ULONG Flags; + wchar_t Name[0]; + + // Writes one EventStaticEntry to the buffer specified by ptr. Since we don't actually know how large the event will be, + // this write may fail if the remaining buffer is not large enough. This function returns the number of bytes written + // on success (return is >= 0), and -1 on failure. If we return -1, the caller is expected to flush the current buffer + // and try again. + static int WriteEntry(BYTE *ptr, int sizeRemaining, ULONGLONG addr, ULONGLONG obj, ULONGLONG typeId, ULONG flags, FieldDesc *fieldDesc) + { + WRAPPER_NO_CONTRACT; + + // sizeRemaining must be larger than the structure + 1 wchar for the struct and + // null terminator of Name. We will do a better bounds check when we know the + // size of the field name. + if (sizeRemaining < sizeof(EventStaticEntry) + sizeof(wchar_t)) + return -1; + + // The location in the structure to write to. We won't actually write here unless we have sufficient buffer. + wchar_t *name = (wchar_t *)(ptr + offsetof(EventStaticEntry, Name)); + int len = 0; + + LPCUTF8 utf8Name = 0; + if (SUCCEEDED(fieldDesc->GetName_NoThrow(&utf8Name))) + { + len = MultiByteToWideChar(CP_ACP, 0, utf8Name, -1, name, sizeRemaining - sizeof(EventStaticEntry)); + if (len <= 0) + { + // We will ignore corrupted/bad metadata here and only emit names for fields which are + // up to 255 characters (and also don't fit in the buffer). + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && sizeRemaining < 256) + return -1; // nothing written, insufficient buffer. Flush and try again. + + // If the name is larger than 255 or we have some other error converting the string, + // just emit an empty string. + len = 1; + name[0] = 0; + } + } + else + { + // Couldn't get the name for some reason, just emit an empty string. + len = 1; + name[0] = 0; + } + + // At this point we should have written something to the name buffer. + _ASSERTE(len > 0); + + // At this point we've written the field name (even if it's just an empty string). + // Write the rest of the fields to the buffer and return the total size. + EventStaticEntry *entry = (EventStaticEntry*)ptr; + entry->GCRootID = addr; + entry->ObjectID = obj; + entry->TypeID = typeId; + entry->Flags = flags; + + return sizeof(EventStaticEntry) + len * sizeof(wchar_t); + } +}; + +struct EventRCWEntry +{ + ULONGLONG ObjectID; + ULONGLONG TypeID; + ULONGLONG IUnk; + ULONGLONG VTable; + ULONG RefCount; + ULONG Flags; +}; + + +struct EventCCWEntry +{ + enum CCWFlags + { + Strong = 0x1, + Pegged = 0x2 + }; + + ULONGLONG RootID; + ULONGLONG ObjectID; + ULONGLONG TypeID; + ULONGLONG IUnk; + ULONG RefCount; + ULONG JupiterRefCount; + ULONG Flags; +}; + +#pragma pack(pop) + +// Represents one instance of the Value struct inside a single BulkType event +class BulkTypeValue +{ +public: + BulkTypeValue(); + void Clear(); + + // How many bytes will this BulkTypeValue take up when written into the actual ETW + // event? + int GetByteCountInEvent() + { + return + sizeof(fixedSizedData) + + sizeof(cTypeParameters) + +#ifdef FEATURE_REDHAWK + sizeof(WCHAR) + // No name in event, so just the null terminator + cTypeParameters * sizeof(ULONGLONG); // Type parameters +#else + (sName.GetCount() + 1) * sizeof(WCHAR) + // Size of name, including null terminator + rgTypeParameters.GetCount() * sizeof(ULONGLONG);// Type parameters +#endif + } + + EventStructBulkTypeFixedSizedData fixedSizedData; + + // Below are the remainder of each struct in the bulk type event (i.e., the + // variable-sized data). The var-sized fields are copied into the event individually + // (not directly), so they don't need to have the same layout as in the ETW manifest + + // This is really a denorm of the size already stored in rgTypeParameters, but we + // need a persistent place to stash this away so EventDataDescCreate & EventWrite + // have a reliable place to copy it from. This is filled in at the last minute, + // when sending the event. (On ProjectN, which doesn't have StackSArray, this is + // filled in earlier and used in more places.) + ULONG cTypeParameters; + +#ifdef FEATURE_REDHAWK + // If > 1 type parameter, this is an array of their EEType*'s + NewArrayHolder<ULONGLONG> rgTypeParameters; + + // If exactly one type parameter, this is its EEType*. (If != 1 type parameter, + // this is 0.) + ULONGLONG ullSingleTypeParameter; +#else // FEATURE_REDHAWK + StackSString sName; + StackSArray<ULONGLONG> rgTypeParameters; +#endif // FEATURE_REDHAWK +}; + +// Encapsulates all the type event batching we need to do. This is used by +// ETW::TypeSystemLog, which calls LogTypeAndParameters for each type to be logged. +// BulkTypeEventLogger will batch each type and its generic type parameters, and flush to +// ETW as necessary. ETW::TypeSystemLog also calls FireBulkTypeEvent directly to force a +// flush (e.g., once at end of GC heap traversal, or on each object allocation). +class BulkTypeEventLogger +{ +private: + + // Estimate of how many bytes we can squeeze in the event data for the value struct + // array. (Intentionally overestimate the size of the non-array parts to keep it safe.) + static const int kMaxBytesTypeValues = (cbMaxEtwEvent - 0x30); + + // Estimate of how many type value elements we can put into the struct array, while + // staying under the ETW event size limit. Note that this is impossible to calculate + // perfectly, since each element of the struct array has variable size. + // + // In addition to the byte-size limit per event, Windows always forces on us a + // max-number-of-descriptors per event, which in the case of BulkType, will kick in + // far sooner. There's a max number of 128 descriptors allowed per event. 2 are used + // for Count + ClrInstanceID. Then 4 per batched value. (Might actually be 3 if there + // are no type parameters to log, but let's overestimate at 4 per value). + static const int kMaxCountTypeValues = (128 - 2) / 4; + // Note: This results in a relatively small batch (about 31 types per event). We + // could increase this substantially by creating a single, contiguous buffer, which + // would let us max out the number of type values to batch by allowing the byte-size + // limit to kick in before the max-descriptor limit. We could esimate that as + // follows: + // + // static const int kMaxCountTypeValues = kMaxBytesTypeValues / + // (sizeof(EventStructBulkTypeFixedSizedData) + + // 200 * sizeof(WCHAR) + // Assume 199 + 1 terminating-NULL character in type name + // sizeof(UINT) + // Type parameter count + // 10 * sizeof(ULONGLONG)); // Assume 10 type parameters + // + // The downside, though, is that we would have to do a lot more copying to fill out + // that buffer before sending the event. It's unclear that increasing the batch size + // is enough of a win to offset all the extra buffer copying. So for now, we'll keep + // the batch size low and avoid extra copying. + + // How many types have we batched? + int m_nBulkTypeValueCount; + + // What is the byte size of all the types we've batched? + int m_nBulkTypeValueByteCount; + + // List of types we've batched. + BulkTypeValue m_rgBulkTypeValues[kMaxCountTypeValues]; + +#ifdef FEATURE_PAL + BYTE m_BulkTypeEventBuffer[65536]; +#endif + +#ifdef FEATURE_REDHAWK + int LogSingleType(EEType * pEEType); +#else + int LogSingleType(TypeHandle th); +#endif + +public: + BulkTypeEventLogger() : + m_nBulkTypeValueCount(0), + m_nBulkTypeValueByteCount(0) + { + LIMITED_METHOD_CONTRACT; + } + + void LogTypeAndParameters(ULONGLONG thAsAddr, ETW::TypeSystemLog::TypeLogBehavior typeLogBehavior); + void FireBulkTypeEvent(); +}; + + +// Does all logging for RCWs and CCWs in the process. We walk RCWs by enumerating all syncblocks in +// the process and seeing if they have associated interop information. We enumerate all CCWs in the +// process from the RefCount handles on the handle table. +class BulkComLogger +{ +public: + // If typeLogger is non-null, we will log out the types via the logger, otherwise no type + // information will be logged. + BulkComLogger(BulkTypeEventLogger *typeLogger); + ~BulkComLogger(); + + // Walks all RCW/CCW objects. + void LogAllComObjects(); + + // Forces a flush of all ETW events not yet fired. + void FireBulkComEvent(); + +private: + // Writes one RCW to the RCW buffer. May or may not fire the event. + void WriteRcw(RCW *rcw, Object *obj); + + // Writes one CCW to the CCW buffer. May or may not fire the event. + void WriteCcw(ComCallWrapper *ccw, Object **handle, Object *obj); + + // Forces a flush of all RCW ETW events not yet fired. + void FlushRcw(); + + // Forces a flush of all CCW ETW events not yet fired. + void FlushCcw(); + + // Callback used during handle table enumeration. + static void HandleWalkCallback(PTR_UNCHECKED_OBJECTREF pref, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2); + + // Used during CCW enumeration to keep track of all object handles which point to a CCW. + void AddCcwHandle(Object **handle); + +private: + struct CCWEnumerationEntry + { + CCWEnumerationEntry *Next; + int Count; + Object **Handles[64]; + + CCWEnumerationEntry() : Next(0), Count(0) + { + } + }; + +private: + // The maximum number of RCW/CCW events we can batch up based on the max size of an ETW event. + static const int kMaxRcwCount = (cbMaxEtwEvent - 0x30) / sizeof(EventRCWEntry); + static const int kMaxCcwCount = (cbMaxEtwEvent - 0x30) / sizeof(EventCCWEntry); + + int m_currRcw; // The current number of batched (but not emitted) RCW events. + int m_currCcw; // The current number of batched (but not emitted) CCW events. + + BulkTypeEventLogger *m_typeLogger; // Type logger to emit type data for. + + EventRCWEntry *m_etwRcwData; // RCW buffer. + EventCCWEntry *m_etwCcwData; // CCW buffer. + + CCWEnumerationEntry *m_enumResult; +}; + + +// Does bulk static variable ETW logging. +class BulkStaticsLogger +{ +public: + BulkStaticsLogger(BulkTypeEventLogger *typeLogger); + ~BulkStaticsLogger(); + + // Walk all static variables in the process and write them to the buffer, firing ETW events + // as we reach the max buffer size. + void LogAllStatics(); + + // Force a flush of the static data, firing an ETW event for any not yet written. + void FireBulkStaticsEvent(); + +private: + // Write a single static variable to the log. + void WriteEntry(AppDomain *domain, Object **address, Object *obj, FieldDesc *fieldDesc); + +private: + // The maximum bytes we can emit in the statics buffer. + static const int kMaxBytesValues = (cbMaxEtwEvent - 0x30); + + BYTE *m_buffer; // Buffer to queue up statics in + int m_used; // The amount of bytes used in m_buffer. + int m_count; // The number of statics currently written to m_buffer. + AppDomain *m_domain; // The current AppDomain m_buffer contains statics for. + BulkTypeEventLogger *m_typeLogger; // The type logger used to emit type data as we encounter it. +}; + + + +#endif // __EVENTTRACEPRIV_H__ + |