Example that Creates a Session and Enables a Manifest-based or Classic Provider
原文链接
作者:Microsoft
译者:塔塔塔塔塔
以下示例显示了如何启动跟踪会话,启用基于清单的提供程序或经典提供程序,禁用提供程序然后停止会话。
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <strsafe.h>
#include <wmistr.h>
#include <evntrace.h>
#define LOGFILE_PATH L"<FULLPATHTOLOGFILE.etl>"
#define LOGSESSION_NAME L"My Event Trace Session"
// 根据GUID确认你的Session
// 记住要创建自己的会话GUID
// {AE44CB98-BD11-4069-8093-770EC9258A12}
static const GUID SessionGuid =
{ 0xae44cb98, 0xbd11, 0x4069, { 0x80, 0x93, 0x77, 0xe, 0xc9, 0x25, 0x8a, 0x12 } };
// 标识所需的提供程序的GUID
// 启用您的会话
// {D8909C24-5BE9-4502-98CA-AB7BDC24899D}
static const GUID ProviderGuid =
{ 0xd8909c24, 0x5be9, 0x4502, {0x98, 0xca, 0xab, 0x7b, 0xdc, 0x24, 0x89, 0x9d } };
void wmain(void)
{
ULONG status = ERROR_SUCCESS;
TRACEHANDLE SessionHandle = 0;
EVENT_TRACE_PROPERTIES* pSessionProperties = NULL;
ULONG BufferSize = 0;
BOOL TraceOn = TRUE;
// 创建一个Session属性的内存空间
// 这内存空间必须足够大去包含日志文件名称和Session名称
// 这些文件名和会话名将附加到会话属性结构的末尾
BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME);
pSessionProperties = (EVENT_TRACE_PROPERTIES*) malloc(BufferSize);
if (NULL == pSessionProperties)
{
wprintf(L"Unable to allocate %d bytes for properties structure.\n", BufferSize);
goto cleanup;
}
// 设置会话属性。您只需将日志文件名附加到属性结构中
// StartTrace函数为您添加Session名称
ZeroMemory(pSessionProperties, BufferSize);
pSessionProperties->Wnode.BufferSize = BufferSize;
pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution
pSessionProperties->Wnode.Guid = SessionGuid;
pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL;
pSessionProperties->MaximumFileSize = 1; // 1 MB
pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH);
// 创建Session
status = StartTrace((PTRACEHANDLE)&SessionHandle, LOGSESSION_NAME, pSessionProperties);
if (ERROR_SUCCESS != status)
{
wprintf(L"StartTrace() failed with %lu\n", status);
goto cleanup;
}
// Enable the providers that you want to log events to your session.
status = EnableTraceEx2(
SessionHandle,
(LPCGUID)&ProviderGuid,
EVENT_CONTROL_CODE_ENABLE_PROVIDER,
TRACE_LEVEL_INFORMATION,
0,
0,
0,
NULL
);
if (ERROR_SUCCESS != status)
{
wprintf(L"EnableTrace() failed with %lu\n", status);
TraceOn = FALSE;
goto cleanup;
}
wprintf(L"Run the provider application. Then hit any key to stop the session.\n");
_getch();
cleanup:
if (SessionHandle)
{
if (TraceOn)
{
status = EnableTraceEx2(
SessionHandle,
(LPCGUID)&ProviderGuid,
EVENT_CONTROL_CODE_DISABLE_PROVIDER,
TRACE_LEVEL_INFORMATION,
0,
0,
0,
NULL
);
}
status = ControlTrace(SessionHandle, LOGSESSION_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
if (ERROR_SUCCESS != status)
{
wprintf(L"ControlTrace(stop) failed with %lu\n", status);
}
}
if (pSessionProperties)
{
free(pSessionProperties);
pSessionProperties = NULL;
}
}
译者叙
参看Microsoft的Sample时要注意一些小问题。
易错点1: 错误代码24
错误提示:The program issued a command but the command length is incorrect. 命令长度不正确。
#define PATH L"123"
这样的Path长度是3+1 有一个EOF终止符。作者热衷与string,凡是遇到char*都尽可能的用string代替了,所以会遇到分配内存不足的情况。我在下面用代码进行了描述。关于Sizeof(String)=40是因为std::string
内部其实是basic_string
而sizeof是得到该类的大小,字符串是以char*
的方式存在到其成员变量中,所以无论多长的字符串只会占basic_string
4字节,这样描述可能会有歧义但是意会即可。
File: etwcontroller.cpp
#define LOGFILE_PATH L"<FULLPATHTOLOGFILE.etl>"
#define LOGSESSION_NAME L"My Event Trace Session"
bool EtwController::CreateSession(Session *session)
{
ULONG status = ERROR_SUCCESS;
TRACEHANDLE SessionHandle = 0;
EVENT_TRACE_PROPERTIES* pSessionProperties = NULL;
ULONG BufferSize = 0;
ULONG BufferSize2 = 0;
BOOL TraceOn = TRUE;
std::cout << "sizeof(session->file.name_) : " << sizeof(session->file.name_) << std::endl;
std::cout << "session->file.name_.length() : " << session->file.name_.length() << std::endl;
std::cout << "sizeof(session->name_) : " << sizeof(session->name_) << std::endl;
std::cout << "session->name_.length() : " << session->name_.length() << std::endl;
std::cout << "sizeof(LOGFILE_PATH) : " << sizeof(LOGFILE_PATH) << std::endl;
std::cout << "sizeof(LOGSESSION_NAME) : " << sizeof(LOGSESSION_NAME) << std::endl;
BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(session->file.name_) + sizeof(session->name_);
BufferSize2 = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME);
pSessionProperties = (EVENT_TRACE_PROPERTIES*)malloc(BufferSize);
... ...
return false;
}
File: etwtest.cpp
int main()
{
auto dll = LoadLibrary("etw-log.dll");
APICreateSession f = (APICreateSession)GetProcAddress(dll, "CreateSession");
logs::Session session("My Event Trace Session","<FULLPATHTOLOGFILE.etl><FULLPATHTOLOGFILE.etl>","123");
std::cout << "session name :" << session.name_ << std::endl;
std::cout << "log file name :" << session.file.name_ << std::endl;
f(&session);
return 0;
}
Output:
session name :My Event Trace Session
log file name :<FULLPATHTOLOGFILE.etl><FULLPATHTOLOGFILE.etl><FULLPATHTOLOGFILE.etl>
sizeof(session->file.name_) : 40
session->file.name_.length() : 69
sizeof(session->name_) : 40
session->name_.length() : 22
sizeof(LOGFILE_PATH) : 24
sizeof(LOGSESSION_NAME) : 23
易错点2: 错误代码123
错误提示: The filename, directory name, or volume label syntax is incorrect. 文件名,目录名称或卷标签语法不正确。
File: etwcontroller.cpp
#define LOGFILE_PATH L"<FULLPATHTOLOGFILE.etl>"
#define LOGSESSION_NAME L"My Event Trace Session"
File: etwtest.cpp
logs::Session session("My Event Trace Session","<FULLPATHTOLOGFILE.etl>",guid);
这里官方给出的代码中有尖括号,不明白其用意,实际程序是不需要<>
这个符号的。
易错点3: 错误代码183
Cannot create a file when that file already exists. 该文件已存在时无法创建该文件。
这里会有两种情况出现该错误:
- 日志文件真的存在无法创建。
- Session存在无法创建。
这个问题很容易在调试中出现,避免的办法一:
- 删除已存在的文件
- 关闭存在的Session
避免的办法二:
- 按时间创建文件或文件夹,保证每次创建不会重名。
- 不要强行退出应用,如果出现183错误可在程序中针对183错误进行停止Session。