Geoff Chappell, Software Analyst
When given EtwActivityIdCreate (0x0C) as its FunctionCode argument, the NtTraceControl function creates an activity identifier such as may be associated with some occurrence of an event to help track relationships between occurrences. This note deals only with the function’s behaviour that is specific to this function code. The function’s general behaviour is here taken as assumed knowledge.
Well-behaved user-mode software does not call NtTraceControl. The documented user-mode API for reaching this functionality is EventActivityIdControl, which is exported by name from ADVAPI32 in version 6.0 and higher, though only ever as a forward to the undocumented NTDLL function EtwEventActivityIdControl. These higher-level functions vary their behaviour according to a ControlCode argument. Two cases can create an activity identifier: EVENT_ACTIVITY_CTRL_CREATE_ID (3) and EVENT_ACTIVITY_CTRL_CREATE_SET_ID (5). This creation is done in kernel mode through NtTraceControl.
Kernel-mode software also does not call NtTraceControl to create an activity identifier. The documented kernel-mode API is more efficient. It is EtwActivityIdControl, which is exported by name from the kernel, also in version 6.0 and higher. This function takes the same ControlCode argument. In the two cases that create an activity identifier, the function cuts through to the same internal routine that NtTraceControl would in kernel mode and does from user mode.
That said, the kernel itself calls ZwTraceControl to create an activity identifier. It does this for the undocumented EtwWriteStartScenario function if the activity identifier that is given as the ActivityId argument is all zeroes.
Since EtwActivityIdCreate is known only as an argument for an undocumented function, it should not surprise that it too is undocumented. Microsoft’s name for it has been published in anything like plain text only in the NTETW.H from the Enterprise WDK for Windows 10 version 1511—specifically this one edition, its publication being very plausibly an oversight. Otherwise it is known from type information in symbol files that Microsoft has published for a few user-mode DLLs in Windows 8 and higher.
The output buffer is to receive a GUID. If the output buffer is not exactly 0x10 bytes, the function returns STATUS_INVALID_PARAMETER. Versions 6.0 to 6.1 also require that the input buffer be exactly 0x10 bytes. This is here guessed as following the design of the EventActivityIdControl function, which can have a GUID as input, output or both.
The whole of the actual work is to generate a suitable GUID as the output. Suitable does not mean random. Presumably because of the role in linking events to track progress from one activity to another, some attempt is made to create GUIDs in sequence. The implementation keeps a separate sequence for each processor. Each KPRCB has as its EtwSupport member a pointer to per-processor storage just to support Event Tracing for Windows (ETW). Microsoft’s name for this storage as a structure is not known. Within this storage is the next activity identifier to allocate. The identifier is treated in two parts: the first eight bytes are stable; the second are incremented at each allocation.
The creation of an activity identifier cannot itself fail. It cannot fail even indirectly in versions 6.0 to 6.1 since these create the activity identifier in the function’s double buffer which is known to be good. In version 6.2 and higher, this case of the function is exempted from double-buffering. The activity identifier is created directly in the caller-supplied output buffer and can fail indirectly if the output buffer, though tested on entry to the function, is no longer good. Curiously, when adding the necessary exception handling, Microsoft’s programmers lost the line that records how much has been placed in the output buffer. The variable that the caller provided for learning the ReturnSize is set to zero instead of 0x10. To a caller who bothers looking, which apparently none do since this oversight persists at least to the 1803 edition of Windows 10, the function succeeds but says it produced no output.