-
Notifications
You must be signed in to change notification settings - Fork 384
Description
Documentation Request
EventSource
does not mention that some built-in .NET types need to be mapped to the types expected by ETW. Presumably this has something to do with the reflection-generated manifests, but I haven't dug in to verify that.
To illustrate the issue, it is easy enough to modify some of the examples in the above link to end up with an event source like:
[EventSource(Name = nameof(CustomEventSource))]
internal class CustomEventSource : EventSource
{
private const int MyEventId = 123;
internal static readonly CustomEventSource Instance = new CustomEventSource();
[Event(MyEventId, Version = 1)]
public unsafe void MyEvent(int someInt, DateTime someDateTime, bool someBool)
{
Span<EventData> events = stackalloc EventData[3];
events[0].DataPointer = (IntPtr)(&someInt);
events[0].Size = sizeof(int);
events[1].DataPointer = (IntPtr)(&someDateTime);
events[1].Size = sizeof(DateTime);
events[2].DataPointer = (IntPtr)(&someBool);
events[2].Size = sizeof(bool);
fixed (EventData* eventsPtr = events)
{
WriteEventCore(MyEventId, events.Length, eventsPtr);
}
}
}
Which is subtly wrong as DateTime
and bool
do not map 1-to-1 to the format ETW expects - specifically DateTime
must be a FILETIME
and bool
must be mapped to an int
of 1 or 0. bool
is particular insidious, as the size mismatch between the passed and expected types can result in data corruption (for the following EventData
s) and potentially reads garbage data off the stack.
A corrected version of the above would be:
[EventSource(Name = nameof(CustomEventSource))]
internal class CustomEventSource : EventSource
{
private const int MyEventId = 123;
internal static readonly CustomEventSource Instance = new CustomEventSource();
[Event(MyEventId, Version = 1)]
public unsafe void MyEvent(int someInt, DateTime someDateTime, bool someBool)
{
Span<EventData> events = stackalloc EventData[3];
events[0].DataPointer = (IntPtr)(&someInt);
events[0].Size = sizeof(int);
var someDateTimeAsFileTime = someDateTime.ToFileTimeUtc();
events[1].DataPointer = (IntPtr)(&someDateTimeAsFileTime);
events[1].Size = sizeof(long);
var someBoolAsInt = someBool ? 1 : 0;
events[2].DataPointer = (IntPtr)(&someBoolAsInt);
events[2].Size = sizeof(int);
fixed (EventData* eventsPtr = events)
{
WriteEventCore(MyEventId, events.Length, eventsPtr);
}
}
}
Ideally, any built-in System.*
types that might reasonably be on an event should have an example of how to prepare them for EventData
. I don't know how involved that would be, so an acceptable alternative would be to just call out that some types need special handling and a link to the relevant ETW documentation (which might be this).
Previous documentation
There's the existing documentation of the EventSource
class which should probably be amended.
There's also ETW's input type remarks which are relevant, though the mapping from those types to .NET types is not always clear.