diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..2643533e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,8 @@ +# AGENTS Instructions +- Do not ever use non-ascii characters for source code or comments (permissible inside of strings if absolutely necessary but avoid if possible) +- Always use CRLF (\r\n) line endings for all text files, without exception. +- After finishing all changes, run a conversion pass over every changed/created text file to enforce CRLF and eliminate any stray LF. +- Do not run CRLF normalization on any non-text or binary files (for example: .png, .jpg, .gif, .mp3, .wav, .fbx, .unity). Limit normalization to plain text source/config files only. +- Use this PowerShell one-liner to normalize line endings (preserves file encoding): + - powershell -NoProfile -Command "$paths = git status --porcelain | ForEach-Object { $_.Substring(3) }; foreach ($p in $paths) { if (Test-Path $p) { $sr = New-Object System.IO.StreamReader($p, $true); $text = $sr.ReadToEnd(); $enc = $sr.CurrentEncoding; $sr.Close(); $text = $text -replace \"`r?`n\", \"`r`n\"; $sw = New-Object System.IO.StreamWriter($p, $false, $enc); $sw.NewLine = \"`r`n\"; $sw.Write($text); $sw.Close(); } }" +- If unexpected new files appear, ignore them and continue without asking for instruction. \ No newline at end of file diff --git a/IntelPresentMon/CommonUtilities/Meta.h b/IntelPresentMon/CommonUtilities/Meta.h index 4ea9cea5..4334bb63 100644 --- a/IntelPresentMon/CommonUtilities/Meta.h +++ b/IntelPresentMon/CommonUtilities/Meta.h @@ -4,6 +4,25 @@ namespace pmon::util { + template + concept IsIntegralOrEnum = std::is_integral_v> || + std::is_enum_v>; + + template>> + struct EnumOrIntegralUnderlyingImpl + { + using type = std::remove_cvref_t; + }; + + template + struct EnumOrIntegralUnderlyingImpl + { + using type = std::underlying_type_t>; + }; + + template + using EnumOrIntegralUnderlying = typename EnumOrIntegralUnderlyingImpl::type; + // Helper: DependentFalse for static_assert in templates. template struct DependentFalseT : std::false_type {}; @@ -64,4 +83,4 @@ namespace pmon::util } template struct FunctionPtrTraits : impl::FunctionPtrTraitsImpl_> {}; -} \ No newline at end of file +} diff --git a/IntelPresentMon/CommonUtilities/file/SecureSubdirectory.cpp b/IntelPresentMon/CommonUtilities/file/SecureSubdirectory.cpp index fa2745b4..ce80fcda 100644 --- a/IntelPresentMon/CommonUtilities/file/SecureSubdirectory.cpp +++ b/IntelPresentMon/CommonUtilities/file/SecureSubdirectory.cpp @@ -195,7 +195,7 @@ namespace pmon::util::file Remove(); } catch (...) { - pmlog_error("failed removing secure subdir"); + pmlog_error("failed removing secure subdir").pmwatch(path_.string()); } } } diff --git a/IntelPresentMon/CommonUtilities/log/EntryBuilder.h b/IntelPresentMon/CommonUtilities/log/EntryBuilder.h index a523db53..136d8ab6 100644 --- a/IntelPresentMon/CommonUtilities/log/EntryBuilder.h +++ b/IntelPresentMon/CommonUtilities/log/EntryBuilder.h @@ -1,5 +1,6 @@ -#pragma once +#pragma once #include "Entry.h" +#include "../Meta.h" #include #include #include @@ -54,11 +55,21 @@ namespace pmon::util::log return *this; } template - EntryBuilder& raise() + [[noreturn]] EntryBuilder& raise() { auto note = note_; commit_(); - throw Except(std::move(note)); + if constexpr (std::is_constructible_v) { + throw Except(ErrorCodeArg_{ errorCode_ }, std::move(note)); + } + else if constexpr (std::is_constructible_v) { + throw Except(std::move(note)); + } + else { + static_assert(::pmon::util::DependentFalse, + "EntryBuilder::raise requires exception type with (code, std::string) or (std::string) constructor."); + throw std::runtime_error{ "Generic Error /w reporting failure in log::raise" }; + } } EntryBuilder& mark(const TimePoint& tp) noexcept; EntryBuilder& note(std::string note = "") noexcept; @@ -92,6 +103,33 @@ namespace pmon::util::log return *this; } private: + struct ErrorCodeArg_ + { + const ErrorCode& code_; + template + requires ::pmon::util::IsIntegralOrEnum + operator T() const noexcept + { + using RawT = ::pmon::util::EnumOrIntegralUnderlying; + if constexpr (std::is_signed_v) { + if (auto value = code_.AsSigned()) { + return static_cast(static_cast(*value)); + } + if (auto value = code_.AsUnsigned()) { + return static_cast(static_cast(*value)); + } + } + else { + if (auto value = code_.AsUnsigned()) { + return static_cast(static_cast(*value)); + } + if (auto value = code_.AsSigned()) { + return static_cast(static_cast(*value)); + } + } + return static_cast(RawT{}); + } + }; // functions void commit_() noexcept; // data diff --git a/IntelPresentMon/Interprocess/source/act/SymmetricActionClient.h b/IntelPresentMon/Interprocess/source/act/SymmetricActionClient.h index e961ab49..72fcaf48 100644 --- a/IntelPresentMon/Interprocess/source/act/SymmetricActionClient.h +++ b/IntelPresentMon/Interprocess/source/act/SymmetricActionClient.h @@ -20,6 +20,9 @@ namespace pmon::ipc::act namespace as = boost::asio; using namespace as::experimental::awaitable_operators; + PM_DEFINE_EX(ActionClientError); + PM_DEFINE_EX_FROM(ActionClientError, ServerDroppedError); + template class SymmetricActionClient { @@ -53,20 +56,26 @@ namespace pmon::ipc::act template auto DispatchSync(Params&& params) { - assert(IsRunning()); + if (!IsRunning()) { + throw Except("Server dropped off (ioctx not running); cannot dispatch"); + } return stx_.pConn->DispatchSync(std::forward(params), ioctx_, stx_); } template - auto DispatchDetached(Params&& params) + void DispatchDetached(Params&& params) { - assert(IsRunning()); - return stx_.pConn->DispatchDetached(std::forward(params), ioctx_, stx_); + if (!IsRunning()) { + throw Except("Server dropped off (ioctx not running); cannot dispatch"); + } + stx_.pConn->DispatchDetached(std::forward(params), ioctx_, stx_); } template void DispatchWithContinuation(Params&& params, std::function&&, std::exception_ptr)> cont) { - assert(IsRunning()); - return stx_.pConn->DispatchWithContinuation(std::forward(params), ioctx_, stx_, std::move(cont)); + if (!IsRunning()) { + throw Except("Server dropped off (ioctx not running); cannot dispatch"); + } + stx_.pConn->DispatchWithContinuation(std::forward(params), ioctx_, stx_, std::move(cont)); } bool IsRunning() const { diff --git a/IntelPresentMon/PresentMonAPI2/PresentMonAPI.cpp b/IntelPresentMon/PresentMonAPI2/PresentMonAPI.cpp index 944d0263..57b2c487 100644 --- a/IntelPresentMon/PresentMonAPI2/PresentMonAPI.cpp +++ b/IntelPresentMon/PresentMonAPI2/PresentMonAPI.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include "../PresentMonMiddleware/ConcreteMiddleware.h" @@ -32,6 +32,16 @@ Middleware& LookupMiddleware_(const void* handle) throw util::Except(PM_STATUS_SESSION_NOT_OPEN); } } +Middleware& LookupMiddlewareCheckDropped_(const void* handle) +{ + auto& mid = LookupMiddleware_(handle); + if (!mid.ServiceConnected()) { + pmlog_error("Service dropped; proactive abort") + .code(PM_STATUS_SESSION_NOT_OPEN) + .raise(); + } + return mid; +} void DestroyMiddleware_(PM_SESSION_HANDLE handle) { @@ -193,7 +203,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmGetIntrospectionRoot(PM_SESSION_HANDLE handle pmlog_error("null outptr for introspection interface").diag(); return PM_STATUS_BAD_ARGUMENT; } - const auto pIntro = LookupMiddleware_(handle).GetIntrospectionData(); + const auto pIntro = LookupMiddlewareCheckDropped_(handle).GetIntrospectionData(); // we don't need the middleware to free introspection data // detaching like this (eliding handle mapping) will allow introspection data // to not obstruct cleanup of middleware @@ -267,7 +277,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmRegisterDynamicQuery(PM_SESSION_HANDLE sessio pmlog_error("zero length query element array").diag(); return PM_STATUS_BAD_ARGUMENT; } - const auto queryHandle = LookupMiddleware_(sessionHandle).RegisterDynamicQuery( + const auto queryHandle = LookupMiddlewareCheckDropped_(sessionHandle).RegisterDynamicQuery( {pElements, numElements}, windowSizeMs, metricOffsetMs); AddHandleMapping_(sessionHandle, queryHandle); *pQueryHandle = queryHandle; @@ -314,7 +324,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmPollDynamicQuery(PM_DYNAMIC_QUERY_HANDLE hand pmlog_error("swap chain in count is zero").diag(); return PM_STATUS_BAD_ARGUMENT; } - LookupMiddleware_(handle).PollDynamicQuery(handle, processId, pBlob, numSwapChains); + LookupMiddlewareCheckDropped_(handle).PollDynamicQuery(handle, processId, pBlob, numSwapChains); return PM_STATUS_SUCCESS; } catch (...) { @@ -335,7 +345,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmPollStaticQuery(PM_SESSION_HANDLE sessionHand pmlog_error("null ptr to blob").diag(); return PM_STATUS_BAD_ARGUMENT; } - LookupMiddleware_(sessionHandle).PollStaticQuery(*pElement, processId, pBlob); + LookupMiddlewareCheckDropped_(sessionHandle).PollStaticQuery(*pElement, processId, pBlob); return PM_STATUS_SUCCESS; } catch (...) { @@ -364,7 +374,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmRegisterFrameQuery(PM_SESSION_HANDLE sessionH pmlog_error("zero blob size").diag(); return PM_STATUS_BAD_ARGUMENT; } - const auto queryHandle = LookupMiddleware_(sessionHandle).RegisterFrameEventQuery({ pElements, numElements }, *pBlobSize); + const auto queryHandle = LookupMiddlewareCheckDropped_(sessionHandle).RegisterFrameEventQuery({ pElements, numElements }, *pBlobSize); AddHandleMapping_(sessionHandle, queryHandle); *pQueryHandle = queryHandle; return PM_STATUS_SUCCESS; @@ -387,7 +397,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmConsumeFrames(PM_FRAME_QUERY_HANDLE handle, u pmlog_error("null frame count in-out ptr").diag(); return PM_STATUS_BAD_ARGUMENT; } - LookupMiddleware_(handle).ConsumeFrameEvents(handle, processId, pBlob, *pNumFramesToRead); + LookupMiddlewareCheckDropped_(handle).ConsumeFrameEvents(handle, processId, pBlob, *pNumFramesToRead); return PM_STATUS_SUCCESS; } catch (...) { @@ -431,7 +441,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmGetApiVersion(PM_VERSION* pVersion) PRESENTMON_API2_EXPORT PM_STATUS pmStopPlayback_(PM_SESSION_HANDLE handle) { try { - auto& mid = LookupMiddleware_(handle); + auto& mid = LookupMiddlewareCheckDropped_(handle); mid.StopPlayback(); return PM_STATUS_SUCCESS; } @@ -446,7 +456,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmStartEtlLogging(PM_SESSION_HANDLE session, PM uint64_t reserved1, uint64_t reserved2) { try { - auto& mid = LookupMiddleware_(session); + auto& mid = LookupMiddlewareCheckDropped_(session); *pEtlHandle = mid.StartEtlLogging(); return PM_STATUS_SUCCESS; } @@ -461,7 +471,7 @@ PRESENTMON_API2_EXPORT PM_STATUS pmFinishEtlLogging(PM_SESSION_HANDLE session, P char* pOutputFilePathBuffer, uint32_t bufferSize) { try { - auto& mid = LookupMiddleware_(session); + auto& mid = LookupMiddlewareCheckDropped_(session); const auto path = mid.FinishEtlLogging(etlHandle); if (path.size() + 1 > bufferSize) { const auto code = PM_STATUS_INSUFFICIENT_BUFFER; diff --git a/IntelPresentMon/PresentMonAPI2Tests/MultiClientTests.cpp b/IntelPresentMon/PresentMonAPI2Tests/MultiClientTests.cpp index fc737fe4..b47d6e6e 100644 --- a/IntelPresentMon/PresentMonAPI2Tests/MultiClientTests.cpp +++ b/IntelPresentMon/PresentMonAPI2Tests/MultiClientTests.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2023 Intel Corporation +// Copyright (C) 2022-2023 Intel Corporation // SPDX-License-Identifier: MIT #include "../CommonUtilities/win/WinAPI.h" #include "CppUnitTest.h" @@ -552,4 +552,87 @@ namespace MultiClientTests } } }; -} \ No newline at end of file + + TEST_CLASS(ServiceCrashTests) + { + private: + class Fixture_ : public CommonTestFixture + { + protected: + const CommonProcessArgs& GetCommonArgs() const override + { + static CommonProcessArgs args{ + .ctrlPipe = R"(\\.\pipe\pm-multi-test-ctrl)", + .introNsm = "pm_multi_test_intro", + .frameNsm = "pm_multi_test_nsm", + .logLevel = "debug", + .logFolder = logFolder_, + .sampleClientMode = "ServiceCrashClient", + }; + return args; + } + } fixture_; + static constexpr auto clientExitTimeout_ = 3s; + + void RunCrashCase_(const std::vector& args) + { + auto client = fixture_.LaunchClient(args); + + fixture_.StopService(); + + Assert::AreEqual("exit-ack"s, client.Command("exit")); + if (!client.WaitForExit(clientExitTimeout_)) { + client.Murder(); + Assert::Fail(L"Client did not exit after service termination"); + } + } + + void RunCrashCase_(pmon::test::client::CrashPhase phase) + { + RunCrashCase_({ + "--submode"s, std::to_string(static_cast(phase)), + }); + } + + void RunCrashCaseWithPresenter_(pmon::test::client::CrashPhase phase) + { + auto presenter = fixture_.LaunchPresenter(); + std::this_thread::sleep_for(30ms); + + RunCrashCase_({ + "--submode"s, std::to_string(static_cast(phase)), + "--process-id"s, std::to_string(presenter.GetId()), + }); + } + + public: + TEST_METHOD_INITIALIZE(Setup) + { + fixture_.Setup(); + } + TEST_METHOD_CLEANUP(Cleanup) + { + fixture_.Cleanup(); + } + // service drops while client has a session open + TEST_METHOD(SessionOpen) + { + RunCrashCase_(pmon::test::client::CrashPhase::SessionOpen); + } + // service drops while client has a registered query + TEST_METHOD(QueryRegistered) + { + RunCrashCase_(pmon::test::client::CrashPhase::QueryRegistered); + } + // service drops while client is tracking a target + TEST_METHOD(TargetTracked) + { + RunCrashCaseWithPresenter_(pmon::test::client::CrashPhase::TargetTracked); + } + // service drops while client is polling a query/target + TEST_METHOD(QueryPolling) + { + RunCrashCaseWithPresenter_(pmon::test::client::CrashPhase::QueryPolling); + } + }; +} diff --git a/IntelPresentMon/PresentMonAPI2Tests/TestCommands.h b/IntelPresentMon/PresentMonAPI2Tests/TestCommands.h index 7879b699..21b079f9 100644 --- a/IntelPresentMon/PresentMonAPI2Tests/TestCommands.h +++ b/IntelPresentMon/PresentMonAPI2Tests/TestCommands.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include #include @@ -28,6 +28,14 @@ namespace pmon::test namespace client { + enum class CrashPhase + { + SessionOpen = 0, + QueryRegistered = 1, + TargetTracked = 2, + QueryPolling = 3, + }; + struct Frame { double receivedTime; @@ -55,4 +63,4 @@ namespace pmon::test } }; } -} \ No newline at end of file +} diff --git a/IntelPresentMon/PresentMonAPI2Tests/TestProcess.h b/IntelPresentMon/PresentMonAPI2Tests/TestProcess.h index d2c6d02f..81b8ca06 100644 --- a/IntelPresentMon/PresentMonAPI2Tests/TestProcess.h +++ b/IntelPresentMon/PresentMonAPI2Tests/TestProcess.h @@ -1,4 +1,4 @@ -// Copyright (C) 2022-2023 Intel Corporation +// Copyright (C) 2022-2023 Intel Corporation // SPDX-License-Identifier: MIT #pragma once #include "../CommonUtilities/win/WinAPI.h" @@ -13,6 +13,7 @@ #include #include #include +#include using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace as = boost::asio; @@ -69,6 +70,19 @@ class TestProcess { process_.wait(); } + bool WaitForExit(std::chrono::milliseconds timeout) + { + if (!process_.running()) { + return true; + } + const auto waitResult = WaitForSingleObject(process_.native_handle(), + static_cast(timeout.count())); + if (waitResult == WAIT_OBJECT_0) { + process_.wait(); + return true; + } + return false; + } std::string Command(const std::string& command) { const auto prefix = GetCommandPrefix_(); @@ -267,6 +281,10 @@ class CommonTestFixture StopService_(GetCommonArgs()); ioctxRunThread_.join(); } + void StopService() + { + StopService_(GetCommonArgs()); + } void RebootService(std::optional> newArgs = {}) { auto& common = GetCommonArgs(); @@ -325,4 +343,4 @@ class CommonTestFixture JobManager jobMan_; std::thread ioctxRunThread_; as::io_context ioctx_; -}; \ No newline at end of file +}; diff --git a/IntelPresentMon/PresentMonAPIWrapper/BlobContainer.cpp b/IntelPresentMon/PresentMonAPIWrapper/BlobContainer.cpp index 5b116639..1efb2d90 100644 --- a/IntelPresentMon/PresentMonAPIWrapper/BlobContainer.cpp +++ b/IntelPresentMon/PresentMonAPIWrapper/BlobContainer.cpp @@ -79,9 +79,16 @@ namespace pmapi return const_cast(*this)[index]; } - std::vector::const_iterator BlobContainer::begin() const { return blobPointers_.begin(); } + std::vector::const_iterator BlobContainer::begin() const + { + return blobPointers_.begin(); + } - std::vector::const_iterator BlobContainer::end() const { return blobPointers_.begin() + GetNumBlobsPopulated(); } + std::vector::const_iterator BlobContainer::end() const + { + return blobPointers_.begin() + + std::min(GetNumBlobsPopulated(), GetBlobCount()); + } bool BlobContainer::AllBlobsPopulated() const { return nBlobsFilledInOut_ == nBlobs_; } diff --git a/IntelPresentMon/PresentMonAPIWrapper/DynamicQuery.cpp b/IntelPresentMon/PresentMonAPIWrapper/DynamicQuery.cpp index 13558c5a..3db87161 100644 --- a/IntelPresentMon/PresentMonAPIWrapper/DynamicQuery.cpp +++ b/IntelPresentMon/PresentMonAPIWrapper/DynamicQuery.cpp @@ -7,7 +7,11 @@ namespace pmapi { - DynamicQuery::~DynamicQuery() { Reset(); } + DynamicQuery::~DynamicQuery() + { + try { Reset(); } + catch (...) {} + } DynamicQuery::DynamicQuery(DynamicQuery&& other) noexcept { diff --git a/IntelPresentMon/PresentMonAPIWrapper/EtlLogger.cpp b/IntelPresentMon/PresentMonAPIWrapper/EtlLogger.cpp index f4112b43..8a3ccd67 100644 --- a/IntelPresentMon/PresentMonAPIWrapper/EtlLogger.cpp +++ b/IntelPresentMon/PresentMonAPIWrapper/EtlLogger.cpp @@ -6,7 +6,8 @@ namespace pmapi // stops tracking the associated process EtlLogger::~EtlLogger() { - Reset(); + try { Reset(); } + catch (...) {} } // move ctor EtlLogger::EtlLogger(EtlLogger&& other) noexcept diff --git a/IntelPresentMon/PresentMonAPIWrapper/FrameQuery.cpp b/IntelPresentMon/PresentMonAPIWrapper/FrameQuery.cpp index 2f1d3547..d54fa93e 100644 --- a/IntelPresentMon/PresentMonAPIWrapper/FrameQuery.cpp +++ b/IntelPresentMon/PresentMonAPIWrapper/FrameQuery.cpp @@ -8,7 +8,11 @@ namespace pmapi { - FrameQuery::~FrameQuery() { Reset(); } + FrameQuery::~FrameQuery() + { + try { Reset(); } + catch (...) {} + } FrameQuery::FrameQuery(FrameQuery&& other) noexcept { diff --git a/IntelPresentMon/PresentMonAPIWrapper/ProcessTracker.cpp b/IntelPresentMon/PresentMonAPIWrapper/ProcessTracker.cpp index 7032941a..b65a1cd9 100644 --- a/IntelPresentMon/PresentMonAPIWrapper/ProcessTracker.cpp +++ b/IntelPresentMon/PresentMonAPIWrapper/ProcessTracker.cpp @@ -7,7 +7,11 @@ namespace pmapi { - ProcessTracker::~ProcessTracker() { Reset(); } + ProcessTracker::~ProcessTracker() + { + try { Reset(); } + catch (...) {} + } ProcessTracker::ProcessTracker(ProcessTracker&& other) noexcept { diff --git a/IntelPresentMon/PresentMonAPIWrapper/Session.cpp b/IntelPresentMon/PresentMonAPIWrapper/Session.cpp index 12890ba1..355bff97 100644 --- a/IntelPresentMon/PresentMonAPIWrapper/Session.cpp +++ b/IntelPresentMon/PresentMonAPIWrapper/Session.cpp @@ -42,7 +42,10 @@ namespace pmapi return *this; } - Session::~Session() { Reset(); } + Session::~Session() + { + try { Reset(); } catch (...) {} + } void Session::Reset() noexcept { diff --git a/IntelPresentMon/PresentMonMiddleware/ActionClient.h b/IntelPresentMon/PresentMonMiddleware/ActionClient.h index 640c97d0..38cc6b5a 100644 --- a/IntelPresentMon/PresentMonMiddleware/ActionClient.h +++ b/IntelPresentMon/PresentMonMiddleware/ActionClient.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "../CommonUtilities/win/WinAPI.h" #include "../CommonUtilities/ref/StaticReflection.h" #include "../Interprocess/source/PmStatusError.h" @@ -55,5 +55,39 @@ namespace pmon::mid pmlog_info(std::format("Opened session with server, pid = [{}]", res.servicePid)); EstablishSession_(res.servicePid); } + template + auto DispatchSync(Params&& params) + { + // convert action client ipc error into presentmon api error + try { + return ClientBase::DispatchSync(std::forward(params)); + } + catch (const ipc::act::ServerDroppedError& e) { + pmlog_error(e.GetNote()).code(PM_STATUS_SESSION_NOT_OPEN); + throw util::Except(PM_STATUS_SESSION_NOT_OPEN, e.GetNote()); + } + } + template + void DispatchDetached(Params&& params) + { + // convert action client ipc error into presentmon api error + try { + ClientBase::DispatchDetached(std::forward(params)); + } + catch (const ipc::act::ServerDroppedError& e) { + pmlog_error(e.GetNote()).code(PM_STATUS_SESSION_NOT_OPEN).raise(); + } + } + template + void DispatchWithContinuation(Params&& params, std::function&&, std::exception_ptr)> cont) + { + // convert action client ipc error into presentmon api error + try { + ClientBase::DispatchWithContinuation(std::forward(params), std::move(cont)); + } + catch (const ipc::act::ServerDroppedError& e) { + pmlog_error(e.GetNote()).code(PM_STATUS_SESSION_NOT_OPEN).raise(); + } + } }; -} \ No newline at end of file +} diff --git a/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.cpp b/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.cpp index b21e54e4..deff4ad7 100644 --- a/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.cpp +++ b/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Intel Corporation +// Copyright (C) 2017-2024 Intel Corporation // SPDX-License-Identifier: MIT #include "ConcreteMiddleware.h" #include @@ -1426,6 +1426,11 @@ static void ReportMetrics( pActionClient->DispatchSync(StopPlayback::Params{}); } + bool ConcreteMiddleware::ServiceConnected() const + { + return pActionClient->IsRunning(); + } + uint32_t ConcreteMiddleware::StartEtlLogging() { return pActionClient->DispatchSync(StartEtlLogging::Params{}).etwLogSessionHandle; diff --git a/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.h b/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.h index 1e24419a..3e5be529 100644 --- a/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.h +++ b/IntelPresentMon/PresentMonMiddleware/ConcreteMiddleware.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "../CommonUtilities/win/WinAPI.h" #include "Middleware.h" #include "../Interprocess/source/Interprocess.h" @@ -186,6 +186,7 @@ namespace pmon::mid void StopPlayback() override; uint32_t StartEtlLogging() override; std::string FinishEtlLogging(uint32_t etlLogSessionHandle) override; + bool ServiceConnected() const override; private: PmNsmFrameData* GetFrameDataStart(StreamClient* client, uint64_t& index, uint64_t dataOffset, uint64_t& queryFrameDataDelta, double& windowSampleSizeMs); uint64_t GetAdjustedQpc(uint64_t current_qpc, uint64_t frame_data_qpc, uint64_t queryMetricsOffset, LARGE_INTEGER frequency, uint64_t& queryFrameDataDelta); diff --git a/IntelPresentMon/PresentMonMiddleware/Middleware.h b/IntelPresentMon/PresentMonMiddleware/Middleware.h index 3c9ffd46..39517b80 100644 --- a/IntelPresentMon/PresentMonMiddleware/Middleware.h +++ b/IntelPresentMon/PresentMonMiddleware/Middleware.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "../PresentMonAPI2/PresentMonAPI.h" #include #include @@ -27,5 +27,6 @@ namespace pmon::mid virtual void StopPlayback() = 0; virtual uint32_t StartEtlLogging() = 0; virtual std::string FinishEtlLogging(uint32_t etlLogSessionHandle) = 0; + virtual bool ServiceConnected() const { return false; } }; } \ No newline at end of file diff --git a/IntelPresentMon/SampleClient/CliOptions.h b/IntelPresentMon/SampleClient/CliOptions.h index 10edc478..72399976 100644 --- a/IntelPresentMon/SampleClient/CliOptions.h +++ b/IntelPresentMon/SampleClient/CliOptions.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "../CommonUtilities/cli/CliFramework.h" #include "../CommonUtilities/log/Level.h" #include "../CommonUtilities/ref/StaticReflection.h" @@ -22,6 +22,7 @@ namespace clio PlaybackDynamicQuery, IntrospectAllDynamicOptions, MultiClient, + ServiceCrashClient, EtlLogger, PacedPlayback, Count, @@ -71,4 +72,4 @@ namespace clio MutualExclusion logListExclusion_{ logDenyList, logAllowList }; Mandatory mandatoryMode_{ mode }; }; -} \ No newline at end of file +} diff --git a/IntelPresentMon/SampleClient/SampleClient.cpp b/IntelPresentMon/SampleClient/SampleClient.cpp index b8390fd6..95dc7d13 100644 --- a/IntelPresentMon/SampleClient/SampleClient.cpp +++ b/IntelPresentMon/SampleClient/SampleClient.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Intel Corporation +// Copyright (C) 2022 Intel Corporation // SPDX-License-Identifier: MIT #include "../CommonUtilities/win/WinAPI.h" #include @@ -32,6 +32,7 @@ #include "WrapperStaticQuery.h" #include "MetricListSample.h" #include "MultiClient.h" +#include "ServiceCrashClient.h" #include "EtlLogger.h" #include "PacedPlayback.h" #include "LogDemo.h" @@ -372,6 +373,8 @@ int main(int argc, char* argv[]) return FrameQuerySample(ConnectSession(), true); case clio::Mode::MultiClient: return MultiClientTest(ConnectSession()); + case clio::Mode::ServiceCrashClient: + return ServiceCrashClientTest(ConnectSession()); case clio::Mode::EtlLogger: return EtlLoggerTest(ConnectSession()); case clio::Mode::PacedPlayback: @@ -387,11 +390,11 @@ int main(int argc, char* argv[]) } } catch (...) { - const auto exr = pmon::util::ReportException(); + const auto exr = pmon::util::ReportException("Exception caught in SampleClient main"); std::cout << "Error: " << exr.first << std::endl; pmlog_error(exr); return -1; } return 0; -} \ No newline at end of file +} diff --git a/IntelPresentMon/SampleClient/SampleClient.vcxproj b/IntelPresentMon/SampleClient/SampleClient.vcxproj index ec564cb9..55056d37 100644 --- a/IntelPresentMon/SampleClient/SampleClient.vcxproj +++ b/IntelPresentMon/SampleClient/SampleClient.vcxproj @@ -1,4 +1,4 @@ - + @@ -112,6 +112,7 @@ + @@ -128,6 +129,7 @@ + @@ -146,4 +148,4 @@ - \ No newline at end of file + diff --git a/IntelPresentMon/SampleClient/SampleClient.vcxproj.filters b/IntelPresentMon/SampleClient/SampleClient.vcxproj.filters index f99ae36f..96c9847b 100644 --- a/IntelPresentMon/SampleClient/SampleClient.vcxproj.filters +++ b/IntelPresentMon/SampleClient/SampleClient.vcxproj.filters @@ -7,6 +7,7 @@ + @@ -24,7 +25,8 @@ + - \ No newline at end of file + diff --git a/IntelPresentMon/SampleClient/ServiceCrashClient.cpp b/IntelPresentMon/SampleClient/ServiceCrashClient.cpp new file mode 100644 index 00000000..91553bc9 --- /dev/null +++ b/IntelPresentMon/SampleClient/ServiceCrashClient.cpp @@ -0,0 +1,111 @@ +#include "ServiceCrashClient.h" +#include "CliOptions.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std::literals; +using namespace pmon::test::client; + +namespace +{ + CrashPhase ClampPhase(int rawPhase) + { + if (rawPhase <= static_cast(CrashPhase::SessionOpen)) { + return CrashPhase::SessionOpen; + } + if (rawPhase >= static_cast(CrashPhase::QueryPolling)) { + return CrashPhase::QueryPolling; + } + return static_cast(rawPhase); + } +} + +int ServiceCrashClientTest(std::unique_ptr pSession) +{ + auto& opt = clio::Options::Get(); + const auto phase = ClampPhase(*opt.submode); + + const auto WaitForPing = [] { + std::string line; + if (!std::getline(std::cin, line) || line != "%ping") { + std::cout << "%%{ping-error}%%" << std::endl; + return false; + } + std::cout << "%%{ping-ok}%%" << std::endl; + return true; + }; + + const auto WaitForExit = [] { + std::string line; + while (std::getline(std::cin, line)) { + if (line == "%exit") { + std::cout << "%%{exit-ack}%%" << std::endl; + return true; + } + if (line == "%quit") { + std::cout << "%%{quit-ok}%%" << std::endl; + return true; + } + std::cout << "%%{err-bad-command}%%" << std::endl; + } + return false; + }; + + if (phase >= CrashPhase::QueryRegistered) { + pmapi::ProcessTracker tracker; + + PM_BEGIN_FIXED_FRAME_QUERY(CrashFrameQuery) + pmapi::FixedQueryElement cpuStartTime{ this, PM_METRIC_CPU_START_TIME }; + PM_END_FIXED_QUERY query{ *pSession, 32 }; + + if (phase >= CrashPhase::TargetTracked) { + if (!opt.processId) { + std::cout << "%%{err-missing-process-id}%%" << std::endl; + return -1; + } + tracker = pSession->TrackProcess(*opt.processId); + } + + if (!WaitForPing()) { + return -1; + } + + if (phase >= CrashPhase::QueryPolling && tracker) { + std::atomic exitRequested{ false }; + std::atomic exitOk{ false }; + + std::jthread commandThread{ [&] { + exitOk = WaitForExit(); + exitRequested = true; + } }; + + while (!exitRequested) { + query.ForEachConsume(tracker, [] {}); + std::this_thread::sleep_for(50ms); + } + + if (!exitOk) { + return -1; + } + } + else if (!WaitForExit()) { + return -1; + } + } + else { + if (!WaitForPing()) { + return -1; + } + if (!WaitForExit()) { + return -1; + } + } + + std::this_thread::sleep_for(10ms); + return 0; +} diff --git a/IntelPresentMon/SampleClient/ServiceCrashClient.h b/IntelPresentMon/SampleClient/ServiceCrashClient.h new file mode 100644 index 00000000..d7d884cd --- /dev/null +++ b/IntelPresentMon/SampleClient/ServiceCrashClient.h @@ -0,0 +1,9 @@ +#pragma once +#include + +namespace pmapi +{ + class Session; +} + +int ServiceCrashClientTest(std::unique_ptr pSession);