我有一个使用本机插件的应用程序。 我有我自己的这些插件的二进制格式。 每个插件在运行时使用类似于将DLL映射到进程空间的方法加载。 这意味着,每个插件都有自己的ImageBase ,像.text或.data这些部分的处理方式与传统DLL相同。 唯一不同的是插件的二进制格式(它不是PE文件)以及将插件映射到进程空间的加载程序代码。
现在我知道ETW在通过这个命令行进行跟踪时:
xperf -on latency -stackwalk profile -buffersize 1024 -minbuffers 300 -start tracea1 -on Microsoft-Windows-Win32k:::'stack'
会发出可用于在跟踪捕获期间重build过程环境的事件。 也就是说,它会发出类似“添加进程”,“将线程添加到进程”,“添加DLL模块到进程”等事件,这样像xperfview这样的工具xperfview可以build立系统中进程状态的虚拟环境,并构build像当前进程树一样的信息。 例如,这些事件是ImageLoad事件,它提供有关在之前或在跟踪期间加载的每个DLL的信息。
当然,对于我的插件来说,这些ImageLoad事件不会生成,因为它们在技术上不是DLL(也就是说,虽然它们的function是相同的,但它们没有像DLL一样被加载)。 这就是为什么像xperfview这样的工具不知道它们在进程空间中的存在。
我想要做的是在我的插件加载器代码中编写我自己的EventWrites,并将这些ImageLoad事件与必要的信息一起发出,以便xperfview和类似的工具可以将我的插件解释为正常的DLL。 我会填写必要的信息,如ImageBase , ProcessId , ImageSize等
要做到这一点,我明白我需要注册事件MSNT_SystemTrace提供者,这是ImageLoad事件的所有者,使用这种结构来构build事件:
<Data Name="ImageBase">0x7FEFDBD0000</Data> <Data Name="ImageSize">0x12D000</Data> <Data Name="ProcessId"> 548</Data> ... <Data Name="Reserved0"> 0</Data> <Data Name="DefaultBase">0x7FEFDBD0000</Data>
并发出事件。
问题是,当我尝试注册另一个MSNT_SystemTrace时,我得到了ERROR_ACCESS_DENIED ,这是合乎逻辑的,因为这个提供者已经存在了。
但这迫使我提出这个问题,即使ETW支持我也想做这件事情?
我想我找到了一个解决方案。
虽然我不知道如何通过现有的提供程序实时发送事件,但Windows 8公开了允许修改ETL跟踪日志的接口,因此可以将事件的ProviderId更改为不同的值。 有问题的界面是ITraceRelogger 。 你需要这些指导:
EXTERN_GUID(CLSID_TraceRelogger, 0x7b40792d, 0x05ff, 0x44c4, 0x90, 0x58, 0xf4, 0x40, 0xc7, 0x1f, 0x17, 0xd4); DEFINE_GUID(IID_ITraceRelogger, 0xF754AD43, 0x3BCC, 0x4286, 0x80, 0x09,0x9C, 0x5D, 0xA2, 0x14, 0xE8, 0x4E); // {F754AD43-3BCC-4286-8009-9C5DA214E84E} DEFINE_GUID(IID_ITraceEventCallback, 0x3ED25501, 0x593F, 0x43E9, 0x8F, 0x38,0x3A, 0xB4, 0x6F, 0x5A, 0x4A, 0x52); // {3ED25501-593F-43E9-8F38-3AB46F5A4A52}
和Windows 8 SDK( c:\Program Files (x86)\Windows Kits\8.0\Include\um\relogger.h )中的c:\Program Files (x86)\Windows Kits\8.0\Include\um\relogger.h 。 原始的relogger.h似乎被破坏了,因为它引用了一些外部符号,但似乎没有LIB文件来补充它。 我相信你会设法解决这个问题!
要使用它,只需创建一个实例:
ITraceRelogger *relog = NULL; hres = CoCreateInstance(CLSID_TraceRelogger, 0, CLSCTX_INPROC_SERVER, IID_ITraceRelogger2, (LPVOID *)& relog);
添加你的input.etl和output.etl文件:
#include <windows.h> #include <cguid.h> #include <atlbase.h> #include <comdef.h> ... CComBSTR input = "input.etl"; CComBSTR output = "output.etl"; ... hres = relog->AddLogfileTraceStream(input, NULL, & trace); ... hres = relog->SetOutputFilename(output);
然后,你需要注册一个回调,它将处理事件修改。 事件回调的实现示例放在这个答案的末尾。 以下是关于如何在当前ITraceRelogger中使用它的代码:
EventCallback *ec = new EventCallback(); hres = relog->RegisterCallback(ec); ... hres = relog->ProcessTrace();
警告:如果您尚未注册任何回调, ProcessTrace()将返回一个错误。
这里是一个工作回调的例子:
class EventCallback: public ITraceEventCallback { private: DWORD ref_count; DWORD64 evno; public: EventCallback() { ref_count = 0; evno = 0; } STDMETHODIMP QueryInterface(const IID& iid, void **obj) { if(iid == IID_IUnknown) { *obj = dynamic_cast<IUnknown *>(this); } else if(iid == IID_ITraceEventCallback) { *obj = dynamic_cast<ITraceEventCallback *>(this); } else { *obj = NULL; return E_NOINTERFACE; } return S_OK; } STDMETHODIMP_ (ULONG) AddRef(void) { return InterlockedIncrement(& ref_count); } STDMETHODIMP_ (ULONG) Release() { ULONG ucount = InterlockedDecrement(& ref_count); if(ucount == 0) { delete this; } return ucount; } HRESULT STDMETHODCALLTYPE OnBeginProcessTrace(ITraceEvent *HeaderEvent, ITraceRelogger *Relogger) { return S_OK; } HRESULT STDMETHODCALLTYPE OnEvent(ITraceEvent *Event, ITraceRelogger *Relogger) { // Your main method. evno++; Relogger->Inject(Event); } HRESULT STDMETHODCALLTYPE OnFinalizeProcessTrace(ITraceRelogger *Relogger) { return S_OK; } };
Relogger->Inject会将当前Event复制到输出文件。 您可以使用ITraceRelogger来检查可用的方法,这将允许您更改所需的事件属性。 我感兴趣的方法是SetProviderId() 。
此外,请记住,MSDN指出, ITraceRelogger在Windows 7中可用。这不是我所经历的 – 我不能在Windows 7中实例化此类,所以MSDN可能有关于此主题的错误信息。