-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
// | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// | ||
|
||
#pragma once | ||
#include "pch.h" | ||
|
||
#include "ETWTrace.h" | ||
|
||
#include <wil\stl.h> | ||
#include <wil\win32_helpers.h> | ||
|
||
namespace fs = std::filesystem; | ||
|
||
namespace | ||
{ | ||
inline std::wstring get_root_save_folder_location() | ||
{ | ||
PWSTR local_app_path; | ||
winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &local_app_path)); | ||
std::wstring result{ local_app_path }; | ||
CoTaskMemFree(local_app_path); | ||
|
||
result += L"\\Microsoft\\PowerToys"; | ||
std::filesystem::path save_path(result); | ||
if (!std::filesystem::exists(save_path)) | ||
{ | ||
std::filesystem::create_directories(save_path); | ||
} | ||
return result; | ||
} | ||
} | ||
|
||
namespace Shared | ||
{ | ||
namespace Trace | ||
{ | ||
ETWTrace::ETWTrace(const std::wstring& providerGUIDstr) | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
Dstr is not a recognized word. (unrecognized-spelling)
|
||
{ | ||
GUID id; | ||
if (SUCCEEDED(CLSIDFromString(providerGUIDstr.c_str(), &id))) | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
Dstr is not a recognized word. (unrecognized-spelling)
|
||
{ | ||
m_providerGUID = id; | ||
} | ||
|
||
fs::path outputFolder = get_root_save_folder_location(); | ||
m_etwFolder = (outputFolder / c_etwFolderName); | ||
} | ||
|
||
ETWTrace::ETWTrace(const GUID& providerGUID) : | ||
m_providerGUID(providerGUID) | ||
{ | ||
fs::path outputFolder = get_root_save_folder_location(); | ||
m_etwFolder = (outputFolder / c_etwFolderName); | ||
} | ||
|
||
ETWTrace::~ETWTrace() | ||
{ | ||
Stop(); | ||
m_etwFolder.clear(); | ||
m_providerGUID = {}; | ||
} | ||
|
||
void ETWTrace::UpdateState(bool tracing) | ||
{ | ||
if (tracing) | ||
{ | ||
Start(); | ||
} | ||
else | ||
{ | ||
Stop(); | ||
} | ||
} | ||
|
||
void ETWTrace::Flush() | ||
{ | ||
if (m_tracing) | ||
{ | ||
Control(EVENT_TRACE_CONTROL_FLUSH); | ||
Control(EVENT_TRACE_CONTROL_INCREMENT_FILE); | ||
} | ||
} | ||
|
||
void ETWTrace::CreateEtwFolderIfNeeded() | ||
{ | ||
if (!std::filesystem::exists(m_etwFolder)) | ||
{ | ||
std::filesystem::create_directories(m_etwFolder); | ||
} | ||
else if (!std::filesystem::is_directory(m_etwFolder)) | ||
{ | ||
std::filesystem::remove(m_etwFolder); | ||
std::filesystem::create_directory(m_etwFolder); | ||
} | ||
|
||
THROW_HR_IF(E_UNEXPECTED, !std::filesystem::exists(m_etwFolder)); | ||
} | ||
|
||
void ETWTrace::InitEventTraceProperties() | ||
{ | ||
const std::filesystem::path exePath(wil::GetModuleFileNameW<std::wstring>(nullptr)); | ||
const auto exeName = exePath.stem().wstring(); | ||
|
||
auto now = std::chrono::system_clock::now(); | ||
auto timeNow = std::chrono::system_clock::to_time_t(now); | ||
std::wstringstream dateTime; | ||
struct tm timeInfo | ||
{ | ||
}; | ||
errno_t err = localtime_s(&timeInfo, &timeNow); | ||
if (err == 0) | ||
{ | ||
dateTime << std::put_time(&timeInfo, L"-%m-%d-%Y__%H_%M_%S"); | ||
} | ||
|
||
m_sessionName = wil::str_printf<std::wstring>(L"%ws-%d%ws", exeName.c_str(), GetCurrentProcessId(), dateTime.str().c_str()); | ||
const ULONG etwSessionNameCharCount = static_cast<ULONG>(m_sessionName.size() + 1); | ||
const ULONG etwSessionNameByteSize = etwSessionNameCharCount * sizeof(m_sessionName[0]); | ||
|
||
auto etlFileNameFormattedCounter = m_sessionName + c_etwNewFileFormattedCounter; | ||
std::filesystem::path etlFilePath = m_etwFolder / etlFileNameFormattedCounter; | ||
etlFilePath.replace_extension(c_etwFileNameEnd); | ||
THROW_HR_IF(E_UNEXPECTED, etlFilePath.empty()); | ||
|
||
const auto etlFilePathStr = etlFilePath.wstring(); | ||
// std::string/wstring returns number of characters not including the null terminator, so add +1 for that. | ||
const ULONG etwFilePathCharCount = static_cast<ULONG>(etlFilePathStr.size() + 1); | ||
const ULONG etwFilePathByteSize = etwFilePathCharCount * sizeof(etlFilePathStr[0]); | ||
|
||
const ULONG bufferSizeInBytes = sizeof(EVENT_TRACE_PROPERTIES) + etwSessionNameByteSize + etwFilePathByteSize; | ||
auto eventTracePropertiesBuffer = std::make_unique<unsigned char[]>(bufferSizeInBytes); | ||
ZeroMemory(eventTracePropertiesBuffer.get(), bufferSizeInBytes); | ||
auto eventTraceProperties = reinterpret_cast<EVENT_TRACE_PROPERTIES*>(eventTracePropertiesBuffer.get()); | ||
|
||
eventTraceProperties->Wnode.BufferSize = bufferSizeInBytes; | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
Wnode is not a recognized word. (unrecognized-spelling)
|
||
eventTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
Wnode is not a recognized word. (unrecognized-spelling)
Check failure Code scanning / check-spelling Unrecognized Spelling Error
WNODE is not a recognized word. (unrecognized-spelling)
|
||
eventTraceProperties->Wnode.ClientContext = 1; // QPC clock resolution | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
Wnode is not a recognized word. (unrecognized-spelling)
Check failure Code scanning / check-spelling Unrecognized Spelling Error
QPC is not a recognized word. (unrecognized-spelling)
|
||
eventTraceProperties->Wnode.Guid = m_providerGUID; | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
Wnode is not a recognized word. (unrecognized-spelling)
|
||
eventTraceProperties->BufferSize = 4; // 4KB, the minimum size | ||
eventTraceProperties->LogFileMode = EVENT_TRACE_PRIVATE_LOGGER_MODE | EVENT_TRACE_PRIVATE_IN_PROC | EVENT_TRACE_FILE_MODE_NEWFILE; | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
NEWFILE is not a recognized word. (unrecognized-spelling)
|
||
eventTraceProperties->MaximumFileSize = 1; // 1 MB | ||
|
||
// LoggerName is placed at the end of EVENT_TRACE_PROPERTIES structure | ||
eventTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); | ||
wcsncpy_s(reinterpret_cast<LPWSTR>(eventTracePropertiesBuffer.get() + eventTraceProperties->LoggerNameOffset), etwSessionNameCharCount, m_sessionName.c_str(), etwSessionNameCharCount); | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
wcsncpy is not a recognized word. (unrecognized-spelling)
|
||
|
||
// LogFileName is placed at the end of the Logger Name | ||
eventTraceProperties->LogFileNameOffset = eventTraceProperties->LoggerNameOffset + etwSessionNameByteSize; | ||
wcsncpy_s(reinterpret_cast<LPWSTR>(eventTracePropertiesBuffer.get() + eventTraceProperties->LogFileNameOffset), etwFilePathCharCount, etlFilePathStr.c_str(), etwFilePathCharCount); | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
wcsncpy is not a recognized word. (unrecognized-spelling)
|
||
|
||
m_eventTracePropertiesBuffer = std::move(eventTracePropertiesBuffer); | ||
} | ||
|
||
void ETWTrace::Start() | ||
{ | ||
if (m_tracing) | ||
{ | ||
return; | ||
} | ||
|
||
CreateEtwFolderIfNeeded(); | ||
InitEventTraceProperties(); | ||
|
||
auto eventTraceProperties = reinterpret_cast<EVENT_TRACE_PROPERTIES*>(m_eventTracePropertiesBuffer.get()); | ||
THROW_IF_WIN32_ERROR(StartTrace(&m_traceHandle, m_sessionName.c_str(), eventTraceProperties)); | ||
Enable(EVENT_CONTROL_CODE_ENABLE_PROVIDER); | ||
|
||
m_tracing = true; | ||
} | ||
|
||
void ETWTrace::Stop() | ||
{ | ||
if (!m_tracing) | ||
{ | ||
return; | ||
} | ||
|
||
Enable(EVENT_CONTROL_CODE_DISABLE_PROVIDER); | ||
|
||
// ControlTrace with EVENT_TRACE_CONTROL_STOP on the trace handle, | ||
// which is equivalent to calling CloseTrace() on the trace handle. | ||
Control(EVENT_TRACE_CONTROL_STOP); | ||
|
||
m_traceHandle = INVALID_PROCESSTRACE_HANDLE; | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
PROCESSTRACE is not a recognized word. (unrecognized-spelling)
|
||
m_eventTracePropertiesBuffer.reset(); | ||
m_tracing = false; | ||
} | ||
|
||
void ETWTrace::Control(ULONG traceControlCode) | ||
{ | ||
auto eventTraceProperties = reinterpret_cast<EVENT_TRACE_PROPERTIES*>(m_eventTracePropertiesBuffer.get()); | ||
const ULONG result = ControlTrace(m_traceHandle, m_sessionName.c_str(), eventTraceProperties, traceControlCode); | ||
THROW_IF_FAILED(HRESULT_FROM_WIN32(result)); | ||
} | ||
|
||
void ETWTrace::Enable(ULONG eventControlCode) | ||
{ | ||
// Control the main provider | ||
THROW_IF_WIN32_ERROR(EnableTraceEx2(m_traceHandle, &m_providerGUID, eventControlCode, TRACE_LEVEL_VERBOSE, 0, 0, 0, nullptr)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// | ||
|
||
#pragma once | ||
#include "pch.h" | ||
|
||
#include <evntrace.h> | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
evntrace is not a recognized word. (unrecognized-spelling)
|
||
#include <filesystem> | ||
|
||
namespace Shared | ||
{ | ||
namespace Trace | ||
{ | ||
class ETWTrace | ||
{ | ||
public: | ||
ETWTrace(const std::wstring& providerGUID); | ||
ETWTrace(const GUID& providerGUID); | ||
~ETWTrace(); | ||
|
||
void UpdateState(bool tracing); | ||
void Flush(); | ||
|
||
private: | ||
void CreateEtwFolderIfNeeded(); | ||
void InitEventTraceProperties(); | ||
void Start(); | ||
void Stop(); | ||
void Control(const ULONG traceControlCode); | ||
void Enable(const ULONG eventControlCode); | ||
|
||
GUID m_providerGUID{}; | ||
std::filesystem::path m_etwFolder; | ||
std::wstring m_sessionName; | ||
TRACEHANDLE m_traceHandle{ INVALID_PROCESSTRACE_HANDLE }; | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
TRACEHANDLE is not a recognized word. (unrecognized-spelling)
Check failure Code scanning / check-spelling Unrecognized Spelling Error
PROCESSTRACE is not a recognized word. (unrecognized-spelling)
|
||
std::unique_ptr<unsigned char[]> m_eventTracePropertiesBuffer; | ||
bool m_tracing{ false }; | ||
|
||
static constexpr PCWSTR c_etwFolderName = L"etw"; | ||
static constexpr PCWSTR c_etwNewFileFormattedCounter = L"-%d"; | ||
static constexpr PCWSTR c_etwFileNameEnd = L".etl"; | ||
}; | ||
} | ||
} |