From 253e1df43b6bfe55e9d85d59578abd95f424fb32 Mon Sep 17 00:00:00 2001 From: Brandon Yates Date: Wed, 27 Sep 2023 20:54:52 +0000 Subject: [PATCH] feature(debugger): Filter page fault threads by start IP When a thread is stopped due to potential page fault we must check AIP against start IP to ensure it is not a newly started thread accidentally caught by PF algorithm Related-to: NEO-8617 Signed-off-by: Brandon Yates --- .../tools/source/debug/debug_session_imp.cpp | 20 ++++++-- .../tools/source/debug/debug_session_imp.h | 2 + .../debug/linux/prelim/debug_session.cpp | 1 + .../sources/debug/debug_session_tests.cpp | 47 +++++++++++++++++++ .../sources/debug/mock_debug_session.h | 9 ++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/level_zero/tools/source/debug/debug_session_imp.cpp b/level_zero/tools/source/debug/debug_session_imp.cpp index 09f3ed8dcf..0c3ed6833a 100644 --- a/level_zero/tools/source/debug/debug_session_imp.cpp +++ b/level_zero/tools/source/debug/debug_session_imp.cpp @@ -808,6 +808,14 @@ bool DebugSessionImp::isForceExceptionOrForceExternalHaltOnlyExceptionReason(uin return (((cr0[1] & cr0ExceptionBitmask) & (~cr0ForcedExcpetionBitmask)) == 0); } +bool DebugSessionImp::isAIPequalToThreadStartIP(uint32_t *cr0, uint32_t *dbg0) { + if (cr0[2] == dbg0[0]) { + return true; + } else { + return false; + } +} + void DebugSessionImp::fillResumeAndStoppedThreadsFromNewlyStopped(std::vector &resumeThreads, std::vector &stoppedThreadsToReport, std::vector &interruptedThreads) { if (newlyStoppedThreads.empty()) { @@ -822,9 +830,15 @@ void DebugSessionImp::fillResumeAndStoppedThreadsFromNewlyStopped(std::vectorgetPageFault()) { - const uint32_t cr0PFBit16 = 0x10000; - reg[1] = reg[1] | cr0PFBit16; - writeRegistersImp(newlyStopped, ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU, 0, 1, reg.get()); + uint32_t dbgreg[64u] = {0}; + readRegistersImp(newlyStopped, ZET_DEBUG_REGSET_TYPE_DBG_INTEL_GPU, 0, 1, dbgreg); + if (isAIPequalToThreadStartIP(reg.get(), dbgreg)) { + PRINT_DEBUGGER_THREAD_LOG("Thread %s with PF AIP is equal to StartIP. Filtering out\n", allThreads[newlyStopped]->toString().c_str()); + } else { + const uint32_t cr0PFBit16 = 0x10000; + reg[1] = reg[1] | cr0PFBit16; + writeRegistersImp(newlyStopped, ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU, 0, 1, reg.get()); + } } if (isForceExceptionOrForceExternalHaltOnlyExceptionReason(reg.get())) { bool threadWasInterrupted = false; diff --git a/level_zero/tools/source/debug/debug_session_imp.h b/level_zero/tools/source/debug/debug_session_imp.h index d40296c764..333750e3bd 100644 --- a/level_zero/tools/source/debug/debug_session_imp.h +++ b/level_zero/tools/source/debug/debug_session_imp.h @@ -97,6 +97,8 @@ struct DebugSessionImp : DebugSession { ze_result_t readSbaRegisters(EuThread::ThreadId thread, uint32_t start, uint32_t count, void *pRegisterValues); MOCKABLE_VIRTUAL bool isForceExceptionOrForceExternalHaltOnlyExceptionReason(uint32_t *cr0); + MOCKABLE_VIRTUAL bool isAIPequalToThreadStartIP(uint32_t *cr0, uint32_t *dbg0); + void sendInterrupts(); MOCKABLE_VIRTUAL void addThreadToNewlyStoppedFromRaisedAttention(EuThread::ThreadId threadId, uint64_t memoryHandle, const void *stateSaveArea); MOCKABLE_VIRTUAL void fillResumeAndStoppedThreadsFromNewlyStopped(std::vector &resumeThreads, std::vector &stoppedThreadsToReport, std::vector &interruptedThreads); diff --git a/level_zero/tools/source/debug/linux/prelim/debug_session.cpp b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp index 27c59753d6..cb854aa72a 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp @@ -1420,6 +1420,7 @@ void DebugSessionLinux::handlePageFaultEvent(prelim_drm_i915_debug_event_page_fa lock = std::unique_lock(threadStateMutex); } for (auto threadId : threadsWithPF) { + PRINT_DEBUGGER_INFO_LOG("PageFault event for thread %s", EuThread::toString(threadId).c_str()); allThreads[threadId]->setPageFault(true); } diff --git a/level_zero/tools/test/unit_tests/sources/debug/debug_session_tests.cpp b/level_zero/tools/test/unit_tests/sources/debug/debug_session_tests.cpp index fafff66897..5b64d6ac6d 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/debug_session_tests.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/debug_session_tests.cpp @@ -756,6 +756,53 @@ TEST(DebugSessionTest, givenStoppedThreadsWhenFillingResumeAndStoppedThreadsFrom } } +TEST(DebugSessionTest, givenPFThreadWithAIPEqStartIPWhenCallingFillResumeAndStoppedThreadsFromNewlyStoppedThenCRIsNotWritten) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + auto hwInfo = *NEO::defaultHwInfo.get(); + + NEO::MockDevice *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + + auto sessionMock = std::make_unique(config, &deviceImp); + + EuThread::ThreadId thread = {0, 0, 0, 0, 1}; + + sessionMock->newlyStoppedThreads.push_back(thread); + sessionMock->onlyForceException = false; + sessionMock->forceAIPEqualStartIP = true; + + std::vector resumeThreads; + std::vector stoppedThreads; + std::vector interruptedThreads; + + sessionMock->allThreads[thread]->stopThread(1u); + sessionMock->allThreads[thread]->setPageFault(true); + + sessionMock->fillResumeAndStoppedThreadsFromNewlyStopped(resumeThreads, stoppedThreads, interruptedThreads); + EXPECT_EQ(0u, resumeThreads.size()); + EXPECT_EQ(1u, stoppedThreads.size()); + EXPECT_EQ(0u, sessionMock->writeRegistersCallCount); + EXPECT_EQ(false, sessionMock->allThreads[thread]->getPageFault()); +} + +TEST(DebugSessionTest, givenDbgRegAndCRThenisAIPequalToThreadStartIPReturnsCorrectResult) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + auto hwInfo = *NEO::defaultHwInfo.get(); + + NEO::MockDevice *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + + auto sessionMock = std::make_unique(config, &deviceImp); + sessionMock->callBaseisAIPequalToThreadStartIP = true; + uint32_t dbg[4] = {0xDEADBEEF, 0, 0, 0}; + uint32_t cr[4] = {0, 0, 0xDEADBEEF, 0}; + EXPECT_TRUE(sessionMock->isAIPequalToThreadStartIP(cr, dbg)); + cr[2] = 0x1234567; + EXPECT_FALSE(sessionMock->isAIPequalToThreadStartIP(cr, dbg)); +} + TEST(DebugSessionTest, givenThreadsStoppedWithPageFaultWhenCallingfillResumeAndStoppedThreadsFromNewlyStoppedThenCRIsWritten) { zet_debug_config_t config = {}; config.pid = 0x1234; diff --git a/level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h b/level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h index 4a136f3454..7325cd5ca3 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h +++ b/level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h @@ -323,6 +323,13 @@ struct MockDebugSession : public L0::DebugSessionImp { apiEventCondition.notify_all(); } + bool isAIPequalToThreadStartIP(uint32_t *cr0, uint32_t *dbg0) override { + if (callBaseisAIPequalToThreadStartIP) { + return DebugSessionImp::isAIPequalToThreadStartIP(cr0, dbg0); + } + return forceAIPEqualStartIP; + } + bool isForceExceptionOrForceExternalHaltOnlyExceptionReason(uint32_t *cr0) override { if (callBaseIsForceExceptionOrForceExternalHaltOnlyExceptionReason) { return isForceExceptionOrForceExternalHaltOnlyExceptionReasonBase(cr0); @@ -488,7 +495,9 @@ struct MockDebugSession : public L0::DebugSessionImp { ze_result_t interruptImpResult = ZE_RESULT_SUCCESS; ze_result_t resumeImpResult = ZE_RESULT_SUCCESS; bool onlyForceException = true; + bool forceAIPEqualStartIP = false; bool callBaseIsForceExceptionOrForceExternalHaltOnlyExceptionReason = false; + bool callBaseisAIPequalToThreadStartIP = false; bool threadStopped = true; int areRequestedThreadsStoppedReturnValue = -1; bool readSystemRoutineIdentRetVal = true;