Geoff Chappell - Software Analyst
SKETCH OF HOW RESEARCH MIGHT CONTINUE AND RESULTS BE PRESENTED
An event provider calls this function to write an event. Like EtwEventWriteTransfer, this function allows for relating the event to others. It also can specify two other points of handling that are not supported through any other Event API function.
ULONG EtwEventWriteEx ( REGHANDLE RegHandle, EVENT_DESCRIPTOR const *EventDescriptor, ULONG64 Filter, ULONG Flags, GUID const *ActivityId, GUID const *RelatedActivityId, ULONG UserDataCount, EVENT_DATA_DESCRIPTOR *UserData);
The required RegHandle argument identifies which registration of an event provider is writing the event.
The required EventDescriptor argument tells which event is to be written.
The Filter argument specifies tracing sessions for which the event provider is enabled but which are not to receive this event.
The Flags argument allows for variation in the handling of the event. This argument is ignored before version 10.0.
The optional ActivityId argument tags the event with an identifier that other events may refer to as their related activity. This argument can be NULL to stand for whatever activity identifier is current set for the calling thread.
The optional RelatedActivityId argument tags the event as being related to the activity of some other event.
The optional UserDataCount and UserData arguments are respectively the element count and address of an array of structures that supply event-specific data to record with the event.
The function returns zero for success, else a Win32 error code.
The EtwEventWriteEx function is exported by name from NTDLL.DLL in version 6.1 and higher. Well-behaved software does not call this function but instead calls EventWriteEx, which is exported by name from ADVAPI32.DLL in version 6.1 and higher as a forward to EtwEventWriteEx.
As with many NTDLL exports, Microsoft does not document EtwEventWriteEx. The higher-level ADVAPI32 function EventWriteEx is documented.
A C-language declaration of EtwEventWriteEx is published by Microsoft in a file named NTETW.H in the Enterprise edition of the Windows Driver Kit (WDK) for Windows 10 Version 1511.
The EtwEventWriteEx function exists only as an embellishment of EtwEventWriteTransfer, specifically to add the Filter and Flags arguments. Note that it does not provide for the EventProperty argument, as does EtwEventWriteFull.
If EventDescriptor is NULL, the function has no event to write: it returns ERROR_INVALID_PARAMETER.
If the RegHandle is not valid, the function has no event provider to write an event from: it returns ERROR_INVALID_HANDLE. A valid REGHANDLE is produced by the EtwEventRegister function and is made invalid by EtwEventUnregister.
Event providers send events into the ETW mechanism. Tracing sessions receive them. Each tracing session chooses which event providers they want events from. For each event provider that they so enable, a tracing session may also specify a level and keyword to match against events from that provider. In later versions, they may also specify properties that affect the matching. If the Level and Keyword in at EventDescriptor match this specification, then the event is said to be enabled for the session.
If the event is enabled for any private tracing session, the function writes the event to the user-mode tracing buffers of each such session for which the event is enabled. Failure to write to any such session is failure for the function.
If the event is enabled for any tracing session that is managed by the kernel, the function sends the event to the kernel for distribution to each tracing session for which the event is enabled. Inputs and defaults are packaged as an EVENT_HEADER for NtTraceEvent with ETW_NT_FLAGS_TRACE_EVENT as its Flags argument. Failure from the kernel is failure for the function.
The background to the Filter is that when a tracing session enables an event provider, the tracing session can specify various kinds of filters for what events it receives. There are system-side filters which add to the old methods of filtering by level and keyword. Importantly, there are also provider-side filters. A tracing session may know of these through some private understanding of the provider, typically helped by descriptions in the provider’s instrumentation manifest. Presumably because of the manifest, provider-side filters are named schematized filters. For any given event that the provider might write, only the provider can determine whether the event meets the conditions of the filter. If the event fails the provider-side filtering for some tracing sessions but not for all (including because some tracing sessions have no such filter), the provider proceeds with writing the event but sets its Filter argument to tell the ETW machinery which tracing sessions to omit from delivery.
The general interpretation is that each of the Filter argument’s 64 bits may correspond to a tracing session that has enabled the provider. If a bit is set, then the corresponding tracing session, if any, does not receive the event even if the event is otherwise enabled for the session. The bit that corresponds to any one tracing session is communicated to the event provider when the tracing session enables its use of the provider. The event provider must have supplied a callback routine when registering. If this routine is called with its FilterData argument pointing to an EVENT_FILTER_DESCRIPTOR whose Type is EVENT_FILTER_TYPE_SCHEMATIZED, then its Ptr is the address of the schematized filter data, starting with an EVENT_FILTER_HEADER in which the InstanceId has the one set bit that would represent the tracing session in this function’s Filter argument.
Note that the caller of this function does not need to interpret the bits in Filter except to know that they are bits, one per tracing session, the correspondence being learnt as each tracing session enables the provider. The following few notes, then, are just implementation details. Though the Filter is 64 bits, only two sets of 16 matter: for sending to the kernel, just the lowest 16, i.e., bits 0 to 15; for private tracing sessions, only bits 32 to 47, and only then in version 6.3 and higher. That each set is allowed 16 bits is presumably for the future. For now, the kernel-mode and user-mode implementations allow at most eight and four tracing sessions, respectively.
The Flags are ignored before version 10.0. Even in version 10.0, the Flags are ignored for private tracing sessions. Microsoft’s EVNTPROV.H defines two flags:
|0x00000001||EVENT_WRITE_FLAG_NO_FAULTING||10.0 and higher|
|0x00000002||EVENT_WRITE_FLAG_INPRIVATE||10.0 and higher|
If EVENT_WRITE_FLAG_INPRIVATE is set, the event is not written to any tracing session that set EVENT_ENABLE_PROPERTY_EXCLUDE_INPRIVATE as an EnableProperty when enabling the provider. Note that Microsoft’s documentation of the property, in several places, leaves it “up to the process or event to designate itself as InPrivate for this to work.” Setting this bit in the Flags is how to designate this for the event, no matter that Microsoft’s documentation of EventWriteEx has it that Flags is “Reserved. Must be zero.” To learn this from Microsoft, do not heed the documentation but instead read EVNTPROV.H for its comments.