Geoff Chappell, Software Analyst
SKETCH OF HOW RESEARCH MIGHT CONTINUE AND RESULTS BE PRESENTED
This function starts an Event Tracing for Windows (ETW) tracing session, also known informally as an event logger.
ULONG StartTrace ( TRACEHANDLE *SessionHandle, LPCTSTR InstanceName, EVENT_TRACE_PROPERTIES *Properties);
The SessionHandle argument is the address of an 8-byte variable that is to receive a handle to the started session.
The InstanceName argument is the address of a null-terminated case-insensitive string that names the session.
The Properties argument is the address of a buffer that is used for both input and output to describe the session. On input, it specifies what properties are wanted for the session. The successful function updates the buffer to show what properties are accepted for the session.
The function returns zero for success, else an error code (which is also set as the thread’s last Win32 error).
The StartTrace function is exported by name from ADVAPI32.DLL in both ANSI and Unicode variants, StartTraceA and StartTraceW, in version 5.0 and higher.
As with many ETW functions, the StartTrace implementation has moved. It is natively in ADVAPI32 only in versions 5.0 to 5.1 and again in 6.0 to 6.1. In version 5.2 only, the export from ADVAPI32 is merely a forward to the NTDLL function EtwStartTrace, again in ANSI and Unicode variants. In version 6.2 and higher, the ADVAPI32 implementations are merely stubs for jumping to or calling the true implementations elsewhere: KERNELBASE.DLL in version 6.2, but SECHOST.DLL in versions 6.3 and higher; StartTraceA as a direct import but StartTraceW via the API Set api-ms-win-eventing-controller-l1-1-0.dll.
The StartTrace function has always been documented. A curiosity is that for nearly two decades, the documentation used SessionName for the argument that the declaration in EVNTRACE.H has always had as InstanceName.
Broadly speaking, the successful function proceeds in distinct stages:
All three arguments are required. The returned error code is ERROR_INVALID_PARAMETER if either of SessionHandle or Properties is NULL, but ERROR_INVALID_NAME if InstanceName is NULL. Note that although Microsoft’s documentation has long gone to unusual trouble to list “some common errors”, it somehow misses this second possibility.
The buffer at Properties begins with an EVENT_TRACE_PROPERTIES structure which directly specifies many properties for the session but which is in general a fixed-size header to be followed by variable-size properties. The total size in bytes must be supplied as the BufferSize in the Wnode. If this is not large enough for at least the EVENT_TRACE_PROPERTIES structure, the function fails, returning ERROR_BAD_LENGTH.
The variable-size properties on input can be:
On the function’s success, the variable-size output can be:
All are optional. The original two are indicated by obviously named members of the EVENT_TRACE_PROPERTIES. A non-zero LogFileNameOffset on input is the offset in bytes from Properties to the filename that the tracing session is to log to. If only in principle (see below), a non-zero LoggerNameOffset on input is the offset from Properties to where the function is to place the tracing session’s name. It is an error (ERROR_INVALID_PARAMETER) if either offset is non-zero but would place the corresponding name to start inside the EVENT_TRACE_PROPERTIES or beyond the buffer.
Every tracing session has a GUID and a name. The GUID can be specified in the Guid member of the Wnode. The name is supplied by the InstanceName argument.
If the session name is either of the reserved names in the following table, then the Guid in the Wnode is forced to the corresponding GUID. Conversely, if the Guid is either of these but the InstanceName is not the corresponding reserved name, the function returns ERROR_INVALID_PARAMETER. Comparison is case-sensitive before version 6.0 but case-insensitive since.
Session Name | GUID | Versions |
---|---|---|
NT Kernel Logger | {9E814AAD-3204-11D2-9A82-006008A86939} | 5.1 and higher |
Circular Kernel Context Logger | {54DEA73A-ED1F-42A4-AF71-3E63D056F174} | 6.0 and higher |
If the Properties name a log file, i.e., if LogFileNameOffset is non-zero, then the function attempts to obtain the full pathname to use instead of the given name. Failure to get the full pathname is not of itself an error, but if sufficient memory cannot be found for the attempt, the function fails, returning ERROR_NOT_ENOUGH_MEMORY in version 5.2 and higher (but ERROR_OUTOFMEMORY before).
Whether the function obtains the full pathname or persists with the filename as given, if the name is empty or is too long, then (except for the point in the next paragraph) the function proceeds as if no log file is named. Too long means more than 65,536 characters.
Even if the function obtains the full pathname or rejects the name as too long, it requires that the Properties buffer continues far enough to hold the log file name as given and to be capable of receiving a copy of the InstanceName. Otherwise, the function returns ERROR_BAD_LENGTH.
If the InstanceName is empty or too long, the function fails, returning ERROR_INVALID_NAME.
There are numerous constraints on the LogFileMode, sometimes in conjunction with other input, most notably to require that a log file is named or that MaximumFileSize is non-zero.
The function requires at least one of the following, else it fails, returning ERROR_BAD_PATHNAME:
From the error code, the thinking may be surmised as: a usable log file name (see above) is required except in real-time or buffering mode. As may be seen below, the converse is not true: even in real-time or buffering mode, a log file name may be required because some other mode is set too.
For other invalid combinations, as listed next, the returned error code is ERROR_INVALID_PARAMETER. Be aware that the kernel or NTDLL, should the function proceed that far, impose yet more constraints. What StartTrace aims for is apparently just to reject the main inconsistencies.
Numerical Value | Symbolic Name | Requirements | Versions |
---|---|---|---|
0x00000001 | EVENT_TRACE_FILE_MODE_SEQUENTIAL | ||
0x00000002 | EVENT_TRACE_FILE_MODE_CIRCULAR | invalid with EVENT_TRACE_FILE_MODE_APPEND;
invalid with EVENT_TRACE_FILE_MODE_NEWFILE; invalid with EVENT_TRACE_RELOG_MODE |
5.1 and higher |
requires non-zero MaximumFileSize | 5.2 and higher | ||
0x00000004 | EVENT_TRACE_FILE_MODE_APPEND | invalid with EVENT_TRACE_FILE_MODE_CIRCULAR;
invalid with EVENT_TRACE_REAL_TIME_MODE; invalid with EVENT_TRACE_RELOG_MODE |
5.1 and higher |
0x00000008 | EVENT_TRACE_FILE_MODE_NEWFILE | invalid with EVENT_TRACE_FILE_MODE_CIRCULAR;
invalid with EVENT_TRACE_FILE_MODE_PREALLOCATE; invalid with EVENT_TRACE_RELOG_MODE; requires non-zero MaximumFileSize; requires log file name |
5.1 and higher |
invalid with EVENT_TRACE_PRIVATE_LOGGER_MODE | 5.2 to 6.0 | ||
invalid for NT Kernel Logger | 5.2 and higher | ||
0x00000010 | EVENT_TRACE_USE_MS_FLUSH_TIMER | ||
0x00000020 | EVENT_TRACE_FILE_MODE_PREALLOCATE | requires non-zero MaximumFileSize; invalid with EVENT_TRACE_FILE_MODE_NEWFILE; requires log file name |
5.1 and higher |
invalid with EVENT_TRACE_PRIVATE_LOGGER_MODE | 5.2 only | ||
0x00000040 | EVENT_TRACE_NONSTOPPABLE_MODE | invalid | 6.0 and higher |
0x00000080 | EVENT_TRACE_SECURE_MODE | ||
0x00000100 | EVENT_TRACE_REAL_TIME_MODE | invalid with EVENT_TRACE_PRIVATE_LOGGER_MODE | 5.0 and higher |
invalid with EVENT_TRACE_FILE_MODE_APPEND | 5.1 and higher | ||
0x00000200 | EVENT_TRACE_DELAY_OPEN_FILE_MODE | ||
0x00000400 | EVENT_TRACE_BUFFERING_MODE | ||
0x00000800 | EVENT_TRACE_PRIVATE_LOGGER_MODE | invalid with EVENT_TRACE_REAL_TIME_MODE | 5.0 and higher |
required by EVENT_TRACE_RELOG_MODE | 5.1 and higher | ||
invalid with EVENT_TRACE_FILE_MODE_PREALLOCATE | 5.2 only | ||
invalid with EVENT_TRACE_FILE_MODE_NEWFILE | 5.2 to 6.0 | ||
required by EVENT_TRACE_PRIVATE_IN_PROC | 6.0 and higher | ||
required by EVENT_TRACE_INDEPENDENT_SESSION_MODE | 6.3 and higher | ||
0x00001000 | EVENT_TRACE_ADD_HEADER_MODE | invalid with EVENT_TRACE_REAL_TIME_MODE | 5.0 and higher |
0x00002000 | EVENT_TRACE_USE_KBYTES_FOR_SIZE | requires non-zero MaximumFileSize; requires log file name |
5.2 and higher |
0x00004000 | EVENT_TRACE_USE_GLOBAL_SEQUENCE | ||
0x00008000 | EVENT_TRACE_USE_LOCAL_SEQUENCE | ||
0x00010000 | EVENT_TRACE_RELOG_MODE | invalid with EVENT_TRACE_FILE_MODE_CIRCULAR;
invalid with EVENT_TRACE_FILE_MODE_APPEND; invalid with EVENT_TRACE_FILE_MODE_NEWFILE; requires EVENT_TRACE_PRIVATE_LOGGER_MODE |
5.1 and higher |
0x00020000 | EVENT_TRACE_PRIVATE_IN_PROC | requires EVENT_TRACE_PRIVATE_LOGGER_MODE | 6.0 and higher |
0x00040000 | EVENT_TRACE_BUFFER_INTERFACE_MODE | ||
0x00080000 | EVENT_TRACE_KD_FILTER_MODE | ||
0x00100000 | EVENT_TRACE_REAL_TIME_RELOG_MODE | ||
0x00200000 | EVENT_TRACE_LOST_EVENTS_DEBUG_MODE | ||
0x00400000 | EVENT_TRACE_STOP_ON_HYBRID_SHUTDOWN | ||
0x00800000 | EVENT_TRACE_PERSIST_ON_HYBRID_SHUTDOWN | ||
0x01000000 | EVENT_TRACE_USE_PAGED_MEMORY | ||
0x02000000 | EVENT_TRACE_SYSTEM_LOGGER_MODE | ||
0x04000000 | EVENT_TRACE_COMPRESSED_MODE | ||
0x08000000 | EVENT_TRACE_INDEPENDENT_SESSION_MODE | requires EVENT_TRACE_PRIVATE_LOGGER_MODE | 6.3 and higher |
0x10000000 | EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING | ||
0x20000000 | EVENT_TRACE_BLOCKING_MODE | ||
0x40000000 | apparently unused | ||
0x80000000 | EVENT_TRACE_ADDTO_TRIAGE_DUMP |
A set EVENT_TRACE_FILE_MODE_NEWFILE means that the log file’s name is only the basis from which the implementation is to generate new names for new log files. In version 5.2 and higher, the name (as given or as the full pathname) must contain exactly one percent sign which is followed immediately by a lower-case d to make a %d placeholder in the style of the printf function from the C Run-Time. Otherwise, the function fails, returning ERROR_INVALID_NAME.
Version 5.1 also requires a numerical placeholder in this case but is less particular. It rejects the log file name only if a trial resolution of 1 for the placeholder does not change the name. Though this flexibility soon was lost from the code, it remains in Microsoft’s documentation of Logging Mode Constants, which to this day might easily be read as suggesting %d only “for example”.
Except on one point, the EnableFlags in the EVENT_TRACE_PROPERTIES are no direct concern to the StartTrace function. They are meaningful to the kernel only if the tracing session is the NT Kernel Logger or, in version 6.2 and higher, has EVENT_TRACE_SYSTEM_LOGGER_MODE in the LogFileMode. They then select broad categories of events that the tracing session seeks from the system trace provider.
The exception applies in version 5.1 and higher if the highest bit of the EnableFlags is set. That Microsoft names this bit EVENT_TRACE_FLAG_EXTENSION has been known from EVNTRACE.H since at least 1999, but it has otherwise been left undocumented for two decades. Its effect is that the 32-bit EnableFlags is re-interpreted as a 4-byte TRACE_ENABLE_FLAG_EXTENSION structure. The low 16 bits are an Offset in bytes from the Properties to the extension. The next 8 bits are the extension’s Length in dwords.
Originally, the extension is opaque to the function but version 6.0 extended the extensibility to allow for multiple items which are each opaque. This is indicated when the Length is 0xFF. The extension then begins as a 4-byte TRACE_ENABLE_FLAG_EXT_HEADER that supplies the extension’s true Length in dwords (including the header just mentioned) and the count of Items that follow. Each item is its own 4-byte TRACE_ENABLE_FLAG_EXT_ITEM as a header to introduce the item’s opaque contents. The item’s header supplies as Offset and a Type. Perhaps confusingly, this Offset is the item’s length in dwords (including the header).
Given that the EVENT_TRACE_FLAG_EXTENSION bit is set in the EnableFlags, the function aims to enforce that a properly formed extension is supplied within the Properties buffer after the EVENT_TRACE_PROPERTIES header, else to return ERROR_INVALID_PARAMETER. The following are required always:
If Length is not 0xFF, then this last requirement is simply for Length dwords.
If Length is 0xFF, then this space in the Properties buffer must be large enough for a 4-byte TRACE_ENABLE_FLAG_EXT_HEADER
TO BE DONE?