Windows ETW: при запуске StartTrace произошла ошибка 87 (ERROR_INVALID_PARAMETER)

Я вызываю Event Tracing для Windows StartTraceфункция:

StartTrace(sessionHandle, KERNEL_LOGGER_NAME, sessionProperties);

Ошибка с кодом ошибки 87 (ERROR_INVALID_PARAMETER). В MSDN приведены некоторые распространенные причины этой ошибки:

  • СвойстваNULL.
  • SessionHandle имеетзначение NULL.
  • Элементсвойств LogFileNameOffset недопустим.
  • Член LoggerNameOffset свойств недопустим.
  • Член LogFileMode в Properties указываетнедопустимую комбинацию флагов.
  • Членом Wnode.Guid является SystemTraceControlGuid, но параметром SessionName не являетсяKERNEL_LOGGER_NAME.

Код, по которому я звоню:

procedure StartKernelLogging;
var
    sessionProperties: PEVENT_TRACE_PROPERTIES;
    bufferSize: Int64;
    loggerName: AnsiString;
    logFilePath: AnsiString;
    th: TRACEHANDLE;
    hr: Cardinal;
begin
    {
        Allocate memory for the session properties. The memory must
        be large enough to include the log file name and session name,
        which get appended to the end of the session properties structure.
    }
    loggerName := KERNEL_LOGGER_NAME;
    logFilePath := 'C:\Users\Ian\foo.etl';

    bufferSize := sizeof(EVENT_TRACE_PROPERTIES)
            + Length(loggerName)+1
            + Length(logFilePath)+1;

    sessionProperties := AllocMem(bufferSize);

    ZeroMemory(sessionProperties, bufferSize);

    sessionProperties.Wnode.BufferSize := bufferSize;
    sessionProperties.Wnode.Flags := WNODE_FLAG_TRACED_GUID;
    sessionProperties.Wnode.ClientContext := 1; //QPC clock resolution
    sessionProperties.Wnode.Guid := SystemTraceControlGuid;
    sessionProperties.EnableFlags := EVENT_TRACE_FLAG_NETWORK_TCPIP;
    sessionProperties.LogFileMode := EVENT_TRACE_FILE_MODE_CIRCULAR;
    sessionProperties.MaximumFileSize := 5;  // 5 MB
    sessionProperties.LoggerNameOffset := sizeof(EVENT_TRACE_PROPERTIES);
    sessionProperties.LogFileNameOffset := sizeof(EVENT_TRACE_PROPERTIES) + Length(loggerName)+1;

    //Copy LoggerName to the offset address
    MoveMemory(Pointer(Cardinal(sessionProperties)+Cardinal(sessionProperties.LoggerNameOffset)), PAnsiChar(loggerName), Length(loggerName)+1);

    //Copy LogFilePath to the offset address
    MoveMemory(Pointer(Cardinal(sessionProperties)+Cardinal(sessionProperties.LogFileNameOffset)), PAnsiChar(logFilePath), Length(logFilePath)+1);

    th := 0;
    hr := EventTrace.StartTrace({var}th, PChar(loggerName), sessionProperties);
    if (hr <> ERROR_SUCCESS) then
    begin
        raise EWin32Error.Create(SysErrorMessage(hr));
    end;
end;

Языковая версия моего звонка:

ADVAPI32.StartTraceA(
     TraceHandle: 0x18F56C
     InstanceName: 0x44E840
     Properties: 0x243BD8);

гдеTraceHandleуказывает на 64-битное целое число:

0018F56C: 00 00 00 00  00 00 00 00

а такжеInstanceNameявляется указателем на строку ANSI с нулевым символом в конце:

0044E840: 4E 54 20 4B  65 72 6E 65  NT Kerne
0044E848: 6C 20 4C 6F  67 67 65 72  l Logger
0044E850: 00

а такжеPropertiesэто указатель наEVENT_TRACE_PROPERTIES структура, которую я воздержусь от воспроизведения полного шестнадцатеричного дампа

002A3BD8: 0000009A (154 bytes)

Важными значениями являются два смещения:

properties.LoggerNameOffset = 116  (i.e. $243BB8 + 116 = $243C4C)
properties.LogFileNameOffset = 133 (i.e. $243BD8 + 133 = $243C5D)

которые также содержат действительные строки ANSI с нулевым символом в конце, которые они должны:

"NT Kernel Logger":

$243C4C  4B20544E 656E7265  NT Kerne
$243C54  6F4C206C 72656767  l Logger
$243C5C  xxxxxx00           .

"C: \ Users \ Ян \foo.etl":

$243C5C  5C3A43xx 72657355  .C:\User
$243C64  61495C73 6F665C6E  s\Ian\fo
$243C6C  74652E6F xxxx006C  o.etc.

Почему мои параметры неверны?


Бонус Чтение

Обновить:

Параметры сеанса:

9A 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
AD 4A 81 9E 04 32 D2 11
9A 82 00 60 08 A8 69 39
01 00 00 00 00 00 02 00
00 00 00 00 00 00 00 00
00 00 00 00 05 00 00 00
02 00 00 00 00 00 00 00
00 00 01 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 85 00 00 00
74 00 00 00 

Который распадается на:

000| Wnode.BufferSize:          9A 00 00 00 (154)
004| Wnode.ProviderID:          00 00 00 00
008| Wnode.Version:             00 00 00 00
012| Wnode.Linkage:             00 00 00 00
016| Wnode.Timestamp:           00 00 00 00 00 00 00 00
024| Wnode.Guid:                AD 4A 81 9E 04 32 D2 11 9A 82 00 60 08 A8 69 39 (SystemTraceControlGuid)
040| Wnode.ClientContext:       01 00 00 00 (1)
044| Wnode.Flags:               00 00 02 00 (WNODE_FLAG_TRACED_GUID)
048| BufferSize:          00 00 00 00 
052| MinimumBuffers:      00 00 00 00
056| MaximumBuffers:      00 00 00 00 
060| MaximumFileSize:     05 00 00 00 (5 MB)
064| LogFileMode:         02 00 00 00 (EVENT_TRACE_FILE_MODE_CIRCULAR)
068| FlushTimer:          00 00 00 00
072| EnableFlags:         00 00 01 00 (EVENT_TRACE_FLAG_NETWORK_TCPIP)
076| AgeLimit:            00 00 00 00
080| NumberOfBuffers:     00 00 00 00 
084| FreeBuffers:         00 00 00 00
088| EventsLost:          00 00 00 00 
092| BuffersWritten:      00 00 00 00
096| LogBuffersLost:      00 00 00 00 
100| RealTimeBuffersLost: 00 00 00 00
104| LoggerThreadId:      00 00 00 00 
108| LogFileNameOffset:   85 00 00 00 (133)
112| LoggerNameOffset:    74 00 00 00 (116)
116| NT Kernel Logger\0
133| C:\Users\Ian\foo.etl\0
154|

Если есть проблема с выравниванием, я не смогу обнаружить ее в одиночку.


Структура:

EVENT_TRACE_PROPERTIES = packed record
   Wnode : WNODE_HEADER;

   // data provided by caller
   BufferSize : Longword;              // buffer size for logging (kbytes)
   MinimumBuffers : Longword;          // minimum to preallocate
   MaximumBuffers : Longword;          // maximum buffers allowed
   MaximumFileSize : Longword;         // maximum logfile size (in MBytes)
   LogFileMode : Longword;             // sequential, circular
   FlushTimer : Longword;              // buffer flush timer, in seconds
   EnableFlags :Longword;              // trace enable flags
   AgeLimit : Longint;                // age decay time, in minutes

   // data returned to caller
   NumberOfBuffers : Longword;         // no of buffers in use
   FreeBuffers : Longword;             // no of buffers free
   EventsLost : Longword;              // event records lost
   BuffersWritten : Longword;          // no of buffers written to file
   LogBuffersLost : Longword;          // no of logfile write failures
   RealTimeBuffersLost : Longword;     // no of rt delivery failures
   LoggerThreadId : HANDLE;            // thread id of Logger
   LogFileNameOffset : Longword;        // Offset to LogFileName
   LoggerNameOffset : Longword;         // Offset to LoggerName
end;

вместе с:

WNODE_HEADER = packed record
   BufferSize : Longword;
   ProviderId : Longword;
   Version : Longword;
   Linkage : Longword;
   TimeStamp : Int64;
   Guid : TGUID;
   ClientContext : Longword;
   Flags : Longword;
end;

2 ответа

Решение

О, я вижу, что говорят комментарии из комментариев Люка.

Дело не в том, что структура смещена каким-либо образом. Содержание после структуры должно быть 8-byte выровнены. Другими словами:

000| Wnode.BufferSize:          9A 00 00 00 (154)
004| Wnode.ProviderID:          00 00 00 00
     ...snip...
108| LogFileNameOffset:   85 00 00 00 (133)
112| LoggerNameOffset:    74 00 00 00 (116)
116| 00 00 00 00 (4 bytes padding)
120| NT Kernel Logger\0
136| 00 00 00 00 00 00 00 (7 bytes padding)
144| C:\Users\Ian\foo.etl\0

Похоже, данные должны быть выровнены по 8-byte границы в Windows (7 (Professional (64-разрядная версия)))

Чтобы помочь дополнению, я написал Pad функция, которая округляет число до ближайшего кратного 8:

function Pad(length: Cardinal): Cardinal;
var
    m: Integer;
const
    DataAlignment = 8; //align data on 8-byte boundaries
begin
    Result := length;

    m := length mod DataAlignment;
    if (m > 0) then
        Result := result + DataAlignment-m;
end;

Затем я изменил часть кода из исходного вопроса, чтобы использовать его.

  • подсчитать сумму buffserSize требуется:

    loggerName := KERNEL_LOGGER_NAME;
    logFilePath := 'C:\Users\Ian\foo.etl';
    
    bufferSize := sizeof(EVENT_TRACE_PROPERTIES)
            + Pad(Length(loggerName)+1)
            + Pad(Length(logFilePath)+1);
    
  • тогда мне нужно переместить мои смещения на 8-байтовую границу:

    sessionProperties.LoggerNameOffset := Pad(sizeof(EVENT_TRACE_PROPERTIES));
    sessionProperties.LogFileNameOffset := Pad(sizeof(EVENT_TRACE_PROPERTIES)) + Pad(Length(loggerName)+1);
    
  • и пока я копирую строки в смещения, объявленные в структуре, я в порядке:

    //Copy LoggerName to the offset address
    MoveMemory(
          Pointer(Cardinal(sessionProperties)+sessionProperties.LoggerNameOffset),
          PAnsiChar(loggerName), Length(loggerName)+1);
    
    //Copy LogFilePath to the offset address
    MoveMemory(
          Pointer(Cardinal(sessionProperties)+sessionProperties.LogFileNameOffset),
          PAnsiChar(logFilePath), Length(logFilePath)+1);
    

И Блинго-Бланго, это работает.

Примечание. Любой код публикуется в открытом доступе. Атрибуция не требуется.

Не ответ, но кое-что интересное я нашел: Если вы замените строку:

hr := EventTrace.StartTrace({var}th, PChar(loggerName), sessionProperties);

с

hr := EventTrace.StartTrace({var}th, KERNEL_LOGGER_NAME, sessionProperties);

затем код ошибки становится ERROR_BAD_LENGTH (24). Я думаю, что я использую другой набор модулей для API-интерфейсов трассировки, и в моем случае StartTrace хочет PWideChar, но loggerName является AnsiString. Поскольку я не знаю, как выглядит ваше устройство EventTrace, трудно сказать, так ли это для вас или нет. И, тем не менее, это не делает вызов StartTrace() успешным.

Другие вопросы по тегам