diff --git a/level_zero/tools/source/debug/CMakeLists.txt b/level_zero/tools/source/debug/CMakeLists.txt index 92f615408c..96bb531ad9 100644 --- a/level_zero/tools/source/debug/CMakeLists.txt +++ b/level_zero/tools/source/debug/CMakeLists.txt @@ -5,8 +5,10 @@ # set(L0_SRCS_TOOLS_DEBUG + ${CMAKE_CURRENT_SOURCE_DIR}/debug_session.cpp ${CMAKE_CURRENT_SOURCE_DIR}/debug_session.h ${CMAKE_CURRENT_SOURCE_DIR}/debug_handlers.h + ${CMAKE_CURRENT_SOURCE_DIR}/eu_thread.h ${CMAKE_CURRENT_SOURCE_DIR}${BRANCH_DIR_SUFFIX}/debug_handlers.cpp ) diff --git a/level_zero/tools/source/debug/debug_session.cpp b/level_zero/tools/source/debug/debug_session.cpp new file mode 100644 index 0000000000..9cc9acb987 --- /dev/null +++ b/level_zero/tools/source/debug/debug_session.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2021 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/source/debug/debug_session.h" + +#include "shared/source/helpers/hw_info.h" + +#include "level_zero/core/source/device/device_imp.h" + +namespace L0 { + +RootDebugSession::RootDebugSession(const zet_debug_config_t &config, Device *device) : DebugSession(config, device) { + + if (connectedDevice) { + auto hwInfo = connectedDevice->getHwInfo(); + const uint32_t numSubslicesPerSlice = hwInfo.gtSystemInfo.MaxSubSlicesSupported / hwInfo.gtSystemInfo.MaxSlicesSupported; + const uint32_t numEuPerSubslice = hwInfo.gtSystemInfo.MaxEuPerSubSlice; + const uint32_t numThreadsPerEu = (hwInfo.gtSystemInfo.ThreadCount / hwInfo.gtSystemInfo.EUCount); + uint32_t subDeviceCount = 1; + + for (uint32_t tileIndex = 0; tileIndex < subDeviceCount; tileIndex++) { + for (uint32_t sliceID = 0; sliceID < hwInfo.gtSystemInfo.MaxSlicesSupported; sliceID++) { + for (uint32_t subsliceID = 0; subsliceID < numSubslicesPerSlice; subsliceID++) { + for (uint32_t euID = 0; euID < numEuPerSubslice; euID++) { + + for (uint32_t threadID = 0; threadID < numThreadsPerEu; threadID++) { + + EuThread::ThreadId thread = {tileIndex, sliceID, subsliceID, euID, threadID}; + + allThreads[uint64_t(thread)] = std::make_unique(thread); + } + } + } + } + } + } +} + +std::vector RootDebugSession::getSingleThreads(ze_device_thread_t physicalThread, const NEO::HardwareInfo &hwInfo) { + const uint32_t numSubslicesPerSlice = hwInfo.gtSystemInfo.MaxSubSlicesSupported / hwInfo.gtSystemInfo.MaxSlicesSupported; + const uint32_t numEuPerSubslice = hwInfo.gtSystemInfo.MaxEuPerSubSlice; + const uint32_t numThreadsPerEu = (hwInfo.gtSystemInfo.ThreadCount / hwInfo.gtSystemInfo.EUCount); + + UNRECOVERABLE_IF(numThreadsPerEu > 8); + + std::vector threads; + + const uint32_t slice = physicalThread.slice; + const uint32_t subslice = physicalThread.subslice; + const uint32_t eu = physicalThread.eu; + const uint32_t thread = physicalThread.thread; + + for (uint32_t sliceID = 0; sliceID < hwInfo.gtSystemInfo.MaxSlicesSupported; sliceID++) { + if (slice != UINT32_MAX) { + sliceID = slice; + } + + for (uint32_t subsliceID = 0; subsliceID < numSubslicesPerSlice; subsliceID++) { + if (subslice != UINT32_MAX) { + subsliceID = subslice; + } + + for (uint32_t euID = 0; euID < numEuPerSubslice; euID++) { + if (eu != UINT32_MAX) { + euID = eu; + } + + for (uint32_t threadID = 0; threadID < numThreadsPerEu; threadID++) { + if (thread != UINT32_MAX) { + threadID = thread; + } + threads.push_back({sliceID, subsliceID, euID, threadID}); + + if (thread != UINT32_MAX) { + break; + } + } + + if (eu != UINT32_MAX) { + break; + } + } + if (subslice != UINT32_MAX) { + break; + } + } + if (slice != UINT32_MAX) { + break; + } + } + + return threads; +} + +} // namespace L0 diff --git a/level_zero/tools/source/debug/debug_session.h b/level_zero/tools/source/debug/debug_session.h index ea0e162b34..ee1c999256 100644 --- a/level_zero/tools/source/debug/debug_session.h +++ b/level_zero/tools/source/debug/debug_session.h @@ -7,8 +7,12 @@ #pragma once #include "level_zero/core/source/debugger/debugger_l0.h" +#include "level_zero/tools/source/debug/eu_thread.h" +#include #include +#include + struct _zet_debug_session_handle_t {}; namespace L0 { @@ -51,10 +55,14 @@ struct RootDebugSession : DebugSession { RootDebugSession() = delete; protected: - RootDebugSession(const zet_debug_config_t &config, Device *device) : DebugSession(config, device){}; + RootDebugSession(const zet_debug_config_t &config, Device *device); virtual bool readModuleDebugArea() = 0; + std::vector getSingleThreads(ze_device_thread_t physicalThread, const NEO::HardwareInfo &hwInfo); + DebugAreaHeader debugArea; + + std::map> allThreads; }; } // namespace L0 diff --git a/level_zero/tools/source/debug/eu_thread.h b/level_zero/tools/source/debug/eu_thread.h new file mode 100644 index 0000000000..edce2d77ec --- /dev/null +++ b/level_zero/tools/source/debug/eu_thread.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once + +#include "shared/source/debug_settings/debug_settings_manager.h" + +#include + +#include +#include +#include +namespace L0 { + +class EuThread { + public: + enum class State { + Running, + Stopped, + Unavailable + }; + + struct ThreadId { + union { + struct { + uint64_t thread : 4; + uint64_t eu : 5; + uint64_t subslice : 10; + uint64_t slice : 10; + uint64_t tileIndex : 2; + uint64_t reserved : 33; + }; + uint64_t packed; + }; + + ThreadId(uint32_t tile, uint32_t slice, uint32_t subslice, uint32_t eu, uint32_t thread) { + this->packed = 0; + this->tileIndex = tile; + this->slice = slice; + this->subslice = subslice; + this->eu = eu; + this->thread = thread; + } + + ThreadId(uint32_t tile, ze_device_thread_t thread) { + this->packed = 0; + this->tileIndex = tile; + this->slice = thread.slice; + this->subslice = thread.subslice; + this->eu = thread.eu; + this->thread = thread.thread; + } + + operator uint64_t() const { + return packed; + } + }; + + virtual ~EuThread() = default; + + EuThread(ThreadId threadId) : threadId(threadId) {} + + bool stopThread() { + if (state == State::Stopped) { + return false; + } + state = State::Stopped; + return true; + } + + bool resumeThread() { + if (state != State::Stopped) { + return false; + } + state = State::Running; + return true; + } + + bool isStopped() { + return state == State::Stopped; + } + + bool isRunning() { + return state != State::Stopped; + } + + std::string toString() { + std::stringstream threadString; + threadString << "device index = " << threadId.tileIndex << " slice = " << threadId.slice << " subslice = " << threadId.subslice << " eu = " << threadId.eu + << " thread = " << threadId.thread; + return threadString.str(); + } + + ThreadId getThreadId() { + return threadId; + } + + protected: + ThreadId threadId; + State state = State::Unavailable; +}; + +static_assert(sizeof(EuThread::ThreadId) == sizeof(uint64_t)); +} // namespace L0 diff --git a/level_zero/tools/test/unit_tests/sources/debug/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/debug/CMakeLists.txt index e5a7d173e4..fda0d9ae75 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/CMakeLists.txt +++ b/level_zero/tools/test/unit_tests/sources/debug/CMakeLists.txt @@ -6,6 +6,8 @@ target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/debug_session_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/eu_thread_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mock_debug_session.h ${CMAKE_CURRENT_SOURCE_DIR}${BRANCH_DIR_SUFFIX}/test_debug_api.cpp ) 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 new file mode 100644 index 0000000000..244b01b1f0 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/debug_session_tests.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/test/common/helpers/default_hw_info.h" +#include "shared/test/common/mocks/mock_device.h" + +#include "test.h" + +#include "level_zero/core/source/device/device_imp.h" +#include "level_zero/core/test/unit_tests/mock.h" +#include "level_zero/core/test/unit_tests/mocks/mock_device.h" +#include "level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h" + +namespace L0 { +namespace ult { + +TEST(RootDebugSession, givenSingleThreadWhenGettingSingleThreadsThenCorrectThreadIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto hwInfo = *NEO::defaultHwInfo.get(); + NEO::Device *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto debugSession = std::make_unique(config, &deviceImp); + + auto subslice = hwInfo.gtSystemInfo.MaxSubSlicesSupported / hwInfo.gtSystemInfo.MaxSlicesSupported - 1; + ze_device_thread_t physicalThread = {0, subslice, 2, 3}; + + auto threads = debugSession->getSingleThreads(physicalThread, hwInfo); + + EXPECT_EQ(1u, threads.size()); + EXPECT_EQ(0u, threads[0].slice); + EXPECT_EQ(subslice, threads[0].subslice); + EXPECT_EQ(2u, threads[0].eu); + EXPECT_EQ(3u, threads[0].thread); +} + +TEST(RootDebugSession, givenAllThreadsWhenGettingSingleThreadsThenCorrectThreadsAreReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto hwInfo = *NEO::defaultHwInfo.get(); + NEO::Device *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto debugSession = std::make_unique(config, &deviceImp); + + auto subslice = hwInfo.gtSystemInfo.MaxSubSlicesSupported / hwInfo.gtSystemInfo.MaxSlicesSupported - 1; + ze_device_thread_t physicalThread = {0, subslice, 2, UINT32_MAX}; + const uint32_t numThreadsPerEu = (hwInfo.gtSystemInfo.ThreadCount / hwInfo.gtSystemInfo.EUCount); + + auto threads = debugSession->getSingleThreads(physicalThread, hwInfo); + + EXPECT_EQ(numThreadsPerEu, threads.size()); + + for (uint32_t i = 0; i < numThreadsPerEu; i++) { + EXPECT_EQ(0u, threads[i].slice); + EXPECT_EQ(subslice, threads[i].subslice); + EXPECT_EQ(2u, threads[i].eu); + EXPECT_EQ(i, threads[i].thread); + } +} + +TEST(RootDebugSession, givenAllEUsWhenGettingSingleThreadsThenCorrectThreadsAreReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto hwInfo = *NEO::defaultHwInfo.get(); + NEO::Device *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto debugSession = std::make_unique(config, &deviceImp); + + auto subslice = hwInfo.gtSystemInfo.MaxSubSlicesSupported / hwInfo.gtSystemInfo.MaxSlicesSupported - 1; + ze_device_thread_t physicalThread = {0, subslice, UINT32_MAX, 0}; + const uint32_t numEuPerSubslice = hwInfo.gtSystemInfo.MaxEuPerSubSlice; + + auto threads = debugSession->getSingleThreads(physicalThread, hwInfo); + + EXPECT_EQ(numEuPerSubslice, threads.size()); + + for (uint32_t i = 0; i < numEuPerSubslice; i++) { + EXPECT_EQ(0u, threads[i].slice); + EXPECT_EQ(subslice, threads[i].subslice); + EXPECT_EQ(i, threads[i].eu); + EXPECT_EQ(0u, threads[i].thread); + } +} + +TEST(RootDebugSession, givenAllSubslicesWhenGettingSingleThreadsThenCorrectThreadsAreReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto hwInfo = *NEO::defaultHwInfo.get(); + NEO::Device *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto debugSession = std::make_unique(config, &deviceImp); + + ze_device_thread_t physicalThread = {0, UINT32_MAX, 0, 0}; + const uint32_t numSubslicesPerSlice = hwInfo.gtSystemInfo.MaxSubSlicesSupported / hwInfo.gtSystemInfo.MaxSlicesSupported; + + auto threads = debugSession->getSingleThreads(physicalThread, hwInfo); + + EXPECT_EQ(numSubslicesPerSlice, threads.size()); + + for (uint32_t i = 0; i < numSubslicesPerSlice; i++) { + EXPECT_EQ(0u, threads[i].slice); + EXPECT_EQ(i, threads[i].subslice); + EXPECT_EQ(0u, threads[i].eu); + EXPECT_EQ(0u, threads[i].thread); + } +} + +TEST(RootDebugSession, givenAllSlicesWhenGettingSingleThreadsThenCorrectThreadsAreReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto hwInfo = *NEO::defaultHwInfo.get(); + NEO::Device *neoDevice(NEO::MockDevice::createWithNewExecutionEnvironment(&hwInfo, 0)); + Mock deviceImp(neoDevice, neoDevice->getExecutionEnvironment()); + auto debugSession = std::make_unique(config, &deviceImp); + + ze_device_thread_t physicalThread = {UINT32_MAX, 0, 0, 0}; + const uint32_t numSlices = hwInfo.gtSystemInfo.MaxSlicesSupported; + + auto threads = debugSession->getSingleThreads(physicalThread, hwInfo); + + EXPECT_EQ(numSlices, threads.size()); + + for (uint32_t i = 0; i < numSlices; i++) { + EXPECT_EQ(i, threads[i].slice); + EXPECT_EQ(0u, threads[i].subslice); + EXPECT_EQ(0u, threads[i].eu); + EXPECT_EQ(0u, threads[i].thread); + } +} + +} // namespace ult +} // namespace L0 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 new file mode 100644 index 0000000000..5735457be9 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/eu_thread_tests.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "test.h" + +#include "level_zero/tools/source/debug/eu_thread.h" + +namespace L0 { +namespace ult { + +TEST(EuThread, WhenConstructingEuThreadThenCorrectIdsAreSet) { + ze_device_thread_t devThread = {3, 4, 5, 6}; + + EuThread::ThreadId threadId(0, devThread); + + EXPECT_EQ(0u, threadId.tileIndex); + EXPECT_EQ(3u, threadId.slice); + EXPECT_EQ(4u, threadId.subslice); + EXPECT_EQ(5u, threadId.eu); + EXPECT_EQ(6u, threadId.thread); + + EuThread::ThreadId threadId2(3, 1, 2, 3, 4); + + EXPECT_EQ(3u, threadId2.tileIndex); + EXPECT_EQ(1u, threadId2.slice); + EXPECT_EQ(2u, threadId2.subslice); + EXPECT_EQ(3u, threadId2.eu); + EXPECT_EQ(4u, threadId2.thread); + + auto castValue = static_cast(threadId2); + + EXPECT_EQ(threadId2.packed, castValue); +} + +TEST(EuThread, GivenEuThreadWhenGettingThreadIdThenValidIdReturned) { + ze_device_thread_t devThread = {3, 4, 5, 6}; + EuThread::ThreadId threadId(0, devThread); + EuThread euThread(threadId); + + auto id = euThread.getThreadId(); + + EXPECT_EQ(threadId.packed, id.packed); +} + +TEST(EuThread, GivenEuThreadWhenChangingAndQueryingStatesThenStateIsChanged) { + ze_device_thread_t devThread = {3, 4, 5, 6}; + EuThread::ThreadId threadId(0, devThread); + EuThread euThread(threadId); + + EXPECT_FALSE(euThread.isStopped()); + EXPECT_TRUE(euThread.isRunning()); + + bool result = euThread.stopThread(); + + EXPECT_TRUE(result); + EXPECT_TRUE(euThread.isStopped()); + EXPECT_FALSE(euThread.isRunning()); + + result = euThread.stopThread(); + + EXPECT_FALSE(result); + EXPECT_TRUE(euThread.isStopped()); + EXPECT_FALSE(euThread.isRunning()); + + result = euThread.resumeThread(); + + EXPECT_TRUE(result); + EXPECT_FALSE(euThread.isStopped()); + EXPECT_TRUE(euThread.isRunning()); + + result = euThread.resumeThread(); + + EXPECT_FALSE(result); + EXPECT_FALSE(euThread.isStopped()); + EXPECT_TRUE(euThread.isRunning()); +} + +TEST(EuThread, GivenEuThreadWhenToStringCalledThenCorrectStringReturned) { + ze_device_thread_t devThread = {3, 4, 5, 6}; + EuThread::ThreadId threadId(0, devThread); + EuThread euThread(threadId); + + auto threadString = euThread.toString(); + EXPECT_EQ("device index = 0 slice = 3 subslice = 4 eu = 5 thread = 6", threadString); +} + +} // namespace ult +} // namespace L0 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 2071cf51e6..6cb318899b 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 @@ -24,6 +24,8 @@ class OsInterfaceWithDebugAttach : public NEO::OSInterface { }; struct DebugSessionMock : public L0::RootDebugSession { + using L0::RootDebugSession::getSingleThreads; + DebugSessionMock(const zet_debug_config_t &config, L0::Device *device) : RootDebugSession(config, device), config(config){}; bool closeConnection() override { return true; } ze_result_t initialize() override {