diff --git a/level_zero/tools/source/debug/windows/debug_session.cpp b/level_zero/tools/source/debug/windows/debug_session.cpp index 6acf1542dd..ea3507f1f8 100644 --- a/level_zero/tools/source/debug/windows/debug_session.cpp +++ b/level_zero/tools/source/debug/windows/debug_session.cpp @@ -628,6 +628,17 @@ ze_result_t DebugSessionWindows::resumeImp(const std::vector l0GfxCoreHelper.getAttentionBitmaskForSingleThreads(threads, hwInfo, bitmask, bitmaskSize); printBitmask(bitmask.get(), bitmaskSize); + if (l0GfxCoreHelper.threadResumeRequiresUnlock()) { + uint64_t memoryHandle = allThreads[threads[0]]->getMemoryHandle(); + auto result = resumeContextImp(memoryHandle); + if (result != ZE_RESULT_SUCCESS) { + return result; + } + + result = continueExecutionImp(memoryHandle); + return result; + } + KM_ESCAPE_INFO escapeInfo = {}; escapeInfo.KmEuDbgL0EscapeInfo.EscapeActionType = DBGUMD_ACTION_EU_CONTROL_CLR_ATT_BIT; escapeInfo.KmEuDbgL0EscapeInfo.EuControlClrAttBitParams.BitmaskArrayPtr = reinterpret_cast(bitmask.get()); @@ -649,6 +660,10 @@ ze_result_t DebugSessionWindows::resumeImp(const std::vector } ze_result_t DebugSessionWindows::interruptImp(uint32_t deviceIndex) { + auto &l0GfxCoreHelper = connectedDevice->getNEODevice()->getRootDeviceEnvironment().getHelper(); + if (l0GfxCoreHelper.threadResumeRequiresUnlock()) { + return interruptContextImp(); + } KM_ESCAPE_INFO escapeInfo = {}; escapeInfo.KmEuDbgL0EscapeInfo.EscapeActionType = DBGUMD_ACTION_EU_CONTROL_INT_ALL; @@ -667,6 +682,85 @@ ze_result_t DebugSessionWindows::interruptImp(uint32_t deviceIndex) { return ZE_RESULT_SUCCESS; } +ze_result_t DebugSessionWindows::interruptContextImp() { + + uint64_t memoryHandle = DebugSessionWindows::invalidHandle; + { + std::unique_lock lock(asyncThreadMutex); + if (allContexts.empty()) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + memoryHandle = *allContexts.begin(); + } + if (memoryHandle == EuThread::invalidHandle) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + KM_ESCAPE_INFO escapeInfo = {}; + escapeInfo.KmEuDbgL0EscapeInfo.EscapeActionType = DBGUMD_ACTION_EU_CTRL_INTR_REQUEST; + escapeInfo.KmEuDbgL0EscapeInfo.EuControlParams.hContextHandle = memoryHandle; + + auto status = runEscape(escapeInfo); + if (STATUS_SUCCESS != status) { + PRINT_DEBUGGER_ERROR_LOG("DBGUMD_ACTION_EU_CTRL_INTR_REQUEST: Failed - Status: 0x%llX EscapeReturnStatus: %d\n", status, escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus); + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + if (DBGUMD_RETURN_ESCAPE_SUCCESS != escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus) { + PRINT_DEBUGGER_ERROR_LOG("DBGUMD_ACTION_EU_CTRL_INTR_REQUEST: Failed - Status: 0x%llX EscapeReturnStatus: %d\n", status, escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus); + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + PRINT_DEBUGGER_INFO_LOG("DBGUMD_ACTION_EU_CTRL_INTR_REQUEST - Success\n"); + return ZE_RESULT_SUCCESS; +} + +ze_result_t DebugSessionWindows::resumeContextImp(uint64_t memoryHandle) { + + if (memoryHandle == DebugSessionWindows::invalidHandle) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + KM_ESCAPE_INFO escapeInfo = {}; + escapeInfo.KmEuDbgL0EscapeInfo.EscapeActionType = DBGUMD_ACTION_EU_CTRL_INTR_RESUME; + escapeInfo.KmEuDbgL0EscapeInfo.EuControlParams.hContextHandle = memoryHandle; + + auto status = runEscape(escapeInfo); + if (STATUS_SUCCESS != status) { + PRINT_DEBUGGER_ERROR_LOG("DBGUMD_ACTION_EU_CTRL_INTR_RESUME: Failed - Status: 0x%llX EscapeReturnStatus: %d\n", status, escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus); + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + if (DBGUMD_RETURN_ESCAPE_SUCCESS != escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus) { + PRINT_DEBUGGER_ERROR_LOG("DBGUMD_ACTION_EU_CTRL_INTR_RESUME: Failed - Status: 0x%llX EscapeReturnStatus: %d\n", status, escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus); + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + PRINT_DEBUGGER_INFO_LOG("DBGUMD_ACTION_EU_CTRL_INTR_RESUME - Success\n"); + return ZE_RESULT_SUCCESS; +} + +ze_result_t DebugSessionWindows::continueExecutionImp(uint64_t memoryHandle) { + if (memoryHandle == DebugSessionWindows::invalidHandle) { + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + KM_ESCAPE_INFO escapeInfo = {}; + escapeInfo.KmEuDbgL0EscapeInfo.EscapeActionType = DBGUMD_ACTION_EU_CTRL_CONT_EXECUTION; + escapeInfo.KmEuDbgL0EscapeInfo.EuControlParams.hContextHandle = memoryHandle; + + auto status = runEscape(escapeInfo); + if (STATUS_SUCCESS != status) { + PRINT_DEBUGGER_ERROR_LOG("DBGUMD_ACTION_EU_CTRL_CONT_EXECUTION: Failed - Status: 0x%llX EscapeReturnStatus: %d\n", status, escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus); + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + if (DBGUMD_RETURN_ESCAPE_SUCCESS != escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus) { + PRINT_DEBUGGER_ERROR_LOG("DBGUMD_ACTION_EU_CTRL_CONT_EXECUTION: Failed - Status: 0x%llX EscapeReturnStatus: %d\n", status, escapeInfo.KmEuDbgL0EscapeInfo.EscapeReturnStatus); + return ZE_RESULT_ERROR_NOT_AVAILABLE; + } + + PRINT_DEBUGGER_INFO_LOG("DBGUMD_ACTION_EU_CTRL_INTR_REQUEST - Success\n"); + return ZE_RESULT_SUCCESS; +} + ze_result_t DebugSessionWindows::readGpuMemory(uint64_t memoryHandle, char *output, size_t size, uint64_t gpuVa) { auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); diff --git a/level_zero/tools/source/debug/windows/debug_session.h b/level_zero/tools/source/debug/windows/debug_session.h index 6569284daa..f85bc543c2 100644 --- a/level_zero/tools/source/debug/windows/debug_session.h +++ b/level_zero/tools/source/debug/windows/debug_session.h @@ -57,6 +57,10 @@ struct DebugSessionWindows : DebugSessionImp { ze_result_t interruptImp(uint32_t deviceIndex) override; ze_result_t acknowledgeEventImp(uint32_t seqNo, uint32_t eventType); + MOCKABLE_VIRTUAL ze_result_t interruptContextImp(); + MOCKABLE_VIRTUAL ze_result_t resumeContextImp(uint64_t memoryHandle); + MOCKABLE_VIRTUAL ze_result_t continueExecutionImp(uint64_t memoryHandle); + ze_result_t readGpuMemory(uint64_t memoryHandle, char *output, size_t size, uint64_t gpuVa) override; ze_result_t writeGpuMemory(uint64_t memoryHandle, const char *input, size_t size, uint64_t gpuVa) override; bool isVAElf(const zet_debug_memory_space_desc_t *desc, size_t size); diff --git a/level_zero/tools/test/unit_tests/sources/debug/windows/test_debug_api_windows.cpp b/level_zero/tools/test/unit_tests/sources/debug/windows/test_debug_api_windows.cpp index 47d0419101..c3dc580940 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/windows/test_debug_api_windows.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/windows/test_debug_api_windows.cpp @@ -36,6 +36,7 @@ struct MockDebugSessionWindows : DebugSessionWindows { using DebugSessionWindows::attentionEventContext; using DebugSessionWindows::calculateThreadSlotOffset; using DebugSessionWindows::closeAsyncThread; + using DebugSessionWindows::continueExecutionImp; using DebugSessionWindows::debugArea; using DebugSessionWindows::debugAreaVA; using DebugSessionWindows::debugHandle; @@ -43,6 +44,7 @@ struct MockDebugSessionWindows : DebugSessionWindows { using DebugSessionWindows::eventsToAck; using DebugSessionWindows::getSbaBufferGpuVa; using DebugSessionWindows::initialize; + using DebugSessionWindows::interruptContextImp; using DebugSessionWindows::interruptImp; using DebugSessionWindows::invalidHandle; using DebugSessionWindows::moduleDebugAreaCaptured; @@ -54,6 +56,7 @@ struct MockDebugSessionWindows : DebugSessionWindows { using DebugSessionWindows::readModuleDebugArea; using DebugSessionWindows::readSbaBuffer; using DebugSessionWindows::readStateSaveAreaHeader; + using DebugSessionWindows::resumeContextImp; using DebugSessionWindows::resumeImp; using DebugSessionWindows::runEscape; using DebugSessionWindows::startAsyncThread; @@ -1948,7 +1951,7 @@ TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenReadingStateSaveAreThenMemoryIsNo ASSERT_EQ(1u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_READ_GFX_MEMORY]); } -TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenInterruptImpIsCalledThenErrorIsReturned) { +HWTEST2_F(DebugApiWindowsTest, GivenErrorCasesWhenInterruptImpIsCalledThenErrorIsReturned, IsAtMostXe3Core) { auto session = std::make_unique(zet_debug_config_t{0x1234}, device); ASSERT_NE(nullptr, session); session->wddm = mockWddm; @@ -1968,7 +1971,7 @@ TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenInterruptImpIsCalledThenErrorIsRe EXPECT_EQ(3u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CONTROL_INT_ALL]); } -TEST_F(DebugApiWindowsTest, GivenInterruptImpSucceededThenSuccessIsReturned) { +HWTEST2_F(DebugApiWindowsTest, GivenInterruptImpSucceededThenSuccessIsReturned, IsAtMostXe3Core) { auto session = std::make_unique(zet_debug_config_t{0x1234}, device); ASSERT_NE(nullptr, session); session->wddm = mockWddm; @@ -1978,7 +1981,7 @@ TEST_F(DebugApiWindowsTest, GivenInterruptImpSucceededThenSuccessIsReturned) { EXPECT_EQ(1u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CONTROL_INT_ALL]); } -TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenResumeImpIsCalledThenErrorIsReturned) { +HWTEST2_F(DebugApiWindowsTest, GivenErrorCasesWhenResumeImpIsCalledThenErrorIsReturned, IsAtMostXe3Core) { auto session = std::make_unique(zet_debug_config_t{0x1234}, device); ASSERT_NE(nullptr, session); session->wddm = mockWddm; @@ -1996,7 +1999,7 @@ TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenResumeImpIsCalledThenErrorIsRetur EXPECT_EQ(2u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CONTROL_CLR_ATT_BIT]); } -TEST_F(DebugApiWindowsTest, GivenResumeImpCalledThenBitmaskIsCorrect) { +HWTEST2_F(DebugApiWindowsTest, GivenResumeImpCalledThenBitmaskIsCorrect, IsAtMostXe3Core) { auto session = std::make_unique(zet_debug_config_t{0x1234}, device); ASSERT_NE(nullptr, session); @@ -2015,11 +2018,170 @@ TEST_F(DebugApiWindowsTest, GivenResumeImpCalledThenBitmaskIsCorrect) { auto result = session->resume(thread); EXPECT_EQ(result, ZE_RESULT_SUCCESS); - EXPECT_EQ(1u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CONTROL_CLR_ATT_BIT]); - auto bitmask = mockWddm->euControlBitmask.get(); - EXPECT_EQ(1u, bitmask[0]); - EXPECT_EQ(0u, bitmask[4]); + auto &l0GfxCoreHelper = neoDevice->getRootDeviceEnvironment().getHelper(); + auto expectedCallCount = 0u; + if (!l0GfxCoreHelper.threadResumeRequiresUnlock()) { + expectedCallCount = 1u; + + auto bitmask = mockWddm->euControlBitmask.get(); + EXPECT_EQ(1u, bitmask[0]); + EXPECT_EQ(0u, bitmask[4]); + } + EXPECT_EQ(expectedCallCount, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CONTROL_CLR_ATT_BIT]); +} + +struct MockDebugSessionWindows2 : public MockDebugSessionWindows { + MockDebugSessionWindows2(const zet_debug_config_t &config, L0::Device *device) : MockDebugSessionWindows(config, device) {} + + ze_result_t continueExecutionImp(uint64_t memoryHandle) override { + continueExecutionImpCalled++; + return ZE_RESULT_SUCCESS; + } + + ze_result_t interruptContextImp() override { + interruptContextImpCalled++; + return ZE_RESULT_SUCCESS; + } + + ze_result_t resumeContextImp(uint64_t memoryHandle) override { + resumeContextImpCalled++; + return ZE_RESULT_SUCCESS; + } + + uint32_t continueExecutionImpCalled = 0; + uint32_t interruptContextImpCalled = 0; + uint32_t resumeContextImpCalled = 0; +}; + +TEST_F(DebugApiWindowsTest, GivenInterruptContextImpSucceededThenSuccessIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows::mockDebugHandle; + session->allContexts.insert(0x12345); + + EXPECT_EQ(ZE_RESULT_SUCCESS, session->interruptContextImp()); + EXPECT_EQ(1u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CTRL_INTR_REQUEST]); +} + +TEST_F(DebugApiWindowsTest, GivenThreadResumeRequiresUnlockWhenInterruptCalledThenInterruptContextImpIsCalled) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows2::mockDebugHandle; + + auto expectedCallCount = 0u; + + auto &l0GfxCoreHelper = neoDevice->getRootDeviceEnvironment().getHelper(); + if (l0GfxCoreHelper.threadResumeRequiresUnlock()) { + expectedCallCount = 1u; + } + + EXPECT_EQ(ZE_RESULT_SUCCESS, session->interruptImp(0)); + EXPECT_EQ(expectedCallCount, session->interruptContextImpCalled); +} + +TEST_F(DebugApiWindowsTest, GivenResumeContextImpSucceededThenSuccessIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows::mockDebugHandle; + + EXPECT_EQ(ZE_RESULT_SUCCESS, session->resumeContextImp(0)); + EXPECT_EQ(1u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CTRL_INTR_RESUME]); +} + +TEST_F(DebugApiWindowsTest, GivenThreadResumeRequiresUnlockWhenResumeCalledThenResumeContextImpAndContinueExecutionImpAreCalled) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows2::mockDebugHandle; + + auto expectedCallCount = 0u; + + auto &l0GfxCoreHelper = neoDevice->getRootDeviceEnvironment().getHelper(); + if (l0GfxCoreHelper.threadResumeRequiresUnlock()) { + expectedCallCount = 1u; + } + + std::vector threads{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 1}}; + + EXPECT_EQ(ZE_RESULT_SUCCESS, session->resumeImp(threads, 0)); + EXPECT_EQ(expectedCallCount, session->resumeContextImpCalled); + EXPECT_EQ(expectedCallCount, session->continueExecutionImpCalled); +} + +TEST_F(DebugApiWindowsTest, GivenContinueExecutionImpSucceededThenSuccessIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows::mockDebugHandle; + + EXPECT_EQ(ZE_RESULT_SUCCESS, session->continueExecutionImp(0)); + EXPECT_EQ(1u, mockWddm->dbgUmdEscapeActionCalled[DBGUMD_ACTION_EU_CTRL_CONT_EXECUTION]); +} + +TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenInterruptContextImpIsCalledThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows::invalidHandle; + + session->allContexts = {}; + + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->interruptContextImp()); + + session->allContexts.insert(0x12345); + session->debugHandle = MockDebugSessionWindows::mockDebugHandle; + + session->shouldEscapeReturnStatusNotSuccess = true; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->interruptContextImp()); + + session->shouldEscapeCallFail = true; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->interruptContextImp()); +} + +TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenResumeContextImpIsCalledThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows::invalidHandle; + + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->resumeContextImp(MockDebugSessionWindows::invalidHandle)); + + std::vector threads{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 1}}; + + // create a new thread which initializes memoryHandle to invalidHandle + session->allThreads[threads[0]] = std::make_unique(threads[0]); + session->debugHandle = MockDebugSessionWindows::mockDebugHandle; + auto &l0GfxCoreHelper = neoDevice->getRootDeviceEnvironment().getHelper(); + if (l0GfxCoreHelper.threadResumeRequiresUnlock()) { + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->resumeImp(threads, 0)); + } + + session->shouldEscapeReturnStatusNotSuccess = true; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->resumeContextImp(0)); + + session->shouldEscapeCallFail = true; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->resumeContextImp(0)); +} + +TEST_F(DebugApiWindowsTest, GivenErrorCasesWhenContinueExecutionImpIsCalledThenErrorIsReturned) { + auto session = std::make_unique(zet_debug_config_t{0x1234}, device); + ASSERT_NE(nullptr, session); + session->wddm = mockWddm; + session->debugHandle = MockDebugSessionWindows::invalidHandle; + + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->continueExecutionImp(MockDebugSessionWindows::invalidHandle)); + + session->debugHandle = MockDebugSessionWindows::mockDebugHandle; + + session->shouldEscapeReturnStatusNotSuccess = true; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->continueExecutionImp(0)); + + session->shouldEscapeCallFail = true; + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, session->continueExecutionImp(0)); } TEST_F(DebugApiWindowsTest, givenSyncHostEventReceivedThenEventIsHandledAndAttentionEventContextUpdated) {