windows - Windows 事件跟踪:OpenTrace/ProcessTrace 未返回任何事件 - 未调用回调
问题描述
精简版
我正在尝试使用OpenTrace和ProcessTrace来读取.etl
文件的事件。
- 对OpenTrace的调用成功返回一个
TRACEHANDLE
- 对ProcessTrace的调用返回
ERROR_SUCCESS
- 但ProcessTrace从不调用我的
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;
没有代码返回错误。
- OpenTrace返回一个有效的句柄
- ProcessTrace需要一点时间才能返回(好像它正在处理事件一样)
- 并返回
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://docs.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...
谁能看到我做错了什么?
解决方案
答案正是我所知道的。
- 我从Franck Soranio翻译的标题开始
- 一些定义在
packed record
哪里 - 当那不起作用时,我尝试添加
$ALIGN 8
- Windows 所需的 ABI - 当那不起作用时,我尝试添加
packed
到所有记录
当那不起作用时,我问 Stackoverflow。
与此同时,我启动了 Visual Studio C++,并比较sizeof
了原始结构和 Delphi 的翻译。
他们不匹配。
问题是packed
记录。
sizeof(EVENT_TRACE_LOGFILEW)
:416 字节(原为 404)sizeof(EVENT_TRACE)
: 88 字节(原为 80)sizeof(EVENT_TRACE_HEADER)
: 44 字节(原为 40)
sizeof(TRACE_LOGFILE_HEADER
): 272 字节
卸下唱片包装固定它。
推荐阅读
- html - 虚拟滚动中的角材质扩展面板
- node.js - 消除异步快速路由回调中重复的 try catch 语句?
- shell - 将数据写入 Formta Form 中的文件
- android - 创建自定义箭头视图
- sequelize.js - 复杂的销毁查询
- java - 如何在 uniVocity CsvWriter 中附加不带引号的空值或空值
- python - Matplotlib:绘图函数 plt.contourf() 无法从颜色图喷射中绘制超过 9 种颜色
- javascript - 如何按第一个元素对象键对数组中的数组进行分组
- blazor-server-side - 服务器端 Blazor 中每个用户 DOM 通常使用多少内存?
- javascript - 如何使用安全规则限制在 Firestore 中一次只删除一个文档?