-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
60bff91
commit 2b87eff
Showing
10 changed files
with
399 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
{ | ||
GUID id; | ||
if (SUCCEEDED(CLSIDFromString(providerGUIDstr.c_str(), &id))) | ||
{ | ||
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; | ||
eventTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; | ||
eventTraceProperties->Wnode.ClientContext = 1; // QPC clock resolution | ||
eventTraceProperties->Wnode.Guid = m_providerGUID; | ||
eventTraceProperties->BufferSize = 4; // 4KB, the minimum size | ||
eventTraceProperties->LogFileMode = EVENT_TRACE_PRIVATE_LOGGER_MODE | EVENT_TRACE_PRIVATE_IN_PROC | EVENT_TRACE_FILE_MODE_NEWFILE; | ||
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); | ||
|
||
// 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); | ||
|
||
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; | ||
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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
#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 }; | ||
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"; | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project DefaultTargets="Build" | ||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" /> | ||
<PropertyGroup Label="Globals"> | ||
<VCProjectVersion>17.0</VCProjectVersion> | ||
<Keyword>Win32Proj</Keyword> | ||
<ProjectGuid>{8f021b46-362b-485c-bfba-ccf83e820cbd}</ProjectGuid> | ||
<RootNamespace>EtwTrace</RootNamespace> | ||
</PropertyGroup> | ||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | ||
<PropertyGroup Label="Configuration"> | ||
<ConfigurationType>StaticLibrary</ConfigurationType> | ||
<PlatformToolset>v143</PlatformToolset> | ||
</PropertyGroup> | ||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | ||
<ImportGroup Label="ExtensionSettings"> | ||
</ImportGroup> | ||
<ImportGroup Label="Shared"> | ||
</ImportGroup> | ||
<ImportGroup Label="PropertySheets"> | ||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | ||
</ImportGroup> | ||
<PropertyGroup Label="UserMacros" /> | ||
<ItemGroup> | ||
<ClInclude Include="EtwTrace.h" /> | ||
<ClInclude Include="pch.h" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ClCompile Include="EtwTrace.cpp" /> | ||
<ClCompile Include="pch.cpp"> | ||
<PrecompiledHeader>Create</PrecompiledHeader> | ||
</ClCompile> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="packages.config" /> | ||
</ItemGroup> | ||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | ||
<ImportGroup Label="ExtensionTargets"> | ||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" /> | ||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> | ||
</ImportGroup> | ||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | ||
<PropertyGroup> | ||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | ||
</PropertyGroup> | ||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" /> | ||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" /> | ||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> | ||
</Target> | ||
</Project> |
Oops, something went wrong.