From 802848a43f79140cda90d5c1a0f7ce304477684d Mon Sep 17 00:00:00 2001 From: Mateusz Hoppe Date: Thu, 2 Mar 2023 14:11:02 +0000 Subject: [PATCH] fix: L0Debug - allow access only for reported stopped threads - read/write registers/memory only allowed for threads reported as stopped by events - threads newly stopped, accidentally, that are resumed immediately are not allowed register/memory access Related-To: NEO-7776 Signed-off-by: Mateusz Hoppe --- .../tools/source/debug/debug_session_imp.cpp | 8 ++- level_zero/tools/source/debug/eu_thread.h | 11 +++- .../sources/debug/debug_session_tests.cpp | 55 +++++++++++++++++++ .../debug/debug_session_thread_tests.cpp | 3 + .../sources/debug/eu_thread_tests.cpp | 16 +++++- .../linux/debug_session_fixtures_linux.h | 2 + .../debug/linux/test_debug_api_linux.cpp | 3 + .../debug/windows/test_debug_api_windows.cpp | 2 + 8 files changed, 95 insertions(+), 5 deletions(-) diff --git a/level_zero/tools/source/debug/debug_session_imp.cpp b/level_zero/tools/source/debug/debug_session_imp.cpp index d41c8fce28..890b1403e3 100644 --- a/level_zero/tools/source/debug/debug_session_imp.cpp +++ b/level_zero/tools/source/debug/debug_session_imp.cpp @@ -211,7 +211,7 @@ bool DebugSession::areRequestedThreadsStopped(ze_device_thread_t thread) { for (auto &threadId : singleThreads) { - if (allThreads[threadId]->isStopped()) { + if (allThreads[threadId]->isReportedAsStopped()) { continue; } return false; @@ -708,6 +708,7 @@ void DebugSessionImp::markPendingInterruptsOrAddToNewlyStoppedFromRaisedAttentio request.second = true; } threadWasInterrupted = true; + allThreads[threadId]->reportAsStopped(); } } @@ -832,6 +833,7 @@ void DebugSessionImp::generateEventsForStoppedThreads(const std::vectorreportAsStopped(); debugEvent.type = ZET_DEBUG_EVENT_TYPE_THREAD_STOPPED; debugEvent.info.thread.thread = thread; @@ -1181,7 +1183,7 @@ ze_result_t DebugSessionImp::readRegisters(ze_device_thread_t thread, uint32_t t } auto threadId = convertToThreadId(thread); - if (!allThreads[threadId]->isStopped()) { + if (!allThreads[threadId]->isReportedAsStopped()) { return ZE_RESULT_ERROR_NOT_AVAILABLE; } @@ -1212,7 +1214,7 @@ ze_result_t DebugSessionImp::writeRegisters(ze_device_thread_t thread, uint32_t } auto threadId = convertToThreadId(thread); - if (!allThreads[threadId]->isStopped()) { + if (!allThreads[threadId]->isReportedAsStopped()) { return ZE_RESULT_ERROR_NOT_AVAILABLE; } diff --git a/level_zero/tools/source/debug/eu_thread.h b/level_zero/tools/source/debug/eu_thread.h index 3a5f14adc0..fb2218aa5f 100644 --- a/level_zero/tools/source/debug/eu_thread.h +++ b/level_zero/tools/source/debug/eu_thread.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021-2022 Intel Corporation + * Copyright (C) 2021-2023 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -122,6 +122,7 @@ class EuThread { return false; } PRINT_DEBUGGER_THREAD_LOG("Resumed thread: %s", toString().c_str()); + reportedAsStopped = false; state = State::Running; memoryHandle = invalidHandle; return true; @@ -155,6 +156,13 @@ class EuThread { uint8_t getLastCounter() const { return systemRoutineCounter; } + void reportAsStopped() { + reportedAsStopped = true; + } + bool isReportedAsStopped() { + DEBUG_BREAK_IF(reportedAsStopped && state != State::Stopped); + return reportedAsStopped; + } public: static constexpr uint64_t invalidHandle = std::numeric_limits::max(); @@ -164,6 +172,7 @@ class EuThread { std::atomic state = State::Unavailable; uint8_t systemRoutineCounter = 0; std::atomic memoryHandle = invalidHandle; + std::atomic reportedAsStopped = false; }; static_assert(sizeof(EuThread::ThreadId) == sizeof(uint64_t)); 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 695454669f..47b0bd7f91 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 @@ -109,6 +109,7 @@ TEST(DebugSessionTest, givenAllStoppedThreadsWhenInterruptCalledThenErrorNotAvai for (uint32_t i = 0; i < hwInfo.gtSystemInfo.ThreadCount / hwInfo.gtSystemInfo.EUCount; i++) { EuThread::ThreadId thread(0, 0, 0, 0, i); sessionMock->allThreads[thread]->stopThread(1u); + sessionMock->allThreads[thread]->reportAsStopped(); } ze_device_thread_t apiThread = {0, 0, 0, UINT32_MAX}; @@ -286,6 +287,55 @@ TEST(DebugSessionTest, givenStoppedThreadWhenAddingNewlyStoppedThenThreadIsNotAd EXPECT_EQ(0u, sessionMock->newlyStoppedThreads.size()); } +TEST(DebugSessionTest, givenNoPendingInterruptAndStoppedThreadWithForceExceptionOnlyWhenAddingNewlyStoppedThenThreadIsNotReportedAsStopped) { + 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, 0); + sessionMock->skipReadSystemRoutineIdent = 1; + sessionMock->threadStopped = true; + sessionMock->onlyForceException = true; + + sessionMock->markPendingInterruptsOrAddToNewlyStoppedFromRaisedAttention(thread, 1u); + + EXPECT_EQ(1u, sessionMock->newlyStoppedThreads.size()); + EXPECT_FALSE(sessionMock->allThreads[thread]->isReportedAsStopped()); +} + +TEST(DebugSessionTest, givenNoPendingInterruptAndStoppedThreadWhenGeneratingEventsFromStoppedThreadsThenThreadIsReportedAsStopped) { + 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, 0); + sessionMock->skipReadSystemRoutineIdent = 1; + sessionMock->threadStopped = true; + sessionMock->onlyForceException = false; + sessionMock->triggerEvents = true; + + sessionMock->markPendingInterruptsOrAddToNewlyStoppedFromRaisedAttention(thread, 1u); + + EXPECT_EQ(1u, sessionMock->newlyStoppedThreads.size()); + EXPECT_FALSE(sessionMock->allThreads[thread]->isReportedAsStopped()); + + sessionMock->generateEventsAndResumeStoppedThreads(); + EXPECT_TRUE(sessionMock->allThreads[thread]->isReportedAsStopped()); + + EXPECT_EQ(1u, sessionMock->events.size()); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_THREAD_STOPPED, sessionMock->events[0].type); +} + TEST(DebugSessionTest, givenNoStoppedThreadWhenAddingNewlyStoppedThenThreadIsNotAdded) { zet_debug_config_t config = {}; config.pid = 0x1234; @@ -1626,6 +1676,8 @@ TEST_F(MultiTileDebugSessionTest, GivenMultitileDeviceWhenCallingAreRequestedThr sessionMock->allThreads[EuThread::ThreadId(0, thread)]->stopThread(1u); sessionMock->allThreads[EuThread::ThreadId(1, thread)]->stopThread(1u); + sessionMock->allThreads[EuThread::ThreadId(0, thread)]->reportAsStopped(); + sessionMock->allThreads[EuThread::ThreadId(1, thread)]->reportAsStopped(); auto stopped = sessionMock->areRequestedThreadsStopped(thread); EXPECT_TRUE(stopped); @@ -1636,6 +1688,7 @@ TEST_F(MultiTileDebugSessionTest, GivenMultitileDeviceWhenCallingAreRequestedThr for (uint32_t i = 0; i < sliceCount; i++) { EuThread::ThreadId threadId(0, i, 0, 0, 0); sessionMock->allThreads[threadId]->stopThread(1u); + sessionMock->allThreads[threadId]->reportAsStopped(); } stopped = sessionMock->areRequestedThreadsStopped(allSlices); @@ -1644,6 +1697,7 @@ TEST_F(MultiTileDebugSessionTest, GivenMultitileDeviceWhenCallingAreRequestedThr for (uint32_t i = 0; i < sliceCount; i++) { EuThread::ThreadId threadId(1, i, 0, 0, 0); sessionMock->allThreads[threadId]->stopThread(1u); + sessionMock->allThreads[threadId]->reportAsStopped(); } stopped = sessionMock->areRequestedThreadsStopped(allSlices); @@ -1662,6 +1716,7 @@ struct DebugSessionRegistersAccess { session = std::make_unique(config, deviceImp.get()); session->allThreads[stoppedThreadId]->stopThread(1u); + session->allThreads[stoppedThreadId]->reportAsStopped(); } void tearDown() { diff --git a/level_zero/tools/test/unit_tests/sources/debug/debug_session_thread_tests.cpp b/level_zero/tools/test/unit_tests/sources/debug/debug_session_thread_tests.cpp index 6e07a68010..af10862720 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/debug_session_thread_tests.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/debug_session_thread_tests.cpp @@ -366,6 +366,7 @@ TEST(DebugSession, givenAllStoppedThreadsWhenAreRequestedThreadsStoppedCalledThe for (uint32_t i = 0; i < hwInfo.gtSystemInfo.ThreadCount / hwInfo.gtSystemInfo.EUCount; i++) { EuThread::ThreadId thread(0, 0, 0, 0, i); sessionMock->allThreads[thread]->stopThread(1u); + sessionMock->allThreads[thread]->reportAsStopped(); } ze_device_thread_t apiThread = {0, 0, 0, UINT32_MAX}; @@ -386,6 +387,7 @@ TEST(DebugSession, givenSomeStoppedThreadsWhenAreRequestedThreadsStoppedCalledTh EuThread::ThreadId thread(0, 0, 0, 0, i); if (i % 2) { sessionMock->allThreads[thread]->stopThread(1u); + sessionMock->allThreads[thread]->reportAsStopped(); } } @@ -443,6 +445,7 @@ TEST(DebugSession, givenDifferentCombinationsOfThreadsAndMemoryTypeCheckExpected for (uint32_t i = 0; i < hwInfo.gtSystemInfo.ThreadCount / hwInfo.gtSystemInfo.EUCount; i++) { EuThread::ThreadId thread(0, 0, 0, 0, i); sessionMock->allThreads[thread]->stopThread(1u); + sessionMock->allThreads[thread]->reportAsStopped(); } retVal = sessionMock->sanityMemAccessThreadCheck(thread, &desc); diff --git a/level_zero/tools/test/unit_tests/sources/debug/eu_thread_tests.cpp b/level_zero/tools/test/unit_tests/sources/debug/eu_thread_tests.cpp index e8fc3056e2..f417b244e2 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/eu_thread_tests.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/eu_thread_tests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021-2022 Intel Corporation + * Copyright (C) 2021-2023 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -101,6 +101,20 @@ TEST(EuThread, GivenThreadStateRunningWhenVerifyingStopWithOddCounterThenTrueRet EXPECT_TRUE(euThread.verifyStopped(1)); EXPECT_TRUE(euThread.isStopped()); + EXPECT_FALSE(euThread.isReportedAsStopped()); +} + +TEST(EuThread, GivenThreadStateStoppedAndReportedAsStoppedWhenResumedThenIsReportedAsStoppedIsFalse) { + ze_device_thread_t devThread = {3, 4, 5, 6}; + EuThread::ThreadId threadId(0, devThread); + EuThread euThread(threadId); + + EXPECT_TRUE(euThread.verifyStopped(1)); + euThread.reportAsStopped(); + EXPECT_TRUE(euThread.isReportedAsStopped()); + + EXPECT_TRUE(euThread.resumeThread()); + EXPECT_FALSE(euThread.isReportedAsStopped()); } TEST(EuThread, GivenThreadStateStoppedWhenVerifyingStopWithOddCounterThenTrueReturnedAndStateStopped) { diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/debug_session_fixtures_linux.h b/level_zero/tools/test/unit_tests/sources/debug/linux/debug_session_fixtures_linux.h index 5356002d57..c9bb6cfd2f 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/debug_session_fixtures_linux.h +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/debug_session_fixtures_linux.h @@ -389,6 +389,7 @@ struct MockDebugSessionLinux : public L0::DebugSessionLinux { allThreads[threadId] = std::make_unique(threadId); } allThreads[threadId]->stopThread(vmHandle); + allThreads[threadId]->reportAsStopped(); } bool readSystemRoutineIdent(EuThread *thread, uint64_t vmHandle, SIP::sr_ident &srIdent) override { @@ -536,6 +537,7 @@ struct MockTileDebugSessionLinux : TileDebugSessionLinux { allThreads[threadId] = std::make_unique(threadId); } allThreads[threadId]->stopThread(vmHandle); + allThreads[threadId]->reportAsStopped(); } bool writeResumeCommand(const std::vector &threadIds) override { diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp index adc9e9e3fd..e48d8861e5 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_api_linux.cpp @@ -285,11 +285,13 @@ TEST(DebugSessionLinuxTest, GivenDeviceWithSingleSliceWhenCallingAreRequestedThr EuThread::ThreadId threadId(0, 0, 0, 0, 0); sessionMock->allThreads[threadId]->stopThread(1u); + sessionMock->allThreads[threadId]->reportAsStopped(); uint32_t subDeviceCount = std::max(1u, neoDevice->getNumSubDevices()); for (uint32_t i = 0; i < subDeviceCount; i++) { EuThread::ThreadId threadId(i, 0, 0, 0, 0); sessionMock->allThreads[threadId]->stopThread(1u); + sessionMock->allThreads[threadId]->reportAsStopped(); } stopped = sessionMock->areRequestedThreadsStopped(thread); EXPECT_TRUE(stopped); @@ -6541,6 +6543,7 @@ struct DebugApiRegistersAccessFixture : public DebugApiLinuxFixture { session->clientHandleToConnection[MockDebugSessionLinux::mockClientHandle]->vmToContextStateSaveAreaBindInfo[vmHandle] = {stateSaveAreaGpuVa, maxDbgSurfaceSize}; session->allThreadsStopped = true; session->allThreads[stoppedThreadId]->stopThread(1u); + session->allThreads[stoppedThreadId]->reportAsStopped(); } void tearDown() { 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 a001852ba4..905b6d9685 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 @@ -104,6 +104,7 @@ struct MockDebugSessionWindows : DebugSessionWindows { allThreads[threadId] = std::make_unique(threadId); } allThreads[threadId]->stopThread(context); + allThreads[threadId]->reportAsStopped(); } bool readSystemRoutineIdent(EuThread *thread, uint64_t vmHandle, SIP::sr_ident &srIdent) override { @@ -1655,6 +1656,7 @@ TEST_F(DebugApiWindowsTest, GivenSipNotUpdatingSipCmdThenAccessToSlmFailsGracefu ze_device_thread_t thread = {0, 0, 0, 0}; session->allThreads[EuThread::ThreadId(0, thread)]->stopThread(1u); + session->allThreads[EuThread::ThreadId(0, thread)]->reportAsStopped(); session->sipSupportsSlm = true;