我正在尝试使用 OpenTrace 和 ProcessTrace 来读取
.etl
文件的事件。
TRACEHANDLE
ERROR_SUCCESS
EVENT_CALLBACK
回调函数我知道这是一个有效的
.etl
文件,因为我可以在以下位置打开它:
Microsoft 提供了读取 .etl
文件事件的
示例代码。基本要点是:
用我们要打开的文件名和回调函数的地址初始化一个
EVENT_TRACE_LOGFILE
结构。文件中的每个事件都会调用一次回调函数:
var
filename: UnicodeString;
trace: EVENT_TRACE_LOGFILEW;
filename := 'C:\test.etl';
//Initialize trace with filename and callback
ZeroMemory(@trace, sizeof(trace));
trace.LogFileName := PWideChar(filename);
trace.EventCallback := ProcessTraceEventCallback; //called once for each event in the file
调用OpenTrace打开指定的
.etl
文件,并获取会话的TRACEHANDLE
:
//Open the trace
var
hTrace: TRACEHANDLE;
hTrace := OpenTraceW(trace);
if (hTrace = INVALID_PROCESSTRACE_HANDLE) then
RaiseLastOSError;
调用 ProcessTrace 开始从文件读取事件。我们的回调将为文件中的每个事件调用一次,并且在读取所有事件之前ProcessTrace不会返回:
var
res: Cardinal;
res := ProcessTrace(@hTrace, 1, nil, nil);
if (res <> ERROR_SUCCESS) then
RaiseLastOSError;
应该就是这样,除了回调从未被调用:
procedure ProcessTraceEventCallback(pEvent: PEVENT_TRACE); stdcall;
begin
//TODO: read the event information out of pEvent
//pEvent.MofData ==> raw payload information
//pEvent.MofLength ==> raw payload length (bytes)
WriteLn('Got an event');
Winapi.Windows.DebugBreak;
end;
所有代码都不会返回错误。
ERROR_SUCCESS
我的示例代码和下面的CRME,都强制使用W Unicode 版本的函数(只是为了消除混乱)。我确信我的标头翻译、记录打包、对齐或调用约定有问题 - 但我找不到任何东西。
我知道这看起来并不小。但我们需要所有 API 函数和结构。
您将需要自己的
C:\test.etl
文件。您可以使用 Windows Performance Recorder 创建一个文件,或者在您的 PC 中搜索 .etl
文件 - Windows 和 Microsoft 创建了很多分散在各处的文件。
program Project4;
{$APPTYPE CONSOLE}
{$ALIGN 8} //Windows ABI
{$R *.res}
uses
System.SysUtils,
Winapi.Windows;
type
EVENT_TRACE_HEADER = packed record
Size: Word; // Size of entire record
FieldTypeFlags: Word; // Reserved
EventType: Byte; //Type of event. A provider can define their own event types or use the predefined event types (Info, Start,Stop, Suspend, Checkpoint, Resume, etc.)
EventLevel: Byte; //Provider-defined value that defines the severity level used to generate the event. Verbose, Information, Warning, Error, Critical, etc
EventVersion: Word; //Which version of MOF class to use to decipher the event data. Specify zero if there is only one version of your event trace class.
ThreadId: LongWord; //On output, identifies the thread that generated the event.
ProcessId: LongWord; //On output, identifies the process that generated the event.
TimeStamp: Int64; //The time that the event occurred. On output, contains the time that the event occurred.
Guid: TGUID; //Guid that identifies event. Event trace class GUID. You can use the class GUID to identify a category of events and the Class.Type member to identify an event within the category of events.
ClientContext: LongWord; //Reserved
Flags: LongWord; //
end;
PEVENT_TRACE_HEADER = ^EVENT_TRACE_HEADER;
EVENT_TRACE = packed record
Header: EVENT_TRACE_HEADER; // Event trace header
InstanceId: Cardinal; // Instance identifier. Contains valid data when the provider calls the TraceEventInstance function to generate the event. Otherwise, the value is zero.
ParentInstanceId: Cardinal; // Instance identifier for a parent event. Contains valid data when the provider calls the TraceEventInstance function to generate the event. Otherwise, the value is zero.
ParentGuid: TGUID; // Class GUID of the parent event. Contains valid data when the provider calls the TraceEventInstance function to generate the event. Otherwise, the value is zero.
MofData: Pointer; // Pointer to the beginning of the event-specific data for this event.
MofLength: Cardinal; // Number of bytes to which MofData points.
BufferContext: Cardinal; // Provides information about the event such as the session identifier and processor number of the CPU on which the provider process ran.
end;
PEVENT_TRACE = ^EVENT_TRACE;
TRACE_LOGFILE_HEADER = record
BufferSize: LongWord;
MajorVersion: Byte;
MinorVersion: Byte;
SubVersion: Byte;
SubMinorVersion: Byte;
ProviderVersion: LongWord; // defaults to NT version
NumberOfProcessors: LongWord; // Number of Processors
EndTime: LARGE_INTEGER; // Time when logger stops
TimerResolution: LongWord;
MaximumFileSize: LongWord; // Maximum in Mbytes
LogFileMode: LongWord; // specify logfile mode
BuffersWritten: LongWord; // used to file start of Circular File
StartBuffers: Cardinal; // Reserved
PointerSize: Cardinal; // Size of a Pointer data type, in bytes.
EventsLost: Cardinal; // Number of events lost during the event tracing session. Events may be lost due to insufficient memory or a very high rate of incoming events.
CpuSpeedInMHz: Cardinal; // CPU speed, in megahertz.
LoggerName: PWideChar; // Do not use.
LogFileName: PWideChar; // Do not use
TimeZone: TIME_ZONE_INFORMATION; // A TIME_ZONE_INFORMATION structure that contains the time zone for the BootTime, EndTime and StartTime members.
BootTime: Int64; // Time at which the system was started, in 100-nanosecond intervals since midnight, January 1, 1601. BootTime is supported only for traces written to the Global Logger session.
PerfFreq: Int64; // Frequency of the high-resolution performance counter, if one exists.
StartTime: Int64; // Time at which the event tracing session started, in 100-nanosecond intervals since midnight, January 1, 1601.
ReservedFlags: Cardinal;
BuffersLost: Cardinal; // Total number of buffers lost during the event tracing session.
end;
PTRACE_LOGFILE_HEADER = ^TRACE_LOGFILE_HEADER;
PEVENT_TRACE_LOGFILEW = ^EVENT_TRACE_LOGFILEW; //forward
EVENT_TRACE_BUFFER_CALLBACKW = function (Logfile : PEVENT_TRACE_LOGFILEW) : Cardinal; stdcall;
PEVENT_TRACE_BUFFER_CALLBACKW = EVENT_TRACE_BUFFER_CALLBACKW;
//Legacy EventCallback callback
EVENT_CALLBACK = procedure (pEvent: PEVENT_TRACE); stdcall;
PEVENT_CALLBACK = EVENT_CALLBACK;
//Newer EventCallback if ProcessTraceMode of PROCESS_TRACE_MODE_EVENT_RECORD is specified
// EVENT_RECORD_CALLBACK = procedure (EventRecord: PEVENT_RECORD); stdcall;
// PEVENT_RECORD_CALLBACK = EVENT_RECORD_CALLBACK;
EVENT_TRACE_LOGFILEW = packed record
LogFileName: PWideChar; // Logfile Name
LoggerName: PWideChar; // LoggerName
CurrentTime: Int64; // timestamp of last event
BuffersRead: Cardinal; // buffers read to date
ProcessTraceMode: Cardinal;
CurrentEvent: EVENT_TRACE; // Current Event from this stream. (84 bytes)
LogfileHeader: TRACE_LOGFILE_HEADER; // logfile header structure (272 bytes)
BufferCallback: PEVENT_TRACE_BUFFER_CALLBACKW; // callback before each buffer is read
// following variables are filled for BufferCallback.
BufferSize: Cardinal; //On output, contains the size of each buffer, in bytes
Filled: Cardinal; //On output, contains the number of bytes in the buffer that contain valid information
EventsLost: Cardinal; //Not used
// following needs to be propaged to each buffer
EventCallback: PEVENT_CALLBACK; //or EventRecordCallback: PEvent_Record_Callback if flag is on
IsKernelTrace: Cardinal; // TRUE for kernel logfile
Context: Pointer; // reserved for internal use
end;
TRACEHANDLE = UInt64;
PTRACEHANDLE = ^TRACEHANDLE;
function OpenTraceW(var Logfile : EVENT_TRACE_LOGFILEW): Cardinal; stdcall; external advapi32 name 'OpenTraceW';
function ProcessTrace(HandleArray: PTRACEHANDLE; HandleCount: Cardinal; StartTime: PFileTime; EndTime: PFileTime): Cardinal; stdcall; external advapi32 name 'ProcessTrace';
function CloseTrace(ATraceHandle: TRACEHANDLE): Cardinal; stdcall; external advapi32 name 'CloseTrace';
const
INVALID_PROCESSTRACE_HANDLE: TRACEHANDLE = INVALID_HANDLE_VALUE; //i.e. TRACEHANDLE($00000000FFFFFFFF);
procedure ProcessTraceEventCallback(pEvent: PEVENT_TRACE); stdcall;
begin
{
Our callback, called once for each event in the file
}
WriteLn('Got an event');
Winapi.Windows.DebugBreak;
end;
function BufferCallback(logFile: PEVENT_TRACE_LOGFILEW): LongWord; stdcall;
begin
WriteLn('Finished processing a buffer-full of events. Buffercallback says '+IntToStr(logfile.EventsLost)+' events lost');
Result := 1; // Return TRUE to continue processing more events. Otherwise FALSE
end;
procedure Main;
var
trace: EVENT_TRACE_LOGFILEW;
th: TRACEHANDLE;
filename: UnicodeString;
res: Cardinal;
begin
filename := 'C:\test.etl';
WriteLn('Filename: '+filename);
{
Example code of how to read from a .etl file:
Using TdhFormatProperty to Consume Event Data
https://learn.microsoft.com/en-us/windows/win32/etw/using-tdhformatproperty-to-consume-event-data
}
//Initialize EVENT_TRACE_LOGFILE with filename to open, and our callback function
WriteLn('Initializing EVENT_TRACE_LOGFILE');
ZeroMemory(@trace, sizeof(trace)); // 408 bytes
trace.LogFileName := PWideChar(filename);
trace.EventCallback := ProcessTraceEventCallback; //called once for each event in the file
// trace.ProcessTraceMode := PROCESS_TRACE_MODE_EVENT_RECORD; //we wanted new style events through the "EventRecordCallback"
// trace.BufferCallback := BufferCallback; //optional callback for inforamtion after processing a buffer-full of events
WriteLn('Opening trace file "'+filename+'"');
th := OpenTraceW(trace);
if (th = INVALID_PROCESSTRACE_HANDLE) or (th = -1) then
begin
WriteLn('Error opening trace file: '+SysErrorMessage(GetLastError));
Exit;
end;
WriteLn('Successfully opened trace session. TraceHandle: 0x'+IntToHex(th, 8));
WriteLn('Processing trace file');
res := ProcessTrace(@th, 1, nil, nil);
if (res <> ERROR_SUCCESS) then // and (hr <> ERROR_CANCELLED) then
RaiseLastOSError;
WriteLn('Successfully processed trace file');
WriteLn('Closing trace handle');
CloseTrace(th);
WriteLn('Finished processing '+filename);
end;
begin
try
Main;
WriteLn('Execution complete. Press enter to close...');
ReadLn;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
我的输出是:
Filename: C:\test.etl
Initializing EVENT_TRACE_LOGFILE
Opening trace file "C:\test.etl"
Successfully opened trace session. TraceHandle: 0x00000041
Processing trace file
Successfully processed trace file
Closing trace handle
Finished processing C:\test.etl
Execution complete. Press enter to close...
有人能看出我做错了什么吗?
答案正是我所知道的。
packed record
s$ALIGN 8
- Windows 所需的 ABIpacked
添加到所有记录当这不起作用时,我询问了 Stackoverflow。
同时,我启动了 Visual Studio C++,并比较了原始结构和 Delphi 翻译的
sizeof
。
他们不匹配。
问题是
packed
记录。
sizeof(EVENT_TRACE_LOGFILEW)
:416 字节(原为 404)
sizeof(EVENT_TRACE)
:88 字节(原为 80)
sizeof(EVENT_TRACE_HEADER)
:44 字节(原为 40)sizeof(TRACE_LOGFILE_HEADER
):272 字节拆下唱片包装即可修复。
我正在经历与汇编语言类似的事情。一切都很顺利,但是当进程跟踪发生时,线程被阻塞,并且事件捕获过程没有捕获任何东西......
你能把正确的结构传给我吗?