// Copyright 2026 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later // A local, user-mode-only trace for validating the provider without WPR rights. #pragma once #include #include #include #include #include #include #include #include #include "Common/PerformanceTrace.h" class PrivateETW { public: explicit PrivateETW(const char* output) { if (!output) return; const auto path = std::filesystem::absolute(output); if (std::filesystem::exists(path)) throw std::runtime_error("Private ETL target already exists"); if (!Common::PerformanceTrace::Requested()) throw std::runtime_error("Set DOLPHIN_PERF_TRACE=1 for a private trace"); Common::PerformanceTrace::Enabled(); // Register before starting the session. constexpr wchar_t name[] = L"DolphinP1LocalProbe"; const std::wstring filename = path.wstring(); const size_t name_size = sizeof(name); const size_t filename_size = (filename.size() + 1) * sizeof(wchar_t); m_storage.resize(sizeof(EVENT_TRACE_PROPERTIES) + name_size + filename_size); m_properties = reinterpret_cast(m_storage.data()); std::memset(m_properties, 0, m_storage.size()); m_properties->Wnode.BufferSize = static_cast(m_storage.size()); m_properties->Wnode.Guid = PERF_PROVIDER; m_properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; m_properties->Wnode.ClientContext = 1; // QPC m_properties->BufferSize = 64; m_properties->MinimumBuffers = 2; m_properties->MaximumBuffers = 16; m_properties->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL | EVENT_TRACE_PRIVATE_LOGGER_MODE | EVENT_TRACE_PRIVATE_IN_PROC; m_properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); m_properties->LogFileNameOffset = static_cast(sizeof(EVENT_TRACE_PROPERTIES) + name_size); std::memcpy(m_storage.data() + m_properties->LoggerNameOffset, name, name_size); std::memcpy(m_storage.data() + m_properties->LogFileNameOffset, filename.c_str(), filename_size); const ULONG started = StartTraceW(&m_handle, name, m_properties); if (started != ERROR_SUCCESS) throw std::runtime_error("Private ETW StartTrace error " + std::to_string(started)); m_started = true; const ULONG enabled = EnableTraceEx2(m_handle, &PERF_PROVIDER, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, 0, nullptr); if (enabled != ERROR_SUCCESS || !Common::PerformanceTrace::Enabled()) { Close(); throw std::runtime_error("Private ETW enable error " + std::to_string(enabled)); } } ~PrivateETW() { Close(); } PrivateETW(const PrivateETW&) = delete; PrivateETW& operator=(const PrivateETW&) = delete; bool Close() { if (!m_started) return true; m_started = false; return ControlTraceW(m_handle, L"DolphinP1LocalProbe", m_properties, EVENT_TRACE_CONTROL_STOP) == ERROR_SUCCESS; } private: static constexpr GUID PERF_PROVIDER = { 0x54ee9231, 0x28be, 0x4ab0, {0x98, 0x1e, 0xb2, 0x6f, 0x97, 0x9c, 0x56, 0xa4}}; TRACEHANDLE m_handle = 0; bool m_started = false; EVENT_TRACE_PROPERTIES* m_properties = nullptr; std::vector m_storage; };