diff --git a/level_zero/tools/source/debug/linux/debug_session.cpp b/level_zero/tools/source/debug/linux/debug_session.cpp index d81adaa64a..60de10dcba 100644 --- a/level_zero/tools/source/debug/linux/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/debug_session.cpp @@ -7,14 +7,18 @@ #include "level_zero/tools/source/debug/debug_session.h" +#include "shared/source/helpers/hw_info.h" #include "shared/source/helpers/sleep.h" #include "shared/source/os_interface/linux/drm_allocation.h" #include "shared/source/os_interface/linux/drm_neo.h" #include "shared/source/os_interface/linux/sys_calls.h" #include "level_zero/core/source/device/device.h" +#include "level_zero/core/source/gfx_core_helpers/l0_gfx_core_helper.h" +#include "level_zero/include/zet_intel_gpu_debug.h" #include "level_zero/tools/source/debug/linux/debug_session.h" #include "level_zero/tools/source/debug/linux/debug_session_factory.h" +#include "level_zero/tools/source/debug/linux/drm_helper.h" namespace L0 { DebugSessionLinuxAllocatorFn debugSessionLinuxFactory[DEBUG_SESSION_LINUX_TYPE_MAX] = {}; @@ -121,4 +125,100 @@ void DebugSessionLinux::closeAsyncThread() { internalEventThread.close(); } +bool DebugSessionLinux::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 DebugSessionLinux::checkStoppedThreadsAndGenerateEvents(const std::vector &threads, uint64_t memoryHandle, uint32_t deviceIndex) { + + std::vector threadsWithAttention; + std::vector stoppedThreadsToReport; + NEO::sleep(std::chrono::microseconds(1)); + + if (threads.size() > 1) { + auto hwInfo = connectedDevice->getHwInfo(); + auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper(); + + std::unique_ptr bitmask; + size_t bitmaskSize; + [[maybe_unused]] auto attReadResult = threadControl(threads, deviceIndex, ThreadControlCmd::stopped, bitmask, bitmaskSize); + + // error querying STOPPED threads - no threads available ( for example: threads have completed ) + if (attReadResult != 0) { + PRINT_DEBUGGER_ERROR_LOG("checkStoppedThreadsAndGenerateEvents ATTENTION read failed: %d errno = %d \n", (int)attReadResult, DrmHelper::getErrno(connectedDevice)); + return; + } + + threadsWithAttention = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, deviceIndex, bitmask.get(), bitmaskSize); + + if (threadsWithAttention.size() == 0) { + return; + } + } + + 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 (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; + } + } + + generateEventsForStoppedThreads(stoppedThreadsToReport); +} + +ze_result_t DebugSessionLinux::resumeImp(const std::vector &threads, uint32_t deviceIndex) { + std::unique_ptr bitmask; + size_t bitmaskSize; + + auto result = threadControl(threads, deviceIndex, ThreadControlCmd::resume, bitmask, bitmaskSize); + + return result == 0 ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_NOT_AVAILABLE; +} + +ze_result_t DebugSessionLinux::interruptImp(uint32_t deviceIndex) { + std::unique_ptr bitmask; + size_t bitmaskSize; + + auto result = threadControl({}, deviceIndex, ThreadControlCmd::interruptAll, bitmask, bitmaskSize); + + return result == 0 ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_NOT_AVAILABLE; +} + } // namespace L0 diff --git a/level_zero/tools/source/debug/linux/debug_session.h b/level_zero/tools/source/debug/linux/debug_session.h index 183a1155e5..51ea6e22d8 100644 --- a/level_zero/tools/source/debug/linux/debug_session.h +++ b/level_zero/tools/source/debug/linux/debug_session.h @@ -74,5 +74,18 @@ struct DebugSessionLinux : DebugSessionImp { return NEO::SysCalls::munmap(addr, size); } }; + + enum class ThreadControlCmd { + interrupt, + resume, + stopped, + interruptAll + }; + + virtual int threadControl(const std::vector &threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize) = 0; + 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); + ze_result_t resumeImp(const std::vector &threads, uint32_t deviceIndex) override; + ze_result_t interruptImp(uint32_t deviceIndex) override; }; } // namespace L0 \ No newline at end of file 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 686537f7e6..5c55a80e72 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp @@ -1506,102 +1506,6 @@ 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; - std::vector stoppedThreadsToReport; - NEO::sleep(std::chrono::microseconds(1)); - - if (threads.size() > 1) { - auto hwInfo = connectedDevice->getHwInfo(); - auto &l0GfxCoreHelper = connectedDevice->getL0GfxCoreHelper(); - - std::unique_ptr bitmask; - size_t bitmaskSize; - [[maybe_unused]] auto attReadResult = threadControl(threads, deviceIndex, ThreadControlCmd::stopped, bitmask, bitmaskSize); - - // error querying STOPPED threads - no threads available ( for example: threads have completed ) - if (attReadResult != 0) { - PRINT_DEBUGGER_ERROR_LOG("checkStoppedThreadsAndGenerateEvents ATTENTION read failed: %d errno = %d \n", (int)attReadResult, DrmHelper::getErrno(connectedDevice)); - return; - } - - threadsWithAttention = l0GfxCoreHelper.getThreadsFromAttentionBitmask(hwInfo, deviceIndex, bitmask.get(), bitmaskSize); - - if (threadsWithAttention.size() == 0) { - return; - } - } - - 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 (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; - } - } - - generateEventsForStoppedThreads(stoppedThreadsToReport); -} - -ze_result_t DebugSessionLinuxi915::resumeImp(const std::vector &threads, uint32_t deviceIndex) { - std::unique_ptr bitmask; - size_t bitmaskSize; - - auto result = threadControl(threads, deviceIndex, ThreadControlCmd::resume, bitmask, bitmaskSize); - - return result == 0 ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_NOT_AVAILABLE; -} - -ze_result_t DebugSessionLinuxi915::interruptImp(uint32_t deviceIndex) { - std::unique_ptr bitmask; - size_t bitmaskSize; - - auto result = threadControl({}, deviceIndex, ThreadControlCmd::interruptAll, bitmask, bitmaskSize); - - return result == 0 ? ZE_RESULT_SUCCESS : ZE_RESULT_ERROR_NOT_AVAILABLE; -} - ze_result_t DebugSessionLinuxi915::getISAVMHandle(uint32_t deviceIndex, const zet_debug_memory_space_desc_t *desc, size_t size, uint64_t &vmHandle) { auto gmmHelper = connectedDevice->getNEODevice()->getGmmHelper(); auto accessVA = gmmHelper->decanonize(desc->address); 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 6d818cacce..9d5c552b57 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.h +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.h @@ -153,21 +153,10 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { }; protected: - enum class ThreadControlCmd { - interrupt, - resume, - stopped, - interruptAll - }; - MOCKABLE_VIRTUAL void handleEvent(prelim_drm_i915_debug_event *event); bool checkAllEventsCollected(); std::unordered_map> clientHandleToConnection; ze_result_t readEventImp(prelim_drm_i915_debug_event *drmDebugEvent); - 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); @@ -253,8 +242,7 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { ze_result_t writeDefaultMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, const void *buffer); - MOCKABLE_VIRTUAL int threadControl(const std::vector &threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize); - + int threadControl(const std::vector &threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize) override; uint64_t getContextStateSaveAreaGpuVa(uint64_t memoryHandle) override; size_t getContextStateSaveAreaSize(uint64_t memoryHandle) override; virtual uint64_t getSbaBufferGpuVa(uint64_t memoryHandle); diff --git a/level_zero/tools/source/debug/linux/xe/debug_session.h b/level_zero/tools/source/debug/linux/xe/debug_session.h index 75b44fa7c2..3d935d399e 100644 --- a/level_zero/tools/source/debug/linux/xe/debug_session.h +++ b/level_zero/tools/source/debug/linux/xe/debug_session.h @@ -87,6 +87,9 @@ struct DebugSessionLinuxXe : DebugSessionLinux { uint32_t xeDebuggerVersion = 0; protected: + int threadControl(const std::vector &threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize) override { + return -1; + } void startAsyncThread() override; static void *asyncThreadFunction(void *arg); void handleEventsAsync(); diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h index 48d994dd3f..1362b91ca1 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h @@ -126,6 +126,11 @@ struct MockDebugSessionLinuxXe : public L0::DebugSessionLinuxXe { return DebugSessionLinuxXe::initialize(); } + int threadControl(const std::vector &threads, uint32_t tile, ThreadControlCmd threadCmd, std::unique_ptr &bitmask, size_t &bitmaskSize) override { + numThreadsPassedToThreadControl = threads.size(); + return L0::DebugSessionLinuxXe::threadControl(threads, tile, threadCmd, bitmask, bitmaskSize); + } + std::unique_ptr getInternalEvent() override { getInternalEventCounter++; if (synchronousInternalEventRead) { @@ -134,6 +139,7 @@ struct MockDebugSessionLinuxXe : public L0::DebugSessionLinuxXe { return DebugSessionLinuxXe::getInternalEvent(); } + size_t numThreadsPassedToThreadControl = 0; bool synchronousInternalEventRead = false; std::atomic getInternalEventCounter = 0; ze_result_t initializeRetVal = ZE_RESULT_FORCE_UINT32; diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/test_debug_api_linux_xe.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/test_debug_api_linux_xe.cpp index 5a3582e744..e2f909b0f1 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/test_debug_api_linux_xe.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/test_debug_api_linux_xe.cpp @@ -137,6 +137,19 @@ TEST(IoctlHandler, GivenHandlerWhenIoctlFailsWithEBUSYThenIoctlIsAgain) { using DebugApiLinuxTestXe = Test; +TEST_F(DebugApiLinuxTestXe, WhenCallingThreadControlForInterruptThenErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + + EXPECT_NE(nullptr, session); + std::vector threads({}); + std::unique_ptr bitmaskOut; + size_t bitmaskSizeOut = 0; + EXPECT_EQ(-1, session->threadControl(threads, 0, MockDebugSessionLinuxXe::ThreadControlCmd::interrupt, bitmaskOut, bitmaskSizeOut)); +} + TEST_F(DebugApiLinuxTestXe, GivenDebugSessionWhenCallingPollThenDefaultHandlerRedirectsToSysCall) { zet_debug_config_t config = {}; config.pid = 0x1234;