From 05229e3e703ae26ee24c78e31f215a8cdb16903f Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 23 Jan 2026 11:21:07 -0800 Subject: [PATCH 01/11] Changes to separate etw session starting and the enabling of providers --- .../RealtimePresentMonSession.cpp | 33 ++++++++++++------- .../RealtimePresentMonSession.h | 4 +-- PresentData/PresentMonTraceSession.cpp | 26 ++++++++++++--- PresentData/PresentMonTraceSession.hpp | 8 +++-- PresentMon/PresentMon.args.json | 2 +- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 7a3d56e1..a6e06973 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2023 Intel Corporation +// Copyright (C) 2022-2023 Intel Corporation // SPDX-License-Identifier: MIT #include "Logging.h" #include "RealtimePresentMonSession.h" @@ -17,6 +17,7 @@ RealtimePresentMonSession::RealtimePresentMonSession(svc::FrameBroadcaster& broa { pBroadcaster = &broadcaster; ResetEtwFlushPeriod(); + StartEtwSession(); } bool RealtimePresentMonSession::IsTraceSessionActive() { @@ -33,7 +34,10 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_set lock(session_mutex_); if (pm_consumer_) { return PM_STATUS::PM_STATUS_SERVICE_ERROR; @@ -128,12 +139,12 @@ PM_STATUS RealtimePresentMonSession::StartTraceSession() { // it and start a new session. This is useful if a previous process failed to // properly shut down the session for some reason. trace_session_.mPMConsumer = pm_consumer_.get(); - auto status = trace_session_.Start(etl_file_name, pm_session_name_.c_str()); + auto status = trace_session_.Start(etl_file_name, pm_session_name_.c_str(), false); if (status == ERROR_ALREADY_EXISTS) { status = StopNamedTraceSession(pm_session_name_.c_str()); if (status == ERROR_SUCCESS) { - status = trace_session_.Start(etl_file_name, pm_session_name_.c_str()); + status = trace_session_.Start(etl_file_name, pm_session_name_.c_str(), false); } } @@ -167,7 +178,7 @@ PM_STATUS RealtimePresentMonSession::StartTraceSession() { return PM_STATUS::PM_STATUS_SUCCESS; } -void RealtimePresentMonSession::StopTraceSession() { +void RealtimePresentMonSession::StopEtwSession() { // PHASE 1: Signal shutdown and wait for threads to observe it // this also enforces "only_once" semantics for multiple stop callers if (session_active_.exchange(false, std::memory_order_acq_rel)) { diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h index 54be3135..99178c4a 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h @@ -22,8 +22,8 @@ class RealtimePresentMonSession : public PresentMonSession private: // functions - PM_STATUS StartTraceSession(); - void StopTraceSession(); + PM_STATUS StartEtwSession(); + void StopEtwSession(); void DequeueAnalyzedInfo( std::vector* processEvents, diff --git a/PresentData/PresentMonTraceSession.cpp b/PresentData/PresentMonTraceSession.cpp index d9caadbf..bf654076 100644 --- a/PresentData/PresentMonTraceSession.cpp +++ b/PresentData/PresentMonTraceSession.cpp @@ -458,7 +458,8 @@ ULONG CALLBACK BufferCallback(EVENT_TRACE_LOGFILE* pLogFile) ULONG PMTraceSession::Start( wchar_t const* etlPath, - wchar_t const* sessionName) + wchar_t const* sessionName, + bool enableProviders) { assert(mPMConsumer != nullptr); assert(mSessionHandle == 0); @@ -487,10 +488,15 @@ ULONG PMTraceSession::Start( return status; } - status = EnableProviders(mSessionHandle, sessionProps.Wnode.Guid, mPMConsumer); - if (status != ERROR_SUCCESS) { - Stop(); - return status; + // Set the session GUID + mSessionGuid = sessionProps.Wnode.Guid; + + if (enableProviders) { + status = EnableProviders(mSessionHandle, sessionProps.Wnode.Guid, mPMConsumer); + if (status != ERROR_SUCCESS) { + Stop(); + return status; + } } } @@ -694,6 +700,16 @@ bool PMTraceSession::QueryEtwStatus(EtwStatus* status) const return true; } +ULONG PMTraceSession::StartProviders() +{ + return EnableProviders(mSessionHandle, mSessionGuid, mPMConsumer); +} + +void PMTraceSession::StopProviders() +{ + DisableProviders(mSessionHandle); +} + ULONG EnableProvidersListing( TRACEHANDLE sessionHandle, const GUID* pSessionGuid, diff --git a/PresentData/PresentMonTraceSession.hpp b/PresentData/PresentMonTraceSession.hpp index 7d170fdf..b7ff881c 100644 --- a/PresentData/PresentMonTraceSession.hpp +++ b/PresentData/PresentMonTraceSession.hpp @@ -36,6 +36,7 @@ struct PMTraceSession { uint64_t mStartFileTime = 0; TimestampType mTimestampType = TIMESTAMP_TYPE_QPC; + GUID mSessionGuid = {}; TRACEHANDLE mSessionHandle = 0; // invalid session handles are 0 TRACEHANDLE mTraceHandle = INVALID_PROCESSTRACE_HANDLE; // invalid trace handles are INVALID_PROCESSTRACE_HANDLE @@ -49,9 +50,12 @@ struct PMTraceSession { // Cached ETW status for CSV output (updated periodically via QueryEtwStatus) mutable EtwStatus mCachedEtwStatus = {}; - ULONG Start(wchar_t const* etlPath, // If nullptr, start a live/realtime tracing session - wchar_t const* sessionName); // Required session name + ULONG Start(wchar_t const* etlPath, // If nullptr, start a live/realtime tracing session + wchar_t const* sessionName, // Required session name + bool enableProviders = true); // Enable providers on start void Stop(); + ULONG StartProviders(); + void StopProviders(); double TimestampDeltaToMilliSeconds(uint64_t timestampDelta) const; double TimestampDeltaToMilliSeconds(uint64_t timestampFrom, uint64_t timestampTo) const; diff --git a/PresentMon/PresentMon.args.json b/PresentMon/PresentMon.args.json index 3b3942b9..4497ccf1 100644 --- a/PresentMon/PresentMon.args.json +++ b/PresentMon/PresentMon.args.json @@ -68,7 +68,7 @@ }, { "Id": "8f014852-6512-466b-99cb-0a8724c36862", - "Command": "--output_file \"E:\\EtlTesting\\ETLDebugging\\test_case_8_unified-fid.csv\"" + "Command": "--output_file \"C:\\DataAnalysis\\BG6-Bug\\out.csv\"" }, { "Id": "1606ee3e-ac1c-4fea-9a87-736864dd1f00", From e7f164a24e3759a2358048053af4e23636194111 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 30 Jan 2026 13:55:36 -0800 Subject: [PATCH 02/11] Updated provider list on disable --- PresentData/PresentMonTraceSession.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/PresentData/PresentMonTraceSession.cpp b/PresentData/PresentMonTraceSession.cpp index bf654076..b7831cab 100644 --- a/PresentData/PresentMonTraceSession.cpp +++ b/PresentData/PresentMonTraceSession.cpp @@ -284,6 +284,7 @@ void DisableProviders(TRACEHANDLE sessionHandle) status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_Kernel_Process::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_Win32k::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); status = EnableTraceEx2(sessionHandle, &NvidiaDisplayDriver_Events::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); + status = EnableTraceEx2(sessionHandle, &Nvidia_PCL::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); } template< From 4069bb8e014eccfcf13c9661dfa70e9e4ae92b4f Mon Sep 17 00:00:00 2001 From: markgalvan-intel Date: Fri, 6 Feb 2026 11:14:12 -0800 Subject: [PATCH 03/11] Quiesce changes for all present tracking data on start and stop of providers. * WIP changes * Updated use in CompletePresent and ResetAppAndPclTrackingData * WIP changes to cover the resetting of present tracking data * Removed App/PCL specific quiesce code * Test code for monitoring ETW status * Updated logging --- .../RealtimePresentMonSession.cpp | 8 + PresentData/PresentMonTraceConsumer.cpp | 201 ++++++++++++++++++ PresentData/PresentMonTraceConsumer.hpp | 31 +++ PresentData/PresentMonTraceSession.cpp | 6 + PresentData/PresentMonTraceSession.hpp | 1 + PresentMon/CsvOutput.cpp | 16 +- PresentMon/MainThread.cpp | 5 +- 7 files changed, 260 insertions(+), 8 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index a6e06973..56f8a77e 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -62,6 +62,11 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_setResetPresentTrackingData(true); + } } return PM_STATUS::PM_STATUS_SUCCESS; } @@ -131,6 +136,9 @@ PM_STATUS RealtimePresentMonSession::StartEtwSession() { pm_consumer_->mTrackAppTiming = true; pm_consumer_->mTrackPcLatency = true; + // Service uses provider toggling; enable quiesce gate for safe state reset on start/stop + pm_consumer_->SetProviderToggleMode(true); + auto& opt = clio::Options::Get(); pm_session_name_ = util::str::ToWide(*opt.etwSessionName); diff --git a/PresentData/PresentMonTraceConsumer.cpp b/PresentData/PresentMonTraceConsumer.cpp index e75f4718..a48db047 100644 --- a/PresentData/PresentMonTraceConsumer.cpp +++ b/PresentData/PresentMonTraceConsumer.cpp @@ -89,6 +89,31 @@ static inline void SetScreenTime(std::shared_ptr const& p, uint64_ } } +namespace { + struct WaitOnAddressShim { + using WaitOnAddressFn = BOOL(WINAPI*)(volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds); + using WakeByAddressAllFn = VOID(WINAPI*)(PVOID Address); + + WaitOnAddressFn Wait = nullptr; + WakeByAddressAllFn WakeAll = nullptr; + + bool Available() const noexcept { return Wait && WakeAll; } + }; + + static const WaitOnAddressShim& GetWaitOnAddressShim() + { + static WaitOnAddressShim shim = [] { + WaitOnAddressShim s{}; + HMODULE k32 = ::GetModuleHandleW(L"kernel32.dll"); + if (!k32) return s; + s.Wait = reinterpret_cast(::GetProcAddress(k32, "WaitOnAddress")); + s.WakeAll = reinterpret_cast(::GetProcAddress(k32, "WakeByAddressAll")); + return s; + }(); + return shim; + } +} // namespace + PresentEvent::PresentEvent() : PresentStartTime(0) , ProcessId(0) @@ -3466,6 +3491,182 @@ void PMTraceConsumer::DequeuePresentEvents(std::vector= timeoutMs) { + pmlog_warn("Timed out waiting for event processing to quiesce; skipping present tracking reset"); + return; + } + DWORD remaining = timeoutMs - (DWORD)elapsed; + if (shim.Available()) { + LONG expected = ::InterlockedCompareExchange(&mEventProcessingInFlight, 0, 0); + if (expected != 0) { + // Wait until the value at the address changes from 'expected'. + // Spurious wakeups are possible; loop re-check handles it. + shim.Wait(&mEventProcessingInFlight, &expected, sizeof(expected), remaining); + } + } + else { + // Win7 fallback + ::Sleep(1); + } + } + + pmlog_info("Present tracking reset: Event processing quiesced; proceeding with present tracking reset shrink =" + + std::to_string(shrink)); + // Now it is safe to clear the state + { + std::lock_guard lock(mPresentEventMutex); + // Clear rings and counters + for(auto& p : mTrackedPresents) { + p.reset(); + } + for(auto& p : mCompletedPresents) { + p.reset(); + } + mNextFreeRingIndex = 0; + mCompletedIndex = 0; + mCompletedCount = 0; + mReadyCount = 0; + mNumOverflowedPresents = 0; + + // We need to see a completed present again after the providers restart + mHasCompletedAPresent = false; + + // DWM tracking + mPresentsWaitingForDWM.clear(); + DwmProcessId = 0; + DwmPresentThreadId = 0; + + // Deferred frame-type association state + mPendingPresentFrameTypeEvents.clear(); + mPendingFlipFrameTypeEvents.clear(); + mEnableFlipFrameTypeEvents = false; + } + + // Clear process events + { + std::lock_guard lock(mProcessEventMutex); + mProcessEvents.clear(); + } + + // Clear and potentially shrink present tracking maps + if (shrink) { + decltype(mPresentByThreadId){}.swap(mPresentByThreadId); + decltype(mOrderedPresentsByProcessId){}.swap(mOrderedPresentsByProcessId); + decltype(mPresentBySubmitSequence){}.swap(mPresentBySubmitSequence); + decltype(mPresentByWin32KPresentHistoryToken){}.swap(mPresentByWin32KPresentHistoryToken); + decltype(mPresentByDxgkPresentHistoryToken){}.swap(mPresentByDxgkPresentHistoryToken); + decltype(mPresentByDxgkPresentHistoryTokenData){}.swap(mPresentByDxgkPresentHistoryTokenData); + decltype(mPresentByDxgkContext){}.swap(mPresentByDxgkContext); + decltype(mPresentByVidPnLayerId){}.swap(mPresentByVidPnLayerId); + decltype(mLastPresentByWindow){}.swap(mLastPresentByWindow); + decltype(mPresentByAppFrameId){}.swap(mPresentByAppFrameId); + decltype(mAppTimingDataByAppFrameId){}.swap(mAppTimingDataByAppFrameId); + decltype(mPclTimingDataByPclFrameId){}.swap(mPclTimingDataByPclFrameId); + decltype(mHybridPresentModeBySwapChainPid){}.swap(mHybridPresentModeBySwapChainPid); + decltype(mLatestPingTimestampByProcessId){}.swap(mLatestPingTimestampByProcessId); + } else { + mPresentByThreadId.clear(); + mOrderedPresentsByProcessId.clear(); + mPresentBySubmitSequence.clear(); + mPresentByWin32KPresentHistoryToken.clear(); + mPresentByDxgkPresentHistoryToken.clear(); + mPresentByDxgkPresentHistoryTokenData.clear(); + mPresentByDxgkContext.clear(); + mPresentByVidPnLayerId.clear(); + mLastPresentByWindow.clear(); + mPresentByAppFrameId.clear(); + mAppTimingDataByAppFrameId.clear(); + mPclTimingDataByPclFrameId.clear(); + mHybridPresentModeBySwapChainPid.clear(); + mLatestPingTimestampByProcessId.clear(); + } + + // Input association state + mReceivedMouseClickByHwnd.clear(); + mRetrievedInput.clear(); + mLastInputDeviceReadTime = 0; + mLastInputDeviceType = InputDeviceType::None; + + // Reset PCL State + mUsingOutOfBoundPresentStart = false; + + // Reset GPU/NV tracking helpers + mGpuTrace.~GpuTrace(); + new (&mGpuTrace) GpuTrace(this); + mNvTraceConsumer.~NVTraceConsumer(); + new (&mNvTraceConsumer) NVTraceConsumer(); +} + +PMTraceConsumer::EventProcessingScope::EventProcessingScope(PMTraceConsumer& consumer) + : Consumer(consumer) +{ + // If provider-toggle mode is not enabled, do nothing. + if (!Consumer.mProviderToggleMode.load(std::memory_order_acquire)) { + active = true; + counted = false; + return; + } + + counted = true; + ::InterlockedIncrement(&Consumer.mEventProcessingInFlight); + + if (!Consumer.mEventProcessingEnabled.load(std::memory_order_acquire)) { + LONG v = ::InterlockedDecrement(&Consumer.mEventProcessingInFlight); + if (v == 0) { + auto const& shim = GetWaitOnAddressShim(); + if (shim.Available()) { + shim.WakeAll((PVOID)&Consumer.mEventProcessingInFlight); + } + } + + active = false; + counted = false; + return; + } + + active = true; +} + +PMTraceConsumer::EventProcessingScope::~EventProcessingScope() +{ + if (!counted) { + return; + } + + LONG v = ::InterlockedDecrement(&Consumer.mEventProcessingInFlight); + if (v == 0) { + auto const& shim = GetWaitOnAddressShim(); + if (shim.Available()) { + shim.WakeAll((PVOID)&Consumer.mEventProcessingInFlight); + } + } +} + AppTimingData* PMTraceConsumer::ExtractAppTimingData( std::unordered_map, AppTimingData, PairHash>& timingDataByFrameId, uint32_t processId, uint32_t appFrameId, uint64_t presentStartTime, std::function timingSelector) { diff --git a/PresentData/PresentMonTraceConsumer.hpp b/PresentData/PresentMonTraceConsumer.hpp index 6d8aec97..cf547613 100644 --- a/PresentData/PresentMonTraceConsumer.hpp +++ b/PresentData/PresentMonTraceConsumer.hpp @@ -8,6 +8,7 @@ #endif #include +#include #include #include #include @@ -335,6 +336,14 @@ struct PMTraceConsumer void DequeueProcessEvents(std::vector& outProcessEvents); void DequeuePresentEvents(std::vector>& outPresentEvents); + // Control of general event processing state for service capture start/stop without + // tearing down the underlying ETW session + // + // Provider-toggle mode enables a quiesce gate in the session callback so we can safely + // clear internal present tracking state from a controller thread. + void SetProviderToggleMode(bool enabled); + void SetEventProcessingEnabled(bool enabled); + void ResetPresentTrackingData(bool shrink = false); // ------------------------------------------------------------------------------------------- // The rest of this structure are internal data and functions for analysing the collected ETW @@ -449,6 +458,28 @@ struct PMTraceConsumer } }; + // Guard used to safely mutate/clear all Present tracking structures + // without taking a mutex on every ETW event. ResetPresentTrackingData() disables + // tracking and then waits for in-flight scopes to drain before clearing the maps. + struct EventProcessingScope { + PMTraceConsumer& Consumer; + // Whether event processing is active for the scope duration + bool active = false; + // Whether we incremented the in-flight counter + bool counted = false; + explicit EventProcessingScope(PMTraceConsumer& consumer); + ~EventProcessingScope(); + EventProcessingScope(const EventProcessingScope&) = delete; + EventProcessingScope& operator=(const EventProcessingScope&) = delete; + explicit operator bool() const noexcept { return active; } + }; + + std::atomic mProviderToggleMode{ false }; + std::atomic mEventProcessingEnabled{ true }; + // WaitOnAddress needs a 1/2/4/8-byte memory location. Using LONG makes it + // Win7-compatible (via Interlocked ops) and Win8+ compatible (via WaitOnAddress shim). + volatile LONG mEventProcessingInFlight = 0; + std::unordered_map> mPresentByThreadId; // ThreadId -> PresentEvent std::unordered_map mOrderedPresentsByProcessId; // ProcessId -> ordered PresentStartTime -> PresentEvent std::unordered_map>> diff --git a/PresentData/PresentMonTraceSession.cpp b/PresentData/PresentMonTraceSession.cpp index b7831cab..3eefb3da 100644 --- a/PresentData/PresentMonTraceSession.cpp +++ b/PresentData/PresentMonTraceSession.cpp @@ -298,6 +298,11 @@ void CALLBACK EventRecordCallback(EVENT_RECORD* pEventRecord) auto session = (PMTraceSession*) pEventRecord->UserContext; const auto& hdr = pEventRecord->EventHeader; + PMTraceConsumer::EventProcessingScope processingScope(*session->mPMConsumer); + if (!processingScope) { + return; + } + if constexpr (!IS_REALTIME_SESSION) { if (session->mStartTimestamp.QuadPart == 0) { session->mStartTimestamp = hdr.TimeStamp; @@ -686,6 +691,7 @@ bool PMTraceSession::QueryEtwStatus(EtwStatus* status) const mCachedEtwStatus.mEtwTotalBuffers = sessionProps.NumberOfBuffers; mCachedEtwStatus.mEtwEventsLost = sessionProps.EventsLost; mCachedEtwStatus.mEtwBuffersLost = sessionProps.LogBuffersLost + sessionProps.RealTimeBuffersLost; + mCachedEtwStatus.mNumOverflowedPresents = mPMConsumer->mNumOverflowedPresents; if (sessionProps.NumberOfBuffers > 0) { mCachedEtwStatus.mEtwBufferFillPct = 100.0 * mCachedEtwStatus.mEtwBuffersInUse / sessionProps.NumberOfBuffers; diff --git a/PresentData/PresentMonTraceSession.hpp b/PresentData/PresentMonTraceSession.hpp index b7ff881c..fb07d1a5 100644 --- a/PresentData/PresentMonTraceSession.hpp +++ b/PresentData/PresentMonTraceSession.hpp @@ -12,6 +12,7 @@ struct EtwStatus { ULONG mEtwTotalBuffers; ULONG mEtwEventsLost; ULONG mEtwBuffersLost; + ULONG mNumOverflowedPresents; }; struct PMTraceSession { diff --git a/PresentMon/CsvOutput.cpp b/PresentMon/CsvOutput.cpp index 329898da..ccbbc383 100644 --- a/PresentMon/CsvOutput.cpp +++ b/PresentMon/CsvOutput.cpp @@ -187,7 +187,8 @@ void WriteCsvHeader(FILE* fp) L",EtwBuffersInUse" L",EtwTotalBuffers" L",EtwEventsLost" - L",EtwBuffersLost"); + L",EtwBuffersLost" + L",OverflowedPresents"); } fwprintf(fp, L"\n"); @@ -270,12 +271,13 @@ void WriteCsvRow( fwprintf(fp, L",%u", p.FrameId); } if (args.mTrackEtwStatus) { - fwprintf(fp, L",%.1lf,%lu,%lu,%lu,%lu", + fwprintf(fp, L",%.1lf,%lu,%lu,%lu,%lu,%lu", pmSession.mCachedEtwStatus.mEtwBufferFillPct, pmSession.mCachedEtwStatus.mEtwBuffersInUse, pmSession.mCachedEtwStatus.mEtwTotalBuffers, pmSession.mCachedEtwStatus.mEtwEventsLost, - pmSession.mCachedEtwStatus.mEtwBuffersLost); + pmSession.mCachedEtwStatus.mEtwBuffersLost, + pmSession.mCachedEtwStatus.mNumOverflowedPresents); } fwprintf(fp, L"\n"); @@ -496,7 +498,8 @@ void WriteCsvHeader(FILE* fp) L",EtwBuffersInUse" L",EtwTotalBuffers" L",EtwEventsLost" - L",EtwBuffersLost"); + L",EtwBuffersLost" + L",OverflowedPresents"); } fwprintf(fp, L"\n"); @@ -847,12 +850,13 @@ void WriteCsvRow( } } if (args.mTrackEtwStatus) { - fwprintf(fp, L",%.1lf,%lu,%lu,%lu,%lu", + fwprintf(fp, L",%.1lf,%lu,%lu,%lu,%lu,%lu", pmSession.mCachedEtwStatus.mEtwBufferFillPct, pmSession.mCachedEtwStatus.mEtwBuffersInUse, pmSession.mCachedEtwStatus.mEtwTotalBuffers, pmSession.mCachedEtwStatus.mEtwEventsLost, - pmSession.mCachedEtwStatus.mEtwBuffersLost); + pmSession.mCachedEtwStatus.mEtwBuffersLost, + pmSession.mCachedEtwStatus.mNumOverflowedPresents); } fwprintf(fp, L"\n"); diff --git a/PresentMon/MainThread.cpp b/PresentMon/MainThread.cpp index d2741553..f75c8744 100644 --- a/PresentMon/MainThread.cpp +++ b/PresentMon/MainThread.cpp @@ -89,12 +89,13 @@ static void OutputEtwStatus() EtwStatus status = {}; if (gPMTraceSession->QueryEtwStatus(&status)) { - wprintf(L"[ETW Status] BufferFillPct=%.1f%% BuffersInUse=%lu TotalBuffers=%lu EventsLost=%lu BuffersLost=%lu\n", + wprintf(L"[ETW Status] BufferFillPct=%.1f%% BuffersInUse=%lu TotalBuffers=%lu EventsLost=%lu BuffersLost=%lu, OverflowedPresents=%lu\n", status.mEtwBufferFillPct, status.mEtwBuffersInUse, status.mEtwTotalBuffers, status.mEtwEventsLost, - status.mEtwBuffersLost); + status.mEtwBuffersLost, + status.mNumOverflowedPresents); } } From aee755aa5d5d0309dd41d5525829164d2d6ab980 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 6 Feb 2026 13:07:08 -0800 Subject: [PATCH 04/11] Clean up of start and stop streaming code --- .../RealtimePresentMonSession.cpp | 31 +++++++++++++++++++ .../RealtimePresentMonSession.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 56f8a77e..e994ecda 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -45,6 +45,15 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_setResetPresentTrackingData(false); + // Allow event processing before enabling providers + pm_consumer_->SetEventProcessingEnabled(true); + } + auto const providerStatus = trace_session_.StartProviders(); if (providerStatus != ERROR_SUCCESS) { { @@ -516,3 +525,25 @@ void RealtimePresentMonSession::CheckForTerminatedRealtimeProcesses( std::vector>* terminatedProcesses) { (void)terminatedProcesses; } + +void RealtimePresentMonSession::OnStreamStopped() +{ + if (streamer_.NumActiveStreams() != 0) { + return; + } + + // Signal stopped + if (evtStreamingStarted_) { + evtStreamingStarted_.Reset(); + } + + // Disable providers + nuke state + trace_session_.StopProviders(); + + if (pm_consumer_) { + pm_consumer_->SetEventProcessingEnabled(false); + pm_consumer_->ResetPresentTrackingData(true); + // If you still have app/pcl reset separately, call it here too. + // pm_consumer_->ResetAppAndPclTrackingData(true); + } +} diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h index 99178c4a..a49f8ac9 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h @@ -52,6 +52,8 @@ class RealtimePresentMonSession : public PresentMonSession void CheckForTerminatedRealtimeProcesses( std::vector>* terminatedProcesses); + void OnStreamStopped(); + // data std::wstring pm_session_name_; From 0139e076bcfbb65269dcc468e7ff8cf657a475f6 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 6 Feb 2026 13:07:08 -0800 Subject: [PATCH 05/11] Clean up of start and stop streaming code --- .../RealtimePresentMonSession.cpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index e994ecda..971765c7 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -526,6 +526,26 @@ void RealtimePresentMonSession::CheckForTerminatedRealtimeProcesses( (void)terminatedProcesses; } + // Signal stopped +void RealtimePresentMonSession::OnStreamStopped() +{ + if (streamer_.NumActiveStreams() != 0) { + return; + if (evtStreamingStarted_) { + evtStreamingStarted_.Reset(); + } + + // Disable providers + nuke state + trace_session_.StopProviders(); + + if (pm_consumer_) { + pm_consumer_->SetEventProcessingEnabled(false); + pm_consumer_->ResetPresentTrackingData(true); + // If you still have app/pcl reset separately, call it here too. + // pm_consumer_->ResetAppAndPclTrackingData(true); + } +} + void RealtimePresentMonSession::OnStreamStopped() { if (streamer_.NumActiveStreams() != 0) { From 6101d453ceb5e22849edf3f081ad8fab7c3e904f Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 6 Feb 2026 13:38:34 -0800 Subject: [PATCH 06/11] Updated ordering for the disabling of event processing. --- .../PresentMonService/RealtimePresentMonSession.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 971765c7..3dfabf85 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -557,13 +557,14 @@ void RealtimePresentMonSession::OnStreamStopped() evtStreamingStarted_.Reset(); } + if (pm_consumer_) { + pm_consumer_->SetEventProcessingEnabled(false); + } + // Disable providers + nuke state trace_session_.StopProviders(); if (pm_consumer_) { - pm_consumer_->SetEventProcessingEnabled(false); pm_consumer_->ResetPresentTrackingData(true); - // If you still have app/pcl reset separately, call it here too. - // pm_consumer_->ResetAppAndPclTrackingData(true); } } From 8f9b95b962129849834ce14f44a364a26082ee31 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Thu, 12 Feb 2026 12:35:03 -0800 Subject: [PATCH 07/11] Fixing rebase issues. --- .../RealtimePresentMonSession.cpp | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 3dfabf85..dd1d747b 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -524,47 +524,4 @@ void RealtimePresentMonSession::HandleTerminatedProcess(uint32_t processId) { void RealtimePresentMonSession::CheckForTerminatedRealtimeProcesses( std::vector>* terminatedProcesses) { (void)terminatedProcesses; -} - - // Signal stopped -void RealtimePresentMonSession::OnStreamStopped() -{ - if (streamer_.NumActiveStreams() != 0) { - return; - if (evtStreamingStarted_) { - evtStreamingStarted_.Reset(); - } - - // Disable providers + nuke state - trace_session_.StopProviders(); - - if (pm_consumer_) { - pm_consumer_->SetEventProcessingEnabled(false); - pm_consumer_->ResetPresentTrackingData(true); - // If you still have app/pcl reset separately, call it here too. - // pm_consumer_->ResetAppAndPclTrackingData(true); - } -} - -void RealtimePresentMonSession::OnStreamStopped() -{ - if (streamer_.NumActiveStreams() != 0) { - return; - } - - // Signal stopped - if (evtStreamingStarted_) { - evtStreamingStarted_.Reset(); - } - - if (pm_consumer_) { - pm_consumer_->SetEventProcessingEnabled(false); - } - - // Disable providers + nuke state - trace_session_.StopProviders(); - - if (pm_consumer_) { - pm_consumer_->ResetPresentTrackingData(true); - } -} +} \ No newline at end of file From 30836b47274af960526406e4d8e20bcdc18aca18 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Thu, 12 Feb 2026 17:02:19 -0800 Subject: [PATCH 08/11] UpdateTracking updated to work better with tracking providers --- .../RealtimePresentMonSession.cpp | 95 ++++++++++++------- .../RealtimePresentMonSession.h | 4 +- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index dd1d747b..1537a003 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -24,28 +24,72 @@ bool RealtimePresentMonSession::IsTraceSessionActive() { return session_active_.load(std::memory_order_acquire); } +// Transitions the session to an inactive state without tearing down the ETW session. +// Safe to call multiple times. +void RealtimePresentMonSession::StopProvidersAndResetConsumer(bool shrink) +{ + if (pm_consumer_) { + pm_consumer_->SetEventProcessingEnabled(false); + } + + trace_session_.StopProviders(); // idempotent per your note + + if (pm_consumer_) { + pm_consumer_->ResetPresentTrackingData(shrink); + } + + if (evtStreamingStarted_) { + evtStreamingStarted_.Reset(); + } +} + +bool RealtimePresentMonSession::IsEventSignaled(pmon::util::win::Event const& e) +{ + using namespace pmon::util::win; + + Event::HandleType h = e.Get(); + if (h == nullptr) return false; + + // Wait 0ms: returns index of signaled handle, or nullopt on timeout. + auto r = WaitOnMultipleEvents(std::span(&h, 1), false, 0); + return r.has_value(); // only one handle => signaled if it returned 0 +} + PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_set& trackedPids) { - const bool wasActive = HasLiveTargets(); + // Ensure ETW session exists (StartTraceW done once; providers may be off). + if (!IsTraceSessionActive()) { + // If the session isn't active, then we need to start it before we can update tracking. + auto const status = StartEtwSession(); + if (status != PM_STATUS_SUCCESS) { + return status; + } + } + + // Snapshot state so we can rollback tracking on failure. std::unordered_map previousState; { std::lock_guard lock(tracked_processes_mutex_); previousState = tracked_pid_live_; } + SyncTrackedPidState(trackedPids); const bool isActive = HasLiveTargets(); - if (isActive && (!wasActive || !IsTraceSessionActive())) { - // If the etw session is not active for some reason attempt to start it. - // This will only happen if the session was stopped or failed to start - // during initialization - auto status = StartEtwSession(); - if (status != PM_STATUS_SUCCESS) { - { - std::lock_guard lock(tracked_processes_mutex_); - tracked_pid_live_ = std::move(previousState); - } - return status; + bool const providersEnabled = IsEventSignaled(evtStreamingStarted_); + + // Stop transition: targets went from some->none; providers currently enabled + if(!isActive && providersEnabled) { + StopProvidersAndResetConsumer(true); + if (evtStreamingStarted_) { + evtStreamingStarted_.Reset(); } - // Enable app/PCL tracking before enabling providers so any immediately-arriving + return PM_STATUS::PM_STATUS_SUCCESS; + } + + // Start transition: targets went from none->some; providers currently disabled + // This also handles the case where there was a StartProviders failure for some + // reason. + if (isActive && !providersEnabled) { + // Enable present tracking before enabling providers so any immediately-arriving // events are accounted for by the quiesce logic on StopStreaming. if (pm_consumer_) { // Drop any lingering present tracking state from previous streams @@ -56,27 +100,19 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_setResetPresentTrackingData(true); - } - } + // No transition: either active with providers enabled, or inactive with providers disabled return PM_STATUS::PM_STATUS_SUCCESS; } @@ -86,10 +122,6 @@ bool RealtimePresentMonSession::CheckTraceSessions(bool forceTerminate) { ClearTrackedProcesses(); return true; } - if (!HasLiveTargets() && (IsTraceSessionActive() == true)) { - StopEtwSession(); - return true; - } return false; } @@ -507,9 +539,6 @@ void RealtimePresentMonSession::UpdateProcesses( } void RealtimePresentMonSession::HandleTerminatedProcess(uint32_t processId) { - // TODO(megalvan): Need to figure this out - // Close this process' CSV. - // CloseOutputCsv(processInfo); MarkProcessExited(processId); if (!HasLiveTrackedProcesses() && evtStreamingStarted_) { evtStreamingStarted_.Reset(); diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h index a49f8ac9..157d751e 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h @@ -51,8 +51,8 @@ class RealtimePresentMonSession : public PresentMonSession void CheckForTerminatedRealtimeProcesses( std::vector>* terminatedProcesses); - - void OnStreamStopped(); + void StopProvidersAndResetConsumer(bool shrink); + bool IsEventSignaled(pmon::util::win::Event const& e); // data std::wstring pm_session_name_; From f4b9f405cb9caeb87ee8f3e1cc111414180a9e43 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 13 Feb 2026 08:32:51 -0800 Subject: [PATCH 09/11] Adding in info logging to track when providers are enabled and disabled --- .../PresentMonService/RealtimePresentMonSession.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 1537a003..4dce9c8b 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -78,6 +78,7 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_setnone; providers currently enabled if(!isActive && providersEnabled) { + pmlog_info("All targets inactive: Disabling ETW Providers"); StopProvidersAndResetConsumer(true); if (evtStreamingStarted_) { evtStreamingStarted_.Reset(); @@ -97,9 +98,10 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_setSetEventProcessingEnabled(true); } - + pmlog_info("Active targets detected: Enabling ETW Providers"); auto const providerStatus = trace_session_.StartProviders(); if (providerStatus != ERROR_SUCCESS) { + pmlog_info("Enabling of ETW Providers failed"); StopProvidersAndResetConsumer(true); evtStreamingStarted_.Reset(); { From 1cd189150b38f9f26688b9b2265179e8369a6679 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 13 Feb 2026 10:27:21 -0800 Subject: [PATCH 10/11] Comment clean up --- .../PresentMonService/RealtimePresentMonSession.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 4dce9c8b..75573689 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -32,7 +32,7 @@ void RealtimePresentMonSession::StopProvidersAndResetConsumer(bool shrink) pm_consumer_->SetEventProcessingEnabled(false); } - trace_session_.StopProviders(); // idempotent per your note + trace_session_.StopProviders(); if (pm_consumer_) { pm_consumer_->ResetPresentTrackingData(shrink); @@ -52,7 +52,7 @@ bool RealtimePresentMonSession::IsEventSignaled(pmon::util::win::Event const& e) // Wait 0ms: returns index of signaled handle, or nullopt on timeout. auto r = WaitOnMultipleEvents(std::span(&h, 1), false, 0); - return r.has_value(); // only one handle => signaled if it returned 0 + return r.has_value(); } PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_set& trackedPids) { From 738d55965be1394bbc5ec5a15b0f9a0e155ea83c Mon Sep 17 00:00:00 2001 From: Chili Date: Tue, 17 Feb 2026 17:33:09 +0900 Subject: [PATCH 11/11] simplify event handling --- .../RealtimePresentMonSession.cpp | 28 ++++--------------- .../RealtimePresentMonSession.h | 1 - 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp index 75573689..f94f4c71 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.cpp @@ -38,21 +38,7 @@ void RealtimePresentMonSession::StopProvidersAndResetConsumer(bool shrink) pm_consumer_->ResetPresentTrackingData(shrink); } - if (evtStreamingStarted_) { - evtStreamingStarted_.Reset(); - } -} - -bool RealtimePresentMonSession::IsEventSignaled(pmon::util::win::Event const& e) -{ - using namespace pmon::util::win; - - Event::HandleType h = e.Get(); - if (h == nullptr) return false; - - // Wait 0ms: returns index of signaled handle, or nullopt on timeout. - auto r = WaitOnMultipleEvents(std::span(&h, 1), false, 0); - return r.has_value(); + evtStreamingStarted_.Reset(); } PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_set& trackedPids) { @@ -74,15 +60,13 @@ PM_STATUS RealtimePresentMonSession::UpdateTracking(const std::unordered_setnone; providers currently enabled if(!isActive && providersEnabled) { pmlog_info("All targets inactive: Disabling ETW Providers"); StopProvidersAndResetConsumer(true); - if (evtStreamingStarted_) { - evtStreamingStarted_.Reset(); - } + evtStreamingStarted_.Reset(); return PM_STATUS::PM_STATUS_SUCCESS; } @@ -244,9 +228,7 @@ void RealtimePresentMonSession::StopEtwSession() { // PHASE 2: Safe cleanup after threads have finished std::lock_guard lock(session_mutex_); - if (evtStreamingStarted_) { - evtStreamingStarted_.Reset(); - } + evtStreamingStarted_.Reset(); if (pm_consumer_) { pm_consumer_.reset(); @@ -542,7 +524,7 @@ void RealtimePresentMonSession::UpdateProcesses( void RealtimePresentMonSession::HandleTerminatedProcess(uint32_t processId) { MarkProcessExited(processId); - if (!HasLiveTrackedProcesses() && evtStreamingStarted_) { + if (!HasLiveTrackedProcesses()) { evtStreamingStarted_.Reset(); } } diff --git a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h index 157d751e..32e8c7e1 100644 --- a/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h +++ b/IntelPresentMon/PresentMonService/RealtimePresentMonSession.h @@ -52,7 +52,6 @@ class RealtimePresentMonSession : public PresentMonSession void CheckForTerminatedRealtimeProcesses( std::vector>* terminatedProcesses); void StopProvidersAndResetConsumer(bool shrink); - bool IsEventSignaled(pmon::util::win::Event const& e); // data std::wstring pm_session_name_;