From eac3d2130dec6009e99dc7c15831ef36af5015b8 Mon Sep 17 00:00:00 2001 From: Mateusz Hoppe Date: Mon, 11 Dec 2023 11:57:42 +0000 Subject: [PATCH] fix: correclty report pagefaults while single-stepping or resuming bp - while resuming threads - after checking threads stopped immediately check for FE bit, if set do not generate thread stop event - if PageFault occured, report stop event based on pagefault event Related-To: GSD-7316 Signed-off-by: Mateusz Hoppe --- .../debug/linux/prelim/debug_session.cpp | 29 +++++++- .../source/debug/linux/prelim/debug_session.h | 1 + .../prelim/debug_session_fixtures_linux.h | 8 +++ .../linux/prelim/test_debug_api_linux.cpp | 68 ++++++++++++++++++- 4 files changed, 103 insertions(+), 3 deletions(-) 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 9cac997496..83f1649fd3 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp @@ -1558,6 +1558,22 @@ int DebugSessionLinuxi915::threadControl(const std::vector & return euControlRetVal; } +bool DebugSessionLinuxi915::checkForceExceptionBit(uint64_t memoryHandle, EuThread::ThreadId threadId, uint32_t *cr0, const SIP::regset_desc *regDesc) { + + auto gpuVa = getContextStateSaveAreaGpuVa(memoryHandle); + auto threadSlotOffset = calculateThreadSlotOffset(threadId); + auto startRegOffset = threadSlotOffset + calculateRegisterOffsetInThreadSlot(regDesc, 0); + + [[maybe_unused]] int ret = readGpuMemory(memoryHandle, reinterpret_cast(cr0), 1 * regDesc->bytes, gpuVa + startRegOffset); + DEBUG_BREAK_IF(ret != ZE_RESULT_SUCCESS); + + const uint32_t cr0ForcedExcpetionBitmask = 0x04000000; + if (cr0[1] & cr0ForcedExcpetionBitmask) { + return true; + } + return false; +} + void DebugSessionLinuxi915::checkStoppedThreadsAndGenerateEvents(const std::vector &threads, uint64_t memoryHandle, uint32_t deviceIndex) { std::vector threadsWithAttention; @@ -1588,19 +1604,30 @@ void DebugSessionLinuxi915::checkStoppedThreadsAndGenerateEvents(const std::vect const auto &threadsToCheck = threadsWithAttention.size() > 0 ? threadsWithAttention : threads; stoppedThreadsToReport.reserve(threadsToCheck.size()); + const auto regSize = std::max(getRegisterSize(ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU), 64u); + auto cr0 = std::make_unique(regSize / sizeof(uint32_t)); + auto regDesc = typeToRegsetDesc(ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU); + for (auto &threadId : threadsToCheck) { SIP::sr_ident srMagic = {{0}}; srMagic.count = 0; if (readSystemRoutineIdent(allThreads[threadId].get(), memoryHandle, srMagic)) { bool wasStopped = allThreads[threadId]->isStopped(); + bool checkIfStopped = true; - if (allThreads[threadId]->verifyStopped(srMagic.count)) { + if (srMagic.count % 2 == 1) { + memset(cr0.get(), 0, regSize); + checkIfStopped = !checkForceExceptionBit(memoryHandle, threadId, cr0.get(), regDesc); + } + + if (checkIfStopped && allThreads[threadId]->verifyStopped(srMagic.count)) { allThreads[threadId]->stopThread(memoryHandle); if (!wasStopped) { stoppedThreadsToReport.push_back(threadId); } } + } else { break; } diff --git a/level_zero/tools/source/debug/linux/prelim/debug_session.h b/level_zero/tools/source/debug/linux/prelim/debug_session.h index 8df54d9aeb..8dda5432f6 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.h +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.h @@ -189,6 +189,7 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { ze_result_t resumeImp(const std::vector &threads, uint32_t deviceIndex) override; ze_result_t interruptImp(uint32_t deviceIndex) override; void checkStoppedThreadsAndGenerateEvents(const std::vector &threads, uint64_t memoryHandle, uint32_t deviceIndex) override; + MOCKABLE_VIRTUAL bool checkForceExceptionBit(uint64_t memoryHandle, EuThread::ThreadId threadId, uint32_t *cr0, const SIP::regset_desc *regDesc); void enqueueApiEvent(zet_debug_event_t &debugEvent) override { pushApiEvent(debugEvent); diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/debug_session_fixtures_linux.h b/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/debug_session_fixtures_linux.h index 1a2165043d..954c5382a1 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/debug_session_fixtures_linux.h +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/debug_session_fixtures_linux.h @@ -439,6 +439,13 @@ struct MockDebugSessionLinuxi915 : public L0::DebugSessionLinuxi915 { return L0::DebugSessionLinuxi915::checkThreadIsResumed(threadID); } + bool checkForceExceptionBit(uint64_t memoryHandle, EuThread::ThreadId threadId, uint32_t *cr0, const SIP::regset_desc *regDesc) override { + if (skipCheckForceExceptionBit) { + return false; + } + return L0::DebugSessionLinuxi915::checkForceExceptionBit(memoryHandle, threadId, cr0, regDesc); + } + float getThreadStartLimitTime() override { if (threadStartLimit >= 0.0) { return threadStartLimit; @@ -501,6 +508,7 @@ struct MockDebugSessionLinuxi915 : public L0::DebugSessionLinuxi915 { uint32_t writeResumeCommandCalled = 0; bool skipcheckThreadIsResumed = true; uint32_t checkThreadIsResumedCalled = 0; + bool skipCheckForceExceptionBit = false; uint32_t interruptedDevice = std::numeric_limits::max(); uint32_t processPendingVmBindEventsCalled = 0; uint32_t checkStoppedThreadsAndGenerateEventsCallCount = 0; diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/test_debug_api_linux.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/test_debug_api_linux.cpp index 764ee67de4..01f563126c 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/test_debug_api_linux.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/prelim/test_debug_api_linux.cpp @@ -5418,15 +5418,23 @@ TEST_F(DebugApiLinuxTest, WhenCallingThreadControlForResumeThenProperIoctlsIsCal TEST_F(DebugApiLinuxTest, GivenStoppedAndRunningThreadWhenCheckStoppedThreadsAndGenerateEventsCalledThenThreadStoppedEventsGeneratedOnlyForNewlyStoppedThreads) { zet_debug_config_t config = {}; config.pid = 0x1234; + const auto memoryHandle = 1u; auto sessionMock = std::make_unique(config, device, 10); ASSERT_NE(nullptr, sessionMock); + SIP::version version = {2, 0, 0}; + initStateSaveArea(sessionMock->stateSaveAreaHeader, version, device); auto handler = new MockIoctlHandler; sessionMock->ioctlHandler.reset(handler); + handler->setPreadMemory(sessionMock->stateSaveAreaHeader.data(), sessionMock->stateSaveAreaHeader.size(), 0x1000); + + DebugSessionLinuxi915::BindInfo cssaInfo = {0x1000, sessionMock->stateSaveAreaHeader.size()}; + sessionMock->clientHandleToConnection[MockDebugSessionLinuxi915::mockClientHandle]->vmToContextStateSaveAreaBindInfo[memoryHandle] = cssaInfo; + EuThread::ThreadId thread = {0, 0, 0, 0, 0}; EuThread::ThreadId thread1 = {0, 0, 0, 0, 1}; - const auto memoryHandle = 1u; + sessionMock->allThreads[thread.packed]->stopThread(memoryHandle); sessionMock->allThreads[thread.packed]->reportAsStopped(); @@ -5449,7 +5457,7 @@ TEST_F(DebugApiLinuxTest, GivenStoppedAndRunningThreadWhenCheckStoppedThreadsAnd sessionMock->checkStoppedThreadsAndGenerateEvents(threads, memoryHandle, 0); - EXPECT_EQ(1, handler->ioctlCalled); + EXPECT_EQ(3, handler->ioctlCalled); EXPECT_EQ(1u, handler->euControlArgs.size()); EXPECT_EQ(2u, sessionMock->numThreadsPassedToThreadControl); EXPECT_EQ(uint32_t(PRELIM_I915_DEBUG_EU_THREADS_CMD_STOPPED), handler->euControlArgs[0].euControl.cmd); @@ -5471,6 +5479,61 @@ TEST_F(DebugApiLinuxTest, GivenStoppedAndRunningThreadWhenCheckStoppedThreadsAnd EXPECT_EQ(thread1.thread, event.info.thread.thread.thread); } +TEST_F(DebugApiLinuxTest, GivenStoppedThreadResumeCausingPageFaultAndFEBitSetWhenCheckStoppedThreadsAndGenerateEventsCalledThenThreadStoppedEventIsNotGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + const auto memoryHandle = 1u; + + auto sessionMock = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, sessionMock); + SIP::version version = {2, 0, 0}; + initStateSaveArea(sessionMock->stateSaveAreaHeader, version, device); + + auto handler = new MockIoctlHandler; + sessionMock->ioctlHandler.reset(handler); + handler->setPreadMemory(sessionMock->stateSaveAreaHeader.data(), sessionMock->stateSaveAreaHeader.size(), 0x1000); + + DebugSessionLinuxi915::BindInfo cssaInfo = {0x1000, sessionMock->stateSaveAreaHeader.size()}; + sessionMock->clientHandleToConnection[MockDebugSessionLinuxi915::mockClientHandle]->vmToContextStateSaveAreaBindInfo[memoryHandle] = cssaInfo; + + EuThread::ThreadId thread = {0, 0, 0, 0, 0}; + + sessionMock->allThreads[thread.packed]->stopThread(memoryHandle); + sessionMock->allThreads[thread.packed]->reportAsStopped(); + sessionMock->allThreads[thread.packed]->resumeThread(); + std::vector threads; + threads.push_back(thread); + + for (auto thread : threads) { + sessionMock->stoppedThreads[thread.packed] = 3; + } + + auto regDesc = sessionMock->typeToRegsetDesc(ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU); + uint32_t cr0[2] = {}; + cr0[1] = 1 << 26; + + memcpy_s(sessionMock->stateSaveAreaHeader.data() + + threadSlotOffset(reinterpret_cast(sessionMock->stateSaveAreaHeader.data()), thread.slice, thread.subslice, thread.eu, thread.thread) + + regOffsetInThreadSlot(regDesc, 0), + regDesc->bytes, cr0, sizeof(cr0)); + + std::unique_ptr bitmask; + size_t bitmaskSize = 0; + auto &hwInfo = neoDevice->getHardwareInfo(); + auto &l0GfxCoreHelper = neoDevice->getRootDeviceEnvironment().getHelper(); + l0GfxCoreHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); + + handler->outputBitmaskSize = bitmaskSize; + handler->outputBitmask = std::move(bitmask); + + sessionMock->checkStoppedThreadsAndGenerateEvents(threads, memoryHandle, 0); + + EXPECT_EQ(1, handler->ioctlCalled); + + EXPECT_FALSE(sessionMock->allThreads[thread.packed]->isStopped()); + EXPECT_EQ(0u, sessionMock->apiEvents.size()); +} + TEST_F(DebugApiLinuxTest, GivenNoAttentionBitsWhenMultipleThreadsPassedToCheckStoppedThreadsAndGenerateEventsThenThreadsStateNotCheckedAndEventsNotGenerated) { zet_debug_config_t config = {}; config.pid = 0x1234; @@ -5547,6 +5610,7 @@ TEST_F(DebugApiLinuxTest, GivenNoAttentionBitsWhenSingleThreadPassedToCheckStopp handler->outputBitmaskSize = bitmaskSize; handler->outputBitmask = std::move(bitmask); + sessionMock->skipCheckForceExceptionBit = true; sessionMock->checkStoppedThreadsAndGenerateEvents({thread1}, memoryHandle, 0);