diff --git a/CMakeLists.txt b/CMakeLists.txt index e20b01040e..475bf981b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -511,15 +511,9 @@ if(UNIX) endif() message(STATUS "Xe drm detection: ${NEO_ENABLE_XE_DRM_DETECTION}") - # drm-xe debugger support - if(NOT DEFINED NEO_ENABLE_XE_DEBUGGER) - set(NEO_ENABLE_XE_DEBUGGER FALSE) - endif() - if(NEO_ENABLE_XE_DEBUGGER) - get_filename_component(XE_DEBUG_HEADERS_DIR "${NEO_SOURCE_DIR}/third_party${BRANCH_DIR_SUFFIX}uapi-eudebug" ABSOLUTE) - include_directories(BEFORE ${XE_DEBUG_HEADERS_DIR}) - endif() - message(STATUS "Xe Debugger enabled: ${NEO_ENABLE_XE_DEBUGGER}") + get_filename_component(THIRD_PARTY_DIR "${NEO_SOURCE_DIR}/third_party${BRANCH_DIR_SUFFIX}" ABSOLUTE) + include_directories(BEFORE ${THIRD_PARTY_DIR}) + endif() if(NOT DEFINED I915_HEADERS_DIR OR I915_HEADERS_DIR STREQUAL "") get_filename_component(I915_HEADERS_DIR "${NEO_SOURCE_DIR}/third_party${BRANCH_DIR_SUFFIX}uapi" ABSOLUTE) diff --git a/level_zero/core/source/dll/linux/CMakeLists.txt b/level_zero/core/source/dll/linux/CMakeLists.txt index d2080c8156..036d873c8c 100644 --- a/level_zero/core/source/dll/linux/CMakeLists.txt +++ b/level_zero/core/source/dll/linux/CMakeLists.txt @@ -5,10 +5,13 @@ # if(UNIX) - target_sources(${TARGET_NAME_L0} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt - ) + if(NEO_ENABLE_XE_DRM_DETECTION) + target_sources(${TARGET_NAME_L0} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${NEO_SOURCE_DIR}/level_zero/tools/source/debug/linux/xe/debug_session_xe_helper.cpp + ) + endif() if(NEO_ENABLE_i915_PRELIM_DETECTION) target_sources(${TARGET_NAME_L0} diff --git a/level_zero/tools/source/debug/linux/CMakeLists.txt b/level_zero/tools/source/debug/linux/CMakeLists.txt index 30f2c6a700..2cd43cfb6a 100644 --- a/level_zero/tools/source/debug/linux/CMakeLists.txt +++ b/level_zero/tools/source/debug/linux/CMakeLists.txt @@ -20,7 +20,7 @@ if(UNIX) ) endif() - if(NEO_ENABLE_XE_DEBUGGER) + if(NEO_ENABLE_XE_DRM_DETECTION) target_sources(${L0_STATIC_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/xe/debug_session.cpp diff --git a/level_zero/tools/source/debug/linux/debug_session.cpp b/level_zero/tools/source/debug/linux/debug_session.cpp index 94789a0134..9c05a5c4c4 100644 --- a/level_zero/tools/source/debug/linux/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/debug_session.cpp @@ -7,8 +7,10 @@ #include "level_zero/tools/source/debug/debug_session.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/tools/source/debug/linux/debug_session.h" @@ -32,10 +34,7 @@ DebugSession *DebugSession::create(const zet_debug_config_t &config, Device *dev } else { allocator = debugSessionLinuxFactory[DEBUG_SESSION_LINUX_TYPE_I915]; } - if (!allocator) { - result = ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; - return nullptr; - } + UNRECOVERABLE_IF(!allocator) auto debugSession = allocator(config, device, result, isRootAttach); if (result != ZE_RESULT_SUCCESS) { @@ -70,4 +69,51 @@ ze_result_t DebugSessionLinux::translateDebuggerOpenErrno(int error) { return result; } +bool DebugSessionLinux::closeFd() { + if (fd == 0) { + return false; + } + + auto ret = NEO::SysCalls::close(fd); + + if (ret != 0) { + PRINT_DEBUGGER_ERROR_LOG("Debug connection close() on fd: %d failed: retCode: %d\n", fd, ret); + return false; + } + fd = 0; + return true; +} + +void *DebugSessionLinux::readInternalEventsThreadFunction(void *arg) { + DebugSessionLinux *self = reinterpret_cast(arg); + PRINT_DEBUGGER_INFO_LOG("Debugger internal event thread started\n", ""); + self->internalThreadHasStarted = true; + + while (self->internalEventThread.threadActive) { + self->readInternalEventsAsync(); + } + + PRINT_DEBUGGER_INFO_LOG("Debugger internal event thread closing\n", ""); + + return nullptr; +} + +std::unique_ptr DebugSessionLinux::getInternalEvent() { + std::unique_ptr eventMemory; + + { + std::unique_lock lock(internalEventThreadMutex); + + if (internalEventQueue.empty()) { + NEO::waitOnCondition(internalEventCondition, lock, std::chrono::milliseconds(100)); + } + + if (!internalEventQueue.empty()) { + eventMemory = std::move(internalEventQueue.front()); + internalEventQueue.pop(); + } + } + return eventMemory; +} + } // 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 b839f5a2ab..b8c5f748f3 100644 --- a/level_zero/tools/source/debug/linux/debug_session.h +++ b/level_zero/tools/source/debug/linux/debug_session.h @@ -5,13 +5,73 @@ * */ +#pragma once + +#include "shared/source/os_interface/linux/sys_calls.h" + +#include "level_zero/core/source/device/device.h" +#include "level_zero/core/source/device/device_imp.h" #include "level_zero/tools/source/debug/debug_session.h" #include "level_zero/tools/source/debug/debug_session_imp.h" namespace L0 { struct DebugSessionLinux : DebugSessionImp { - DebugSessionLinux(const zet_debug_config_t &config, Device *device) : DebugSessionImp(config, device){}; + DebugSessionLinux(const zet_debug_config_t &config, Device *device, int fd) : DebugSessionImp(config, device), fd(fd){}; static ze_result_t translateDebuggerOpenErrno(int error); + bool closeFd(); + + int fd = 0; + std::atomic internalThreadHasStarted{false}; + static void *readInternalEventsThreadFunction(void *arg); + + MOCKABLE_VIRTUAL void startInternalEventsThread() { + internalEventThread.thread = NEO::Thread::create(readInternalEventsThreadFunction, reinterpret_cast(this)); + } + void closeInternalEventsThread() { + internalEventThread.close(); + } + + virtual void readInternalEventsAsync() = 0; + MOCKABLE_VIRTUAL std::unique_ptr getInternalEvent(); + MOCKABLE_VIRTUAL float getThreadStartLimitTime() { + return 0.5; + } + + ThreadHelper internalEventThread; + std::mutex internalEventThreadMutex; + std::condition_variable internalEventCondition; + std::queue> internalEventQueue; + constexpr static uint64_t invalidClientHandle = std::numeric_limits::max(); + constexpr static uint64_t invalidHandle = std::numeric_limits::max(); + uint64_t clientHandle = invalidClientHandle; + uint64_t clientHandleClosed = invalidClientHandle; + static constexpr size_t maxEventSize = 4096; + + struct IoctlHandler { + virtual ~IoctlHandler() = default; + virtual int ioctl(int fd, unsigned long request, void *arg) { + return 0; + }; + MOCKABLE_VIRTUAL int poll(pollfd *pollFd, unsigned long int numberOfFds, int timeout) { + return NEO::SysCalls::poll(pollFd, numberOfFds, timeout); + } + + MOCKABLE_VIRTUAL int64_t pread(int fd, void *buf, size_t count, off_t offset) { + return NEO::SysCalls::pread(fd, buf, count, offset); + } + + MOCKABLE_VIRTUAL int64_t pwrite(int fd, const void *buf, size_t count, off_t offset) { + return NEO::SysCalls::pwrite(fd, buf, count, offset); + } + + MOCKABLE_VIRTUAL void *mmap(void *addr, size_t size, int prot, int flags, int fd, off_t off) { + return NEO::SysCalls::mmap(addr, size, prot, flags, fd, off); + } + + MOCKABLE_VIRTUAL int munmap(void *addr, size_t size) { + return NEO::SysCalls::munmap(addr, size); + } + }; }; } // 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 4705f090a4..24a134cdc2 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.cpp @@ -41,8 +41,8 @@ static DebugSessionLinuxPopulateFactoryi915DebuggerVersion = reinterpret_cast(params)->version; @@ -318,20 +318,6 @@ void *DebugSessionLinuxi915::asyncThreadFunction(void *arg) { return nullptr; } -void *DebugSessionLinuxi915::readInternalEventsThreadFunction(void *arg) { - DebugSessionLinuxi915 *self = reinterpret_cast(arg); - PRINT_DEBUGGER_INFO_LOG("Debugger internal event thread started\n", ""); - self->internalThreadHasStarted = true; - - while (self->internalEventThread.threadActive) { - self->readInternalEventsAsync(); - } - - PRINT_DEBUGGER_INFO_LOG("Debugger internal event thread closing\n", ""); - - return nullptr; -} - void DebugSessionLinuxi915::startAsyncThread() { asyncThread.thread = NEO::Thread::create(asyncThreadFunction, reinterpret_cast(this)); } @@ -341,39 +327,6 @@ void DebugSessionLinuxi915::closeAsyncThread() { internalEventThread.close(); } -bool DebugSessionLinuxi915::closeFd() { - if (fd == 0) { - return false; - } - - auto ret = NEO::SysCalls::close(fd); - - if (ret != 0) { - PRINT_DEBUGGER_ERROR_LOG("Debug connection close() on fd: %d failed: retCode: %d\n", fd, ret); - return false; - } - fd = 0; - return true; -} - -std::unique_ptr DebugSessionLinuxi915::getInternalEvent() { - std::unique_ptr eventMemory; - - { - std::unique_lock lock(internalEventThreadMutex); - - if (internalEventQueue.empty()) { - NEO::waitOnCondition(internalEventCondition, lock, std::chrono::milliseconds(100)); - } - - if (!internalEventQueue.empty()) { - eventMemory = std::move(internalEventQueue.front()); - internalEventQueue.pop(); - } - } - return eventMemory; -} - void DebugSessionLinuxi915::handleEventsAsync() { auto eventMemory = getInternalEvent(); if (eventMemory != nullptr) { 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 646ab598b2..7d801e1eb1 100644 --- a/level_zero/tools/source/debug/linux/prelim/debug_session.h +++ b/level_zero/tools/source/debug/linux/prelim/debug_session.h @@ -46,9 +46,8 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { ze_result_t writeMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, const void *buffer) override; ze_result_t acknowledgeEvent(const zet_debug_event_t *event) override; - struct IoctlHandler { - MOCKABLE_VIRTUAL ~IoctlHandler() = default; - MOCKABLE_VIRTUAL int ioctl(int fd, unsigned long request, void *arg) { + struct IoctlHandleri915 : DebugSessionLinux::IoctlHandler { + int ioctl(int fd, unsigned long request, void *arg) override { int ret = 0; int error = 0; bool shouldRetryIoctl = false; @@ -67,28 +66,9 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { } while (shouldRetryIoctl); return ret; } - - MOCKABLE_VIRTUAL int poll(pollfd *pollFd, unsigned long int numberOfFds, int timeout) { - return NEO::SysCalls::poll(pollFd, numberOfFds, timeout); - } - - MOCKABLE_VIRTUAL int64_t pread(int fd, void *buf, size_t count, off_t offset) { - return NEO::SysCalls::pread(fd, buf, count, offset); - } - - MOCKABLE_VIRTUAL int64_t pwrite(int fd, const void *buf, size_t count, off_t offset) { - return NEO::SysCalls::pwrite(fd, buf, count, offset); - } - - MOCKABLE_VIRTUAL void *mmap(void *addr, size_t size, int prot, int flags, int fd, off_t off) { - return NEO::SysCalls::mmap(addr, size, prot, flags, fd, off); - } - - MOCKABLE_VIRTUAL int munmap(void *addr, size_t size) { - return NEO::SysCalls::munmap(addr, size); - } }; - static constexpr size_t maxEventSize = 4096; + + std::unique_ptr ioctlHandler; using ContextHandle = uint64_t; @@ -172,9 +152,6 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { std::unordered_map uuidToModule; }; - constexpr static uint64_t invalidClientHandle = std::numeric_limits::max(); - constexpr static uint64_t invalidHandle = std::numeric_limits::max(); - protected: enum class ThreadControlCmd { interrupt, @@ -185,6 +162,7 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { 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; @@ -216,19 +194,9 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { MOCKABLE_VIRTUAL TileDebugSessionLinuxi915 *createTileSession(const zet_debug_config_t &config, Device *device, DebugSessionImp *rootDebugSession); static void *asyncThreadFunction(void *arg); - static void *readInternalEventsThreadFunction(void *arg); void startAsyncThread() override; void closeAsyncThread(); - MOCKABLE_VIRTUAL void startInternalEventsThread() { - internalEventThread.thread = NEO::Thread::create(readInternalEventsThreadFunction, reinterpret_cast(this)); - } - void closeInternalEventsThread() { - internalEventThread.close(); - } - - bool closeFd(); - virtual std::vector getAllMemoryHandles() { std::vector allVms; std::unique_lock memLock(asyncThreadMutex); @@ -240,11 +208,6 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { } void handleEventsAsync(); - void readInternalEventsAsync(); - MOCKABLE_VIRTUAL std::unique_ptr getInternalEvent(); - MOCKABLE_VIRTUAL float getThreadStartLimitTime() { - return 0.5; - } uint64_t getVmHandleFromClientAndlrcHandle(uint64_t clientHandle, uint64_t lrcHandle); bool handleVmBindEvent(prelim_drm_i915_debug_event_vm_bind *vmBind); @@ -354,26 +317,17 @@ struct DebugSessionLinuxi915 : DebugSessionLinux { return allInstancesRemoved; } - ThreadHelper internalEventThread; - std::mutex internalEventThreadMutex; - std::condition_variable internalEventCondition; - std::queue> internalEventQueue; std::vector> eventsToAck; // debug event, uuid handle to module std::vector> pendingVmBindEvents; - int fd = 0; uint32_t i915DebuggerVersion = 0; virtual int ioctl(unsigned long request, void *arg); - std::unique_ptr ioctlHandler; std::atomic detached{false}; - uint64_t clientHandle = invalidClientHandle; - uint64_t clientHandleClosed = invalidClientHandle; std::unordered_map uuidL0CommandQueueHandleToDevice; uint64_t euControlInterruptSeqno[NEO::EngineLimits::maxHandleCount]; + void readInternalEventsAsync() override; - std::unordered_map> clientHandleToConnection; - std::atomic internalThreadHasStarted{false}; bool blockOnFenceMode = false; // false - blocking VM_BIND on CPU - autoack events until last blocking event // true - blocking on fence - do not auto-ack events }; diff --git a/level_zero/tools/source/debug/linux/xe/debug_session.cpp b/level_zero/tools/source/debug/linux/xe/debug_session.cpp index db3938a696..95c8db83ee 100644 --- a/level_zero/tools/source/debug/linux/xe/debug_session.cpp +++ b/level_zero/tools/source/debug/linux/xe/debug_session.cpp @@ -8,22 +8,155 @@ #include "level_zero/tools/source/debug/linux/xe/debug_session.h" #include "shared/source/debug_settings/debug_settings_manager.h" +#include "shared/source/os_interface/linux/drm_debug.h" +#include "shared/source/os_interface/linux/xe/ioctl_helper_xe.h" #include "level_zero/tools/source/debug/debug_session.h" #include "level_zero/tools/source/debug/linux/drm_helper.h" -#include "drm/xe_drm.h" -#include "drm/xe_drm_tmp.h" - namespace L0 { static DebugSessionLinuxPopulateFactory populateXeDebugger; +DebugSession *createDebugSessionHelperXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params); + +DebugSessionLinuxXe::DebugSessionLinuxXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params) : DebugSessionLinux(config, device, debugFd) { + ioctlHandler.reset(new IoctlHandlerXe); + + if (params) { + this->xeDebuggerVersion = reinterpret_cast(params)->version; + } +}; +DebugSessionLinuxXe::~DebugSessionLinuxXe() { + + // closeAsyncThread(); + closeInternalEventsThread(); + closeFd(); +} + DebugSession *DebugSessionLinuxXe::createLinuxSession(const zet_debug_config_t &config, Device *device, ze_result_t &result, bool isRootAttach) { - result = ZE_RESULT_ERROR_UNSUPPORTED_FEATURE; + struct drm_xe_eudebug_connect open = { + .extensions = 0, + .pid = config.pid, + .flags = 0, + .version = 0}; + auto debugFd = DrmHelper::ioctl(device, NEO::DrmIoctl::debuggerOpen, &open); + if (debugFd >= 0) { + PRINT_DEBUGGER_INFO_LOG("drm_xe_eudebug_connect: open.pid: %d, debugFd: %d\n", + open.pid, debugFd); + + return createDebugSessionHelperXe(config, device, debugFd, &open); + } else { + auto reason = DrmHelper::getErrno(device); + PRINT_DEBUGGER_ERROR_LOG("drm_xe_eudebug_connect failed: open.pid: %d, retCode: %d, errno: %d\n", + open.pid, debugFd, reason); + result = translateDebuggerOpenErrno(reason); + } return nullptr; } +ze_result_t DebugSessionLinuxXe::initialize() { + + struct pollfd pollFd = { + .fd = this->fd, + .events = POLLIN, + .revents = 0, + }; + + auto numberOfFds = ioctlHandler->poll(&pollFd, 1, 1000); + PRINT_DEBUGGER_INFO_LOG("initialization poll() retCode: %d\n", numberOfFds); + + if (numberOfFds <= 0) { + return ZE_RESULT_NOT_READY; + } + + bool isRootDevice = !connectedDevice->getNEODevice()->isSubDevice(); + UNRECOVERABLE_IF(!isRootDevice); + + createEuThreads(); + tileSessionsEnabled = false; + + startInternalEventsThread(); + + return ZE_RESULT_SUCCESS; +} + +void DebugSessionLinuxXe::readInternalEventsAsync() { + + struct pollfd pollFd = { + .fd = fd, + .events = POLLIN, + .revents = 0, + }; + + int pollTimeout = 1000; + auto numberOfFds = ioctlHandler->poll(&pollFd, 1, pollTimeout); + + PRINT_DEBUGGER_INFO_LOG("Debugger async thread readEvent poll() retCode: %d\n", numberOfFds); + + if (!detached && numberOfFds < 0 && errno == EINVAL) { + zet_debug_event_t debugEvent = {}; + debugEvent.type = ZET_DEBUG_EVENT_TYPE_DETACHED; + debugEvent.info.detached.reason = ZET_DEBUG_DETACH_REASON_INVALID; + PRINT_DEBUGGER_INFO_LOG("Debugger detached\n", ""); + pushApiEvent(debugEvent); + detached = true; + } else if (numberOfFds > 0) { + + ze_result_t result = ZE_RESULT_SUCCESS; + + int maxLoopCount = 3; + do { + + uint8_t maxEventBuffer[sizeof(drm_xe_eudebug_event) + maxEventSize]; + auto event = reinterpret_cast(maxEventBuffer); + event->len = maxEventSize; + event->type = DRM_XE_EUDEBUG_EVENT_READ; + event->flags = 0; + + result = readEventImp(event); + maxLoopCount--; + + if (result == ZE_RESULT_SUCCESS) { + std::lock_guard lock(internalEventThreadMutex); + + auto memory = std::make_unique(maxEventSize / sizeof(uint64_t)); + memcpy(memory.get(), event, maxEventSize); + + internalEventQueue.push(std::move(memory)); + internalEventCondition.notify_one(); + } + } while (result == ZE_RESULT_SUCCESS && maxLoopCount > 0); + } +} + +int DebugSessionLinuxXe::ioctl(unsigned long request, void *arg) { + return ioctlHandler->ioctl(fd, request, arg); +} + +ze_result_t DebugSessionLinuxXe::readEventImp(drm_xe_eudebug_event *drmDebugEvent) { + auto ret = ioctl(DRM_XE_EUDEBUG_IOCTL_READ_EVENT, drmDebugEvent); + if (ret != 0) { + PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT failed: retCode: %d errno = %d\n", ret, errno); + return ZE_RESULT_NOT_READY; + } else if (drmDebugEvent->flags & ~static_cast(DRM_XE_EUDEBUG_EVENT_CREATE | DRM_XE_EUDEBUG_EVENT_DESTROY | DRM_XE_EUDEBUG_EVENT_STATE_CHANGE)) { + PRINT_DEBUGGER_ERROR_LOG("DRM_XE_EUDEBUG_IOCTL_READ_EVENT unsupported flag = %d\n", (int)drmDebugEvent->flags); + return ZE_RESULT_ERROR_UNKNOWN; + } + return ZE_RESULT_SUCCESS; +} + +void DebugSessionLinuxXe::pushApiEvent(zet_debug_event_t &debugEvent) { + std::unique_lock lock(asyncThreadMutex); + apiEvents.push(debugEvent); + apiEventCondition.notify_all(); +} + +bool DebugSessionLinuxXe::closeConnection() { + closeInternalEventsThread(); + return closeFd(); +} + } // namespace L0 \ No newline at end of file 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 f7cff19a24..aa2ff67928 100644 --- a/level_zero/tools/source/debug/linux/xe/debug_session.h +++ b/level_zero/tools/source/debug/linux/xe/debug_session.h @@ -5,16 +5,152 @@ * */ +#pragma once + #include "level_zero/tools/source/debug/debug_session.h" #include "level_zero/tools/source/debug/debug_session_imp.h" #include "level_zero/tools/source/debug/linux/debug_session.h" #include "level_zero/tools/source/debug/linux/debug_session_factory.h" +#include "uapi-eudebug/drm/xe_drm.h" +#include "uapi-eudebug/drm/xe_drm_tmp.h" + namespace L0 { struct DebugSessionLinuxXe : DebugSessionLinux { + ~DebugSessionLinuxXe() override; + DebugSessionLinuxXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params); static DebugSession *createLinuxSession(const zet_debug_config_t &config, Device *device, ze_result_t &result, bool isRootAttach); + + ze_result_t initialize() override; + + bool closeConnection() override; + + ze_result_t readMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, void *buffer) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + ze_result_t writeMemory(ze_device_thread_t thread, const zet_debug_memory_space_desc_t *desc, size_t size, const void *buffer) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + ze_result_t acknowledgeEvent(const zet_debug_event_t *event) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + void startAsyncThread() override { + } + + bool readModuleDebugArea() override { + UNRECOVERABLE_IF(true); + return true; + } + + ze_result_t readSbaBuffer(EuThread::ThreadId threadId, NEO::SbaTrackedAddresses &sbaBuffer) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + void cleanRootSessionAfterDetach(uint32_t deviceIndex) override { + UNRECOVERABLE_IF(true); + } + + ze_result_t resumeImp(const std::vector &threads, uint32_t deviceIndex) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + ze_result_t interruptImp(uint32_t deviceIndex) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + ze_result_t readGpuMemory(uint64_t memoryHandle, char *output, size_t size, uint64_t gpuVa) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + ze_result_t writeGpuMemory(uint64_t memoryHandle, const char *input, size_t size, uint64_t gpuVa) override { + UNRECOVERABLE_IF(true); + return ZE_RESULT_SUCCESS; + } + + void enqueueApiEvent(zet_debug_event_t &debugEvent) override { + UNRECOVERABLE_IF(true); + } + + uint64_t getContextStateSaveAreaGpuVa(uint64_t memoryHandle) override { + UNRECOVERABLE_IF(true); + return 0; + } + + size_t getContextStateSaveAreaSize(uint64_t memoryHandle) override { + UNRECOVERABLE_IF(true); + return 0; + } + + void attachTile() override { + UNRECOVERABLE_IF(true); + } + void detachTile() override { + UNRECOVERABLE_IF(true); + } + + struct IoctlHandlerXe : DebugSessionLinux::IoctlHandler { + int ioctl(int fd, unsigned long request, void *arg) override { + int ret = 0; + int error = 0; + bool shouldRetryIoctl = false; + do { + shouldRetryIoctl = false; + ret = NEO::SysCalls::ioctl(fd, request, arg); + error = errno; + + if (ret == -1) { + shouldRetryIoctl = (error == EINTR || error == EAGAIN || error == EBUSY); + + if (request == DRM_XE_EUDEBUG_IOCTL_EU_CONTROL) { + shouldRetryIoctl = (error == EINTR || error == EAGAIN); + } + } + } while (shouldRetryIoctl); + return ret; + } + }; + + using ContextHandle = uint64_t; + + struct ContextParams { + ContextHandle handle = 0; + uint64_t vm = UINT64_MAX; + std::vector engines; + }; + + struct BindInfo { + uint64_t gpuVa = 0; + uint64_t size = 0; + }; + + struct ClientConnection { + drm_xe_eudebug_event_client client = {}; + std::unordered_map vmToModuleDebugAreaBindInfo; + }; + + bool checkAllEventsCollected(); + void handleEvent(drm_xe_eudebug_event *event); + void readInternalEventsAsync() override; + void pushApiEvent(zet_debug_event_t &debugEvent); + std::unique_ptr ioctlHandler; + uint32_t xeDebuggerVersion = 0; + std::unordered_map> clientHandleToConnection; + std::atomic detached{false}; + + ze_result_t readEventImp(drm_xe_eudebug_event *drmDebugEvent); + int ioctl(unsigned long request, void *arg); }; } // namespace L0 diff --git a/level_zero/tools/source/debug/linux/xe/debug_session_xe_helper.cpp b/level_zero/tools/source/debug/linux/xe/debug_session_xe_helper.cpp new file mode 100644 index 0000000000..3d4d48951b --- /dev/null +++ b/level_zero/tools/source/debug/linux/xe/debug_session_xe_helper.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/source/debug/linux/xe/debug_session.h" +#include +namespace L0 { + +DebugSession *createDebugSessionHelperXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params) { + return new DebugSessionLinuxXe(config, device, debugFd, params); +} + +} // namespace L0 diff --git a/level_zero/tools/test/unit_tests/sources/debug/debug_session_helper.cpp b/level_zero/tools/test/unit_tests/sources/debug/debug_session_helper.cpp index 8f8ae70a39..965b3f384f 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/debug_session_helper.cpp +++ b/level_zero/tools/test/unit_tests/sources/debug/debug_session_helper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021-2022 Intel Corporation + * Copyright (C) 2021-2023 Intel Corporation * * SPDX-License-Identifier: MIT * @@ -12,7 +12,8 @@ namespace L0 { namespace ult { CreateDebugSessionHelperFunc createDebugSessionFunc = nullptr; -} +CreateDebugSessionHelperFunc createDebugSessionFuncXe = nullptr; +} // namespace ult DebugSession *createDebugSessionHelper(const zet_debug_config_t &config, Device *device, int debugFd, void *params) { if (L0::ult::createDebugSessionFunc) { return L0::ult::createDebugSessionFunc(config, device, debugFd, params); @@ -20,4 +21,11 @@ DebugSession *createDebugSessionHelper(const zet_debug_config_t &config, Device return new L0::ult::DebugSessionMock(config, device); } +DebugSession *createDebugSessionHelperXe(const zet_debug_config_t &config, Device *device, int debugFd, void *params) { + if (L0::ult::createDebugSessionFuncXe) { + return L0::ult::createDebugSessionFuncXe(config, device, debugFd, params); + } + return new L0::ult::DebugSessionMock(config, device); +} + } // namespace L0 \ No newline at end of file diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/debug/linux/CMakeLists.txt index ef322fba78..847d2c6d6d 100644 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/CMakeLists.txt +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/CMakeLists.txt @@ -7,7 +7,7 @@ if(UNIX) target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt - ${CMAKE_CURRENT_SOURCE_DIR}/test_debug_session_linux_create.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_debug_session_linux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/debug_session_fixtures_linux.cpp ) endif() 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 954c5382a1..62ccd96a3e 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 @@ -34,7 +34,7 @@ namespace ult { using typeOfUUID = std::decay::type; -struct MockIoctlHandler : public L0::DebugSessionLinuxi915::IoctlHandler { +struct MockIoctlHandler : public L0::DebugSessionLinuxi915::IoctlHandleri915 { using EventPair = std::pair; using EventQueue = std::queue; 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 2b23ead353..ad28c0dc70 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 @@ -63,44 +63,6 @@ namespace L0 { namespace ult { extern CreateDebugSessionHelperFunc createDebugSessionFunc; - -TEST(IoctlHandler, GivenHandlerWhenPreadCalledThenSysCallIsCalled) { - L0::DebugSessionLinuxi915::IoctlHandler handler; - NEO::SysCalls::preadFuncCalled = 0; - auto retVal = handler.pread(0, nullptr, 0, 0); - - EXPECT_EQ(0, retVal); - EXPECT_EQ(1u, NEO::SysCalls::preadFuncCalled); - NEO::SysCalls::preadFuncCalled = 0; -} - -TEST(IoctlHandler, GivenHandlerWhenPwriteCalledThenSysCallIsCalled) { - L0::DebugSessionLinuxi915::IoctlHandler handler; - NEO::SysCalls::pwriteFuncCalled = 0; - auto retVal = handler.pwrite(0, nullptr, 0, 0); - - EXPECT_EQ(0, retVal); - EXPECT_EQ(1u, NEO::SysCalls::pwriteFuncCalled); - NEO::SysCalls::pwriteFuncCalled = 0; -} - -TEST(IoctlHandler, GivenHandlerWhenMmapAndMunmapCalledThenRedirectedToSysCall) { - L0::DebugSessionLinuxi915::IoctlHandler handler; - NEO::SysCalls::mmapFuncCalled = 0; - NEO::SysCalls::munmapFuncCalled = 0; - auto mappedPtr = handler.mmap(0, 0, 0, 0, 0, 0); - - EXPECT_EQ(0, mappedPtr); - - auto retVal = handler.munmap(0, 0); - EXPECT_EQ(0, retVal); - - EXPECT_EQ(1u, NEO::SysCalls::mmapFuncCalled); - EXPECT_EQ(1u, NEO::SysCalls::munmapFuncCalled); - NEO::SysCalls::mmapFuncCalled = 0; - NEO::SysCalls::munmapFuncCalled = 0; -} - TEST(IoctlHandler, GivenHandlerWhenEuControlIoctlFailsWithEBUSYThenIoctlIsNotCalledAgain) { VariableBackup mockIoctl(&SysCalls::sysCallsIoctl); VariableBackup mockErrno(&errno); @@ -117,7 +79,7 @@ TEST(IoctlHandler, GivenHandlerWhenEuControlIoctlFailsWithEBUSYThenIoctlIsNotCal return 0; }; - L0::DebugSessionLinuxi915::IoctlHandler handler; + L0::DebugSessionLinuxi915::IoctlHandleri915 handler; auto result = handler.ioctl(0, PRELIM_I915_DEBUG_IOCTL_EU_CONTROL, &ioctlCount); EXPECT_EQ(-1, result); @@ -144,7 +106,7 @@ TEST(IoctlHandler, GivenHandlerWhenEuControlIoctlFailsWithEAGAINOrEINTRThenIoctl return 0; }; - L0::DebugSessionLinuxi915::IoctlHandler handler; + L0::DebugSessionLinuxi915::IoctlHandleri915 handler; auto result = handler.ioctl(0, PRELIM_I915_DEBUG_IOCTL_EU_CONTROL, &ioctlCount); EXPECT_EQ(0, result); @@ -490,7 +452,6 @@ TEST_F(DebugApiLinuxTest, GivenDebuggerOpenVersion1AndSuccessfulInitializationWh mockDrm->context.debuggerOpenVersion = 1; mockDrm->baseErrno = false; mockDrm->errnoRetVal = 0; - auto session = std::unique_ptr(DebugSession::create(config, device, result, !device->getNEODevice()->isSubDevice())); EXPECT_NE(nullptr, session); diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_session_linux.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_session_linux.cpp new file mode 100644 index 0000000000..5a70a8f893 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_session_linux.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022-2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/test/common/helpers/variable_backup.h" +#include "shared/test/common/os_interface/linux/sys_calls_linux_ult.h" + +#include "level_zero/tools/source/debug/linux/debug_session.h" +#include "level_zero/tools/test/unit_tests/sources/debug/linux/debug_session_fixtures_linux.h" + +namespace NEO { +namespace SysCalls { +extern uint32_t closeFuncCalled; +extern int closeFuncArgPassed; +extern int closeFuncRetVal; +extern int setErrno; +extern uint32_t preadFuncCalled; +extern uint32_t pwriteFuncCalled; +extern uint32_t mmapFuncCalled; +extern uint32_t munmapFuncCalled; +} // namespace SysCalls +} // namespace NEO + +namespace L0 { +namespace ult { + +using DebugSessionLinuxCreateTest = Test; + +TEST(IoctlHandlerXe, GivenHandlerWhenPreadCalledThenSysCallIsCalled) { + L0::DebugSessionLinux::IoctlHandler handler; + NEO::SysCalls::preadFuncCalled = 0; + auto retVal = handler.pread(0, nullptr, 0, 0); + + EXPECT_EQ(0, retVal); + EXPECT_EQ(1u, NEO::SysCalls::preadFuncCalled); + NEO::SysCalls::preadFuncCalled = 0; +} + +TEST(IoctlHandlerXe, GivenHandlerWhenPwriteCalledThenSysCallIsCalled) { + L0::DebugSessionLinux::IoctlHandler handler; + NEO::SysCalls::pwriteFuncCalled = 0; + auto retVal = handler.pwrite(0, nullptr, 0, 0); + + EXPECT_EQ(0, retVal); + EXPECT_EQ(1u, NEO::SysCalls::pwriteFuncCalled); + NEO::SysCalls::pwriteFuncCalled = 0; +} + +TEST(IoctlHandlerXe, GivenHandlerWhenMmapAndMunmapCalledThenRedirectedToSysCall) { + L0::DebugSessionLinux::IoctlHandler handler; + NEO::SysCalls::mmapFuncCalled = 0; + NEO::SysCalls::munmapFuncCalled = 0; + auto mappedPtr = handler.mmap(0, 0, 0, 0, 0, 0); + + EXPECT_EQ(0, mappedPtr); + + auto retVal = handler.munmap(0, 0); + EXPECT_EQ(0, retVal); + + EXPECT_EQ(1u, NEO::SysCalls::mmapFuncCalled); + EXPECT_EQ(1u, NEO::SysCalls::munmapFuncCalled); + NEO::SysCalls::mmapFuncCalled = 0; + NEO::SysCalls::munmapFuncCalled = 0; +} + +} // namespace ult +} // namespace L0 diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_session_linux_create.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_session_linux_create.cpp deleted file mode 100644 index 5925bfd00f..0000000000 --- a/level_zero/tools/test/unit_tests/sources/debug/linux/test_debug_session_linux_create.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2022-2023 Intel Corporation - * - * SPDX-License-Identifier: MIT - * - */ - -#include "shared/test/common/helpers/variable_backup.h" -#include "shared/test/common/os_interface/linux/sys_calls_linux_ult.h" - -#include "level_zero/tools/source/debug/linux/debug_session.h" -#include "level_zero/tools/test/unit_tests/sources/debug/linux/debug_session_fixtures_linux.h" - -namespace L0 { -namespace ult { - -using DebugSessionLinuxCreateTest = Test; - -TEST_F(DebugSessionLinuxCreateTest, GivenDebuggerDriverTypeXeThenErrorReturned) { - - zet_debug_config_t config = {}; - config.pid = 0x1234; - mockDrm->setFileDescriptor(SysCalls::fakeFileDescriptor); - VariableBackup backup(&SysCalls::drmVersion); - SysCalls::drmVersion = "xe"; - ze_result_t result = ZE_RESULT_SUCCESS; - auto session = std::unique_ptr(DebugSession::create(config, device, result, false)); - - EXPECT_EQ(result, ZE_RESULT_ERROR_UNSUPPORTED_FEATURE); -} - -} // namespace ult -} // namespace L0 diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/CMakeLists.txt b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/CMakeLists.txt new file mode 100644 index 0000000000..6933e7057a --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/CMakeLists.txt @@ -0,0 +1,18 @@ +# +# Copyright (C) 2022-2023 Intel Corporation +# +# SPDX-License-Identifier: MIT +# + +if(UNIX) + if(NEO_ENABLE_XE_DRM_DETECTION) + target_sources(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + ${CMAKE_CURRENT_SOURCE_DIR}/test_debug_api_linux_xe.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/debug_session_fixtures_linux_xe.h + ${CMAKE_CURRENT_SOURCE_DIR}/debug_session_fixtures_linux_xe.cpp + ) + endif() + +endif() + diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_mock_drm_xe.h b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_mock_drm_xe.h new file mode 100644 index 0000000000..6f0e5c7d75 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_mock_drm_xe.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once + +#include "shared/source/helpers/common_types.h" +#include "shared/source/helpers/compiler_product_helper.h" +#include "shared/source/helpers/register_offsets.h" +#include "shared/source/os_interface/linux/engine_info.h" +#include "shared/source/os_interface/linux/ioctl_helper.h" +#include "shared/source/os_interface/linux/memory_info.h" +#include "shared/source/os_interface/linux/xe/ioctl_helper_xe.h" +#include "shared/source/os_interface/product_helper.h" +#include "shared/test/common/helpers/debug_manager_state_restore.h" +#include "shared/test/common/helpers/default_hw_info.h" +#include "shared/test/common/libult/linux/drm_mock.h" +#include "shared/test/common/mocks/linux/mock_os_time_linux.h" +#include "shared/test/common/mocks/mock_execution_environment.h" +#include "shared/test/common/os_interface/linux/sys_calls_linux_ult.h" +#include "shared/test/common/test_macros/test.h" + +#include "uapi-eudebug/drm/xe_drm.h" +#include "uapi-eudebug/drm/xe_drm_tmp.h" + +using namespace NEO; + +inline constexpr int testValueVmId = 0x5764; +inline constexpr int testValueMapOff = 0x7788; +inline constexpr int testValuePrime = 0x4321; +inline constexpr uint32_t testValueGemCreate = 0x8273; + +class DrmMockXeDebug : public DrmMockCustom { + public: + DrmMockXeDebug(RootDeviceEnvironment &rootDeviceEnvironment) : DrmMockCustom(rootDeviceEnvironment){}; + + int getErrno() override { + if (baseErrno) { + return Drm::getErrno(); + } + return errnoRetVal; + } + bool baseErrno = false; + int errnoRetVal = 0; + + int ioctl(DrmIoctl request, void *arg) override { + int ret = -1; + ioctlCalled = true; + if (forceIoctlAnswer) { + return setIoctlAnswer; + } + switch (request) { + + case DrmIoctl::debuggerOpen: { + auto debuggerOpen = reinterpret_cast(arg); + + if (debuggerOpen->version != 0) { + return -1; + } + if (debuggerOpenVersion != 0) { + debuggerOpen->version = debuggerOpenVersion; + } + return debuggerOpenRetval; + } break; + + default: + break; + } + return ret; + } + + void addMockedQueryTopologyData(uint16_t tileId, uint16_t maskType, uint32_t nBytes, const std::vector &mask) { + + ASSERT_EQ(nBytes, mask.size()); + + auto additionalSize = 8u + nBytes; + auto oldSize = queryTopology.size(); + auto newSize = oldSize + additionalSize; + queryTopology.resize(newSize, 0u); + + uint8_t *dataPtr = queryTopology.data() + oldSize; + + drm_xe_query_topology_mask *topo = reinterpret_cast(dataPtr); + topo->gt_id = tileId; + topo->type = maskType; + topo->num_bytes = nBytes; + + memcpy_s(reinterpret_cast(topo->mask), nBytes, mask.data(), nBytes); + } + + bool isDebugAttachAvailable() override { + if (allowDebugAttachCallBase) { + return Drm::isDebugAttachAvailable(); + } + return allowDebugAttach; + } + + bool allowDebugAttachCallBase = false; + bool allowDebugAttach = false; + bool ioctlCalled = false; + + int forceIoctlAnswer = 0; + int setIoctlAnswer = 0; + int gemVmBindReturn = 0; + + alignas(64) std::vector queryTopology; + + // Debugger ioctls + int debuggerOpenRetval = 10; // debugFd + uint32_t debuggerOpenVersion = 0; +}; diff --git a/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.cpp b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.cpp new file mode 100644 index 0000000000..61d8a4e94f --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h" + +#include "shared/test/common/helpers/variable_backup.h" +#include "shared/test/common/mocks/mock_device.h" +#include "shared/test/common/os_interface/linux/sys_calls_linux_ult.h" + +#include "level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_mock_drm_xe.h" + +namespace L0 { +namespace ult { + +void DebugApiLinuxXeFixture::setUp(NEO::HardwareInfo *hwInfo) { + if (hwInfo != nullptr) { + auto executionEnvironment = NEO::MockDevice::prepareExecutionEnvironment(hwInfo, 0u); + DeviceFixture::setupWithExecutionEnvironment(*executionEnvironment); + } else { + DeviceFixture::setUp(); + } + + mockDrm = new DrmMockXeDebug(*neoDevice->executionEnvironment->rootDeviceEnvironments[0]); + mockDrm->allowDebugAttach = true; + + auto &rootDeviceEnvironment = *neoDevice->executionEnvironment->rootDeviceEnvironments[0]; + auto gtSystemInfo = &rootDeviceEnvironment.getMutableHardwareInfo()->gtSystemInfo; + for (uint32_t slice = 0; slice < GT_MAX_SLICE; slice++) { + gtSystemInfo->SliceInfo[slice].Enabled = slice < gtSystemInfo->SliceCount; + } + + rootDeviceEnvironment.osInterface.reset(new NEO::OSInterface); + rootDeviceEnvironment.osInterface->setDriverModel(std::unique_ptr(mockDrm)); + + mockDrm->setFileDescriptor(SysCalls::fakeFileDescriptor); + SysCalls::drmVersion = "xe"; +} + +} // namespace ult +} // namespace L0 \ No newline at end of file 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 new file mode 100644 index 0000000000..aef827b175 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022-2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/os_interface/linux/drm_debug.h" +#include "shared/source/os_interface/os_interface.h" +#include "shared/test/common/helpers/debug_manager_state_restore.h" +#include "shared/test/common/helpers/gtest_helpers.h" +#include "shared/test/common/libult/linux/drm_mock_helper.h" +#include "shared/test/common/libult/linux/drm_query_mock.h" +#include "shared/test/common/mocks/mock_sip.h" +#include "shared/test/common/mocks/ult_device_factory.h" +#include "shared/test/common/test_macros/test.h" + +#include "level_zero/core/source/gfx_core_helpers/l0_gfx_core_helper.h" +#include "level_zero/core/test/unit_tests/fixtures/device_fixture.h" +#include "level_zero/core/test/unit_tests/mocks/mock_built_ins.h" +#include "level_zero/tools/source/debug/linux/xe/debug_session.h" +#include "level_zero/tools/test/unit_tests/sources/debug/debug_session_common.h" +#include "level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_mock_drm_xe.h" + +#include "common/StateSaveAreaHeader.h" +#include "uapi-eudebug/drm/xe_drm.h" +#include "uapi-eudebug/drm/xe_drm_tmp.h" + +#include +#include +#include +#include + +namespace L0 { +namespace ult { + +struct DebugApiLinuxXeFixture : public DeviceFixture { + void setUp() { + setUp(nullptr); + } + + void setUp(NEO::HardwareInfo *hwInfo); + + void tearDown() { + DeviceFixture::tearDown(); + } + DrmMockXeDebug *mockDrm = nullptr; + static constexpr uint8_t bufferSize = 16; +}; + +struct MockIoctlHandlerXe : public L0::DebugSessionLinuxXe::IoctlHandlerXe { + + using EventPair = std::pair; + using EventQueue = std::queue; + + int ioctl(int fd, unsigned long request, void *arg) override { + ioctlCalled++; + + if ((request == DRM_XE_EUDEBUG_IOCTL_READ_EVENT) && (arg != nullptr)) { + auto debugEvent = reinterpret_cast(arg); + debugEventInput = *debugEvent; + + if (!eventQueue.empty()) { + auto frontEvent = eventQueue.front(); + memcpy(arg, frontEvent.first, frontEvent.second); + eventQueue.pop(); + return 0; + } else { + debugEventRetVal = -1; + } + return debugEventRetVal; + } + + return ioctlRetVal; + } + + int poll(pollfd *pollFd, unsigned long int numberOfFds, int timeout) override { + passedTimeout = timeout; + pollCounter++; + + if (eventQueue.empty() && pollRetVal >= 0) { + return 0; + } + return pollRetVal; + } + + drm_xe_eudebug_event debugEventInput = {}; + + EventQueue eventQueue; + int ioctlRetVal = 0; + int debugEventRetVal = 0; + int ioctlCalled = 0; + std::atomic pollCounter = 0; + int pollRetVal = 0; + int passedTimeout = 0; +}; + +struct MockDebugSessionLinuxXe : public L0::DebugSessionLinuxXe { + using L0::DebugSessionImp::apiEvents; + using L0::DebugSessionLinuxXe::internalEventQueue; + using L0::DebugSessionLinuxXe::internalEventThread; + + MockDebugSessionLinuxXe(const zet_debug_config_t &config, L0::Device *device, int debugFd, void *params) : DebugSessionLinuxXe(config, device, debugFd, params) { + clientHandleToConnection[mockClientHandle].reset(new ClientConnection); + clientHandle = mockClientHandle; + createEuThreads(); + } + MockDebugSessionLinuxXe(const zet_debug_config_t &config, L0::Device *device, int debugFd) : MockDebugSessionLinuxXe(config, device, debugFd, nullptr) {} + + ze_result_t initialize() override { + if (initializeRetVal != ZE_RESULT_FORCE_UINT32) { + createEuThreads(); + clientHandle = mockClientHandle; + return initializeRetVal; + } + return DebugSessionLinuxXe::initialize(); + } + ze_result_t initializeRetVal = ZE_RESULT_FORCE_UINT32; + static constexpr uint64_t mockClientHandle = 1; +}; + +} // namespace ult +} // namespace L0 \ No newline at end of file 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 new file mode 100644 index 0000000000..ed26d4ca69 --- /dev/null +++ b/level_zero/tools/test/unit_tests/sources/debug/linux/xe/test_debug_api_linux_xe.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/gmm_helper/gmm_helper.h" +#include "shared/source/helpers/aligned_memory.h" +#include "shared/source/helpers/basic_math.h" +#include "shared/source/os_interface/linux/drm_debug.h" +#include "shared/source/os_interface/linux/engine_info.h" +#include "shared/source/os_interface/os_interface.h" +#include "shared/source/release_helper/release_helper.h" +#include "shared/test/common/helpers/debug_manager_state_restore.h" +#include "shared/test/common/helpers/gtest_helpers.h" +#include "shared/test/common/helpers/variable_backup.h" +#include "shared/test/common/libult/linux/drm_mock_helper.h" +#include "shared/test/common/libult/linux/drm_query_mock.h" +#include "shared/test/common/mocks/mock_device.h" +#include "shared/test/common/mocks/mock_sip.h" +#include "shared/test/common/mocks/ult_device_factory.h" +#include "shared/test/common/os_interface/linux/sys_calls_linux_ult.h" +#include "shared/test/common/test_macros/test.h" + +#include "level_zero/core/source/gfx_core_helpers/l0_gfx_core_helper.h" +#include "level_zero/core/test/unit_tests/fixtures/device_fixture.h" +#include "level_zero/core/test/unit_tests/mocks/mock_built_ins.h" +#include "level_zero/core/test/unit_tests/mocks/mock_device.h" +#include "level_zero/include/zet_intel_gpu_debug.h" +#include "level_zero/tools/source/debug/debug_handlers.h" +#include "level_zero/tools/source/debug/debug_session.h" +#include "level_zero/tools/source/debug/linux/debug_session.h" +#include "level_zero/tools/source/debug/linux/xe/debug_session.h" +#include "level_zero/tools/test/unit_tests/sources/debug/debug_session_common.h" +#include "level_zero/tools/test/unit_tests/sources/debug/linux/xe/debug_session_fixtures_linux_xe.h" +#include "level_zero/tools/test/unit_tests/sources/debug/mock_debug_session.h" + +#include "common/StateSaveAreaHeader.h" +#include "uapi-eudebug/drm/xe_drm.h" +#include "uapi-eudebug/drm/xe_drm_tmp.h" + +#include +#include +#include + +namespace NEO { +namespace SysCalls { +extern uint32_t closeFuncCalled; +extern int closeFuncArgPassed; +extern int closeFuncRetVal; +extern int setErrno; +extern uint32_t preadFuncCalled; +extern uint32_t pwriteFuncCalled; +extern uint32_t mmapFuncCalled; +extern uint32_t munmapFuncCalled; +} // namespace SysCalls +} // namespace NEO + +namespace L0 { +namespace ult { + +extern CreateDebugSessionHelperFunc createDebugSessionFuncXe; + +TEST(IoctlHandlerXe, GivenHandlerWhenEuControlIoctlFailsWithEBUSYThenIoctlIsNotCalledAgain) { + VariableBackup mockIoctl(&SysCalls::sysCallsIoctl); + VariableBackup mockErrno(&errno); + int ioctlCount = 0; + + SysCalls::sysCallsIoctl = [](int fileDescriptor, unsigned long int request, void *arg) -> int { + auto ioctlCount = reinterpret_cast(arg); + (*ioctlCount)++; + if (*ioctlCount < 2) { + errno = EBUSY; + return -1; + } + errno = 0; + return 0; + }; + + L0::DebugSessionLinuxXe::IoctlHandlerXe handler; + + auto result = handler.ioctl(0, DRM_XE_EUDEBUG_IOCTL_EU_CONTROL, &ioctlCount); + EXPECT_EQ(-1, result); + EXPECT_EQ(1, ioctlCount); +} + +TEST(IoctlHandlerXe, GivenHandlerWhenEuControlIoctlFailsWithEAGAINOrEINTRThenIoctlIsCalledAgain) { + VariableBackup mockIoctl(&SysCalls::sysCallsIoctl); + VariableBackup mockErrno(&errno); + int ioctlCount = 0; + + SysCalls::sysCallsIoctl = [](int fileDescriptor, unsigned long int request, void *arg) -> int { + auto ioctlCount = reinterpret_cast(arg); + (*ioctlCount)++; + if (*ioctlCount == 1) { + errno = EAGAIN; + return -1; + } + if (*ioctlCount == 2) { + errno = EINTR; + return -1; + } + errno = 0; + return 0; + }; + + L0::DebugSessionLinuxXe::IoctlHandlerXe handler; + + auto result = handler.ioctl(0, DRM_XE_EUDEBUG_IOCTL_EU_CONTROL, &ioctlCount); + EXPECT_EQ(0, result); + EXPECT_EQ(3, ioctlCount); +} + +TEST(IoctlHandler, GivenHandlerWhenIoctlFailsWithEBUSYThenIoctlIsAgain) { + VariableBackup mockIoctl(&SysCalls::sysCallsIoctl); + VariableBackup mockErrno(&errno); + int ioctlCount = 0; + + SysCalls::sysCallsIoctl = [](int fileDescriptor, unsigned long int request, void *arg) -> int { + auto ioctlCount = reinterpret_cast(arg); + (*ioctlCount)++; + if (*ioctlCount < 2) { + errno = EBUSY; + return -1; + } + errno = 0; + return 0; + }; + + L0::DebugSessionLinuxXe::IoctlHandlerXe handler; + + auto result = handler.ioctl(0, DRM_XE_EUDEBUG_IOCTL_READ_EVENT, &ioctlCount); + EXPECT_EQ(0, result); + EXPECT_EQ(2, ioctlCount); +} + +using DebugApiLinuxTestXe = Test; + +TEST_F(DebugApiLinuxTestXe, GivenDebugSessionWhenCallingPollThenDefaultHandlerRedirectsToSysCall) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + EXPECT_EQ(0, session->ioctlHandler->poll(nullptr, 0, 0)); +} + +TEST_F(DebugApiLinuxTestXe, givenDeviceWhenCallingDebugAttachThenSuccessAndValidSessionHandleAreReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + zet_debug_session_handle_t debugSession = nullptr; + + auto result = zetDebugAttach(device->toHandle(), &config, &debugSession); + + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_NE(nullptr, debugSession); +} + +TEST_F(DebugApiLinuxTestXe, givenDebugSessionWhenCallingDebugDetachThenSessionIsClosedAndSuccessReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + zet_debug_session_handle_t debugSession = nullptr; + + auto result = zetDebugAttach(device->toHandle(), &config, &debugSession); + + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_NE(nullptr, debugSession); + + result = zetDebugDetach(debugSession); + + EXPECT_EQ(ZE_RESULT_SUCCESS, result); +} + +TEST_F(DebugApiLinuxTestXe, GivenDebuggerCreatedWithParamsThenVersionIsSaved) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + ze_result_t result = ZE_RESULT_SUCCESS; + + VariableBackup mockCreateDebugSessionBackup(&L0::ult::createDebugSessionFuncXe, [](const zet_debug_config_t &config, L0::Device *device, int debugFd, void *params) -> DebugSession * { + auto session = new MockDebugSessionLinuxXe(config, device, debugFd, params); + session->initializeRetVal = ZE_RESULT_SUCCESS; + return session; + }); + + mockDrm->debuggerOpenRetval = 10; + mockDrm->debuggerOpenVersion = 1; + auto session = std::unique_ptr(DebugSession::create(config, device, result, !device->getNEODevice()->isSubDevice())); + + EXPECT_NE(nullptr, session); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + + MockDebugSessionLinuxXe *linuxSession = static_cast(session.get()); + EXPECT_EQ(linuxSession->xeDebuggerVersion, 1u); +} + +TEST_F(DebugApiLinuxTestXe, GivenDebugSessionWhenClosingConnectionThenSysCallCloseOnFdIsCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + + EXPECT_NE(nullptr, session); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncArgPassed = 0; + + auto ret = session->closeConnection(); + EXPECT_TRUE(ret); + + EXPECT_EQ(1u, NEO::SysCalls::closeFuncCalled); + EXPECT_EQ(10, NEO::SysCalls::closeFuncArgPassed); + EXPECT_FALSE(session->internalEventThread.threadActive); + + NEO::SysCalls::closeFuncCalled = 0; + NEO::SysCalls::closeFuncArgPassed = 0; +} + +TEST_F(DebugApiLinuxTestXe, GivenEventWithInvalidFlagsWhenReadingEventThenUnknownErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + drm_xe_eudebug_event_client clientInvalidFlag = {}; + clientInvalidFlag.base.type = DRM_XE_EUDEBUG_EVENT_OPEN; + clientInvalidFlag.base.flags = 0x8000; + clientInvalidFlag.base.len = sizeof(drm_xe_eudebug_event_client); + clientInvalidFlag.client_handle = 1; + + auto handler = new MockIoctlHandlerXe; + handler->eventQueue.push({reinterpret_cast(&clientInvalidFlag), static_cast(clientInvalidFlag.base.len)}); + handler->pollRetVal = 1; + auto eventsCount = handler->eventQueue.size(); + + session->ioctlHandler.reset(handler); + + auto memory = std::make_unique(MockDebugSessionLinuxXe::maxEventSize / sizeof(uint64_t)); + drm_xe_eudebug_event *event = reinterpret_cast(memory.get()); + event->type = DRM_XE_EUDEBUG_EVENT_READ; + event->flags = 0x8000; + event->len = MockDebugSessionLinuxXe::maxEventSize; + + ze_result_t result = session->readEventImp(event); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); + EXPECT_EQ(eventsCount, static_cast(handler->ioctlCalled)); +} + +TEST_F(DebugApiLinuxTestXe, WhenOpenDebuggerFailsThenCorrectErrorIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->debuggerOpenRetval = -1; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = EBUSY; + auto session = DebugSession::create(config, device, result, !device->getNEODevice()->isSubDevice()); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_NOT_AVAILABLE, result); + + mockDrm->errnoRetVal = ENODEV; + session = DebugSession::create(config, device, result, !device->getNEODevice()->isSubDevice()); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_UNSUPPORTED_FEATURE, result); + + mockDrm->errnoRetVal = EACCES; + session = DebugSession::create(config, device, result, !device->getNEODevice()->isSubDevice()); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS, result); + + mockDrm->errnoRetVal = ESRCH; + session = DebugSession::create(config, device, result, !device->getNEODevice()->isSubDevice()); + + EXPECT_EQ(nullptr, session); + EXPECT_EQ(ZE_RESULT_ERROR_UNKNOWN, result); +} + +TEST_F(DebugApiLinuxTestXe, GivenDebugSessionWhenPollReturnsZeroThenNotReadyIsReturned) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + ze_result_t result = ZE_RESULT_SUCCESS; + + mockDrm->debuggerOpenRetval = 10; + mockDrm->baseErrno = false; + mockDrm->errnoRetVal = 0; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandlerXe; + session->ioctlHandler.reset(handler); + + result = session->initialize(); + EXPECT_EQ(ZE_RESULT_NOT_READY, result); +} + +TEST_F(DebugApiLinuxTestXe, GivenDebugSessionInitializedThenInternalEventsThreadStarted) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandlerXe; + session->ioctlHandler.reset(handler); + + drm_xe_eudebug_event_client client = {}; + client.base.type = DRM_XE_EUDEBUG_EVENT_OPEN; + client.base.flags = 0; + client.base.len = sizeof(drm_xe_eudebug_event_client); + client.client_handle = MockDebugSessionLinuxXe::mockClientHandle; + + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.len)}); + + handler->pollRetVal = 1; + + ze_result_t result = session->initialize(); + EXPECT_EQ(ZE_RESULT_SUCCESS, result); + EXPECT_TRUE(session->internalEventThread.threadActive); +} + +TEST_F(DebugApiLinuxTestXe, GivenPollReturnsErrorAndEinvalWhenReadingInternalEventsAsyncThenDetachEventIsGenerated) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandlerXe; + handler->pollRetVal = -1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinuxXe::mockClientHandle; + + errno = EINVAL; + session->readInternalEventsAsync(); + + EXPECT_EQ(1u, session->apiEvents.size()); + EXPECT_EQ(ZET_DEBUG_EVENT_TYPE_DETACHED, session->apiEvents.front().type); + session->apiEvents.pop(); + errno = 0; + + session->readInternalEventsAsync(); + EXPECT_EQ(0u, session->apiEvents.size()); +} + +TEST_F(DebugApiLinuxTestXe, GivenPollReturnsNonZeroWhenReadingInternalEventsAsyncThenEventReadIsCalled) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandlerXe; + handler->pollRetVal = 1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinuxXe::mockClientHandle; + // session->synchronousInternalEventRead = true; + + uint64_t clientHandle = 2; + drm_xe_eudebug_event_client client = {}; + client.base.type = DRM_XE_EUDEBUG_EVENT_OPEN; + client.base.flags = 0; + client.base.len = sizeof(drm_xe_eudebug_event_client); + client.client_handle = clientHandle; + + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.len)}); + + session->readInternalEventsAsync(); + + constexpr int clientEventCount = 1; + constexpr int dummyReadEventCount = 1; + + EXPECT_EQ(clientEventCount + dummyReadEventCount, handler->ioctlCalled); + EXPECT_EQ(DebugSessionLinuxXe::maxEventSize, handler->debugEventInput.len); + EXPECT_EQ(static_cast(DRM_XE_EUDEBUG_EVENT_READ), handler->debugEventInput.type); +} + +TEST_F(DebugApiLinuxTestXe, GivenMoreThan3EventsInQueueThenInternalEventsOnlyReads3) { + zet_debug_config_t config = {}; + config.pid = 0x1234; + + auto session = std::make_unique(config, device, 10); + ASSERT_NE(nullptr, session); + + auto handler = new MockIoctlHandlerXe; + handler->pollRetVal = 1; + session->ioctlHandler.reset(handler); + session->clientHandle = MockDebugSessionLinuxXe::mockClientHandle; + // session->synchronousInternalEventRead = true; + + uint64_t clientHandle = 2; + drm_xe_eudebug_event_client client = {}; + client.base.type = DRM_XE_EUDEBUG_EVENT_OPEN; + client.base.flags = 0; + client.base.len = sizeof(drm_xe_eudebug_event_client); + client.client_handle = clientHandle; + + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.len)}); + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.len)}); + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.len)}); + handler->eventQueue.push({reinterpret_cast(&client), static_cast(client.base.len)}); + + session->readInternalEventsAsync(); + + EXPECT_EQ(3, handler->ioctlCalled); + EXPECT_EQ(DebugSessionLinuxXe::maxEventSize, handler->debugEventInput.len); + EXPECT_EQ(static_cast(DRM_XE_EUDEBUG_EVENT_READ), handler->debugEventInput.type); +} + +} // namespace ult +} // namespace L0 \ No newline at end of file diff --git a/shared/source/os_interface/linux/xe/CMakeLists.txt b/shared/source/os_interface/linux/xe/CMakeLists.txt index c44df137ff..ee96866b47 100644 --- a/shared/source/os_interface/linux/xe/CMakeLists.txt +++ b/shared/source/os_interface/linux/xe/CMakeLists.txt @@ -9,6 +9,7 @@ if(NEO_ENABLE_XE_DRM_DETECTION) ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/drm_version_xe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe_debugger.cpp ${CMAKE_CURRENT_SOURCE_DIR}${BRANCH_DIR_SUFFIX}ioctl_helper_xe_string_value_getter.cpp ${CMAKE_CURRENT_SOURCE_DIR}${BRANCH_DIR_SUFFIX}ioctl_helper_xe_vm_export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe.h diff --git a/shared/source/os_interface/linux/xe/ioctl_helper_xe.h b/shared/source/os_interface/linux/xe/ioctl_helper_xe.h index 2208fce345..e03ddbbd5c 100644 --- a/shared/source/os_interface/linux/xe/ioctl_helper_xe.h +++ b/shared/source/os_interface/linux/xe/ioctl_helper_xe.h @@ -95,6 +95,7 @@ class IoctlHelperXe : public IoctlHelper { int setContextDebugFlag(uint32_t drmContextId) override; bool isDebugAttachAvailable() override; unsigned int getIoctlRequestValue(DrmIoctl ioctlRequest) const override; + unsigned int getIoctlRequestValueDebugger(DrmIoctl ioctlRequest) const; int getDrmParamValue(DrmParam drmParam) const override; int getDrmParamValueBase(DrmParam drmParam) const override; std::string getIoctlString(DrmIoctl ioctlRequest) const override; diff --git a/shared/source/os_interface/linux/xe/ioctl_helper_xe_debugger.cpp b/shared/source/os_interface/linux/xe/ioctl_helper_xe_debugger.cpp new file mode 100644 index 0000000000..4b86fe01c3 --- /dev/null +++ b/shared/source/os_interface/linux/xe/ioctl_helper_xe_debugger.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/os_interface/linux/xe/ioctl_helper_xe.h" + +#include "uapi-eudebug/drm/xe_drm.h" +#include "uapi-eudebug/drm/xe_drm_tmp.h" + +#define STRINGIFY_ME(X) return #X +#define RETURN_ME(X) return X + +namespace NEO { + +unsigned int IoctlHelperXe::getIoctlRequestValueDebugger(DrmIoctl ioctlRequest) const { + switch (ioctlRequest) { + case DrmIoctl::debuggerOpen: + RETURN_ME(DRM_IOCTL_XE_EUDEBUG_CONNECT); + + default: + UNRECOVERABLE_IF(true); + return 0; + } +} + +} // namespace NEO \ No newline at end of file diff --git a/shared/source/os_interface/linux/xe/ioctl_helper_xe_string_value_getter.cpp b/shared/source/os_interface/linux/xe/ioctl_helper_xe_string_value_getter.cpp index a766825ccb..0a0663a5d4 100644 --- a/shared/source/os_interface/linux/xe/ioctl_helper_xe_string_value_getter.cpp +++ b/shared/source/os_interface/linux/xe/ioctl_helper_xe_string_value_getter.cpp @@ -42,6 +42,8 @@ unsigned int IoctlHelperXe::getIoctlRequestValue(DrmIoctl ioctlRequest) const { RETURN_ME(DRM_IOCTL_PRIME_FD_TO_HANDLE); case DrmIoctl::primeHandleToFd: RETURN_ME(DRM_IOCTL_PRIME_HANDLE_TO_FD); + case DrmIoctl::debuggerOpen: + return getIoctlRequestValueDebugger(ioctlRequest); default: UNRECOVERABLE_IF(true); return 0; @@ -76,6 +78,8 @@ std::string IoctlHelperXe::getIoctlString(DrmIoctl ioctlRequest) const { STRINGIFY_ME(DRM_IOCTL_PRIME_FD_TO_HANDLE); case DrmIoctl::primeHandleToFd: STRINGIFY_ME(DRM_IOCTL_PRIME_HANDLE_TO_FD); + case DrmIoctl::debuggerOpen: + STRINGIFY_ME(DRM_IOCTL_XE_EUDEBUG_CONNECT); default: return "???"; } diff --git a/shared/test/common/os_interface/linux/device_command_stream_fixture.h b/shared/test/common/os_interface/linux/device_command_stream_fixture.h index 6ca0d84e48..912cde714c 100644 --- a/shared/test/common/os_interface/linux/device_command_stream_fixture.h +++ b/shared/test/common/os_interface/linux/device_command_stream_fixture.h @@ -96,6 +96,7 @@ class DrmMockCustom : public Drm { using Drm::ioctlHelper; using Drm::memoryInfo; using Drm::pageFaultSupported; + using Drm::queryTopology; using Drm::setupIoctlHelper; struct IoctlResExt { @@ -155,6 +156,10 @@ class DrmMockCustom : public Drm { int ioctl(DrmIoctl request, void *arg) override; + void setFileDescriptor(int fd) { + hwDeviceId = std::make_unique(fd, ""); + } + virtual int ioctlExtra(DrmIoctl request, void *arg) { return -1; } diff --git a/shared/test/unit_test/os_interface/linux/xe/CMakeLists.txt b/shared/test/unit_test/os_interface/linux/xe/CMakeLists.txt index 8c58f1f11a..9607675498 100644 --- a/shared/test/unit_test/os_interface/linux/xe/CMakeLists.txt +++ b/shared/test/unit_test/os_interface/linux/xe/CMakeLists.txt @@ -8,6 +8,7 @@ if(NEO_ENABLE_XE_DRM_DETECTION) set(NEO_CORE_OS_INTERFACE_TESTS_LINUX_XE ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe_debugger_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe_vm_export_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ioctl_helper_xe_tests.h ) diff --git a/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_debugger_tests.cpp b/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_debugger_tests.cpp new file mode 100644 index 0000000000..315bf52028 --- /dev/null +++ b/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_debugger_tests.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "shared/source/os_interface/linux/xe/ioctl_helper_xe.h" +#include "shared/test/common/helpers/debug_manager_state_restore.h" +#include "shared/test/common/helpers/default_hw_info.h" +#include "shared/test/common/libult/linux/drm_mock.h" +#include "shared/test/common/mocks/linux/mock_os_time_linux.h" +#include "shared/test/common/mocks/mock_execution_environment.h" +#include "shared/test/common/test_macros/test.h" + +#include "uapi-eudebug/drm/xe_drm.h" +#include "uapi-eudebug/drm/xe_drm_tmp.h" + +using namespace NEO; + +TEST(IoctlHelperXeTest, givenIoctlHelperXeWhenCallingGetIoctForDebuggerThenCorrectValueReturned) { + auto executionEnvironment = std::make_unique(); + DrmMock drm{*executionEnvironment->rootDeviceEnvironments[0]}; + auto xeIoctlHelper = std::make_unique(drm); + auto verifyIoctlRequestValue = [&xeIoctlHelper](auto value, DrmIoctl drmIoctl) { + EXPECT_EQ(xeIoctlHelper->getIoctlRequestValue(drmIoctl), static_cast(value)); + }; + auto verifyIoctlString = [&xeIoctlHelper](DrmIoctl drmIoctl, const char *string) { + EXPECT_STREQ(string, xeIoctlHelper->getIoctlString(drmIoctl).c_str()); + }; + + verifyIoctlString(DrmIoctl::debuggerOpen, "DRM_IOCTL_XE_EUDEBUG_CONNECT"); + + verifyIoctlRequestValue(DRM_IOCTL_XE_EUDEBUG_CONNECT, DrmIoctl::debuggerOpen); +} \ No newline at end of file diff --git a/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.cpp b/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.cpp index afc25a312c..3fde5d3ab1 100644 --- a/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.cpp +++ b/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.cpp @@ -385,8 +385,6 @@ TEST(IoctlHelperXeTest, givenIoctlHelperXeWhenCallingAnyMethodThenDummyValueIsRe verifyIoctlString(DrmIoctl::primeFdToHandle, "DRM_IOCTL_PRIME_FD_TO_HANDLE"); verifyIoctlString(DrmIoctl::primeHandleToFd, "DRM_IOCTL_PRIME_HANDLE_TO_FD"); - verifyIoctlString(DrmIoctl::debuggerOpen, "???"); - EXPECT_TRUE(xeIoctlHelper->completionFenceExtensionSupported(true)); EXPECT_EQ(static_cast(XE_NEO_VMCREATE_DISABLESCRATCH_FLAG | @@ -426,8 +424,6 @@ TEST(IoctlHelperXeTest, whenGettingIoctlRequestValueThenPropertValueIsReturned) verifyIoctlRequestValue(DRM_IOCTL_XE_EXEC_QUEUE_DESTROY, DrmIoctl::gemContextDestroy); verifyIoctlRequestValue(DRM_IOCTL_PRIME_FD_TO_HANDLE, DrmIoctl::primeFdToHandle); verifyIoctlRequestValue(DRM_IOCTL_PRIME_HANDLE_TO_FD, DrmIoctl::primeHandleToFd); - - EXPECT_THROW(xeIoctlHelper->getIoctlRequestValue(DrmIoctl::debuggerOpen), std::runtime_error); } TEST(IoctlHelperXeTest, verifyPublicFunctions) { diff --git a/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.h b/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.h index 217d05317b..2445df116a 100644 --- a/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.h +++ b/shared/test/unit_test/os_interface/linux/xe/ioctl_helper_xe_tests.h @@ -11,7 +11,6 @@ #include "shared/source/helpers/compiler_product_helper.h" #include "shared/source/helpers/register_offsets.h" #include "shared/source/os_interface/linux/engine_info.h" -#include "shared/source/os_interface/linux/i915_prelim.h" #include "shared/source/os_interface/linux/ioctl_helper.h" #include "shared/source/os_interface/linux/memory_info.h" #include "shared/source/os_interface/linux/xe/ioctl_helper_xe.h" diff --git a/third_party/uapi-eudebug/drm/xe_drm_tmp.h b/third_party/uapi-eudebug/drm/xe_drm_tmp.h index 906bb0ef2d..96c04aa2c6 100644 --- a/third_party/uapi-eudebug/drm/xe_drm_tmp.h +++ b/third_party/uapi-eudebug/drm/xe_drm_tmp.h @@ -23,6 +23,7 @@ extern "C" { #define DRM_XE_EUDEBUG_IOCTL_READ_EVENT _IO('j', 0x0) #define DRM_XE_EUDEBUG_IOCTL_EU_CONTROL _IOWR('j', 0x2, struct drm_xe_eudebug_eu_control) #define DRM_XE_EUDEBUG_IOCTL_VM_OPEN _IOW('j', 0x1, struct drm_xe_eudebug_vm_open) +#define DRM_XE_EUDEBUG_IOCTL_READ_METADATA _IOWR('j', 0x3, struct drm_xe_eudebug_read_metadata) /* XXX: Document events to match their internal counterparts when moved to xe_drm.h */ struct drm_xe_eudebug_event { @@ -36,12 +37,15 @@ struct drm_xe_eudebug_event { #define DRM_XE_EUDEBUG_EVENT_EXEC_QUEUE 4 #define DRM_XE_EUDEBUG_EVENT_EU_ATTENTION 5 #define DRM_XE_EUDEBUG_EVENT_VM_BIND 6 -#define DRM_XE_EUDEBUG_EVENT_MAX_EVENT DRM_XE_EUDEBUG_EVENT_VM_BIND +#define DRM_XE_EUDEBUG_EVENT_METADATA 7 +#define DRM_XE_EUDEBUG_EVENT_VM_SET_METADATA 8 +#define DRM_XE_EUDEBUG_EVENT_MAX_EVENT DRM_XE_EUDEBUG_EVENT_VM_SET_METADATA __u16 flags; -#define DRM_XE_EUDEBUG_EVENT_CREATE BIT(0) -#define DRM_XE_EUDEBUG_EVENT_DESTROY BIT(1) -#define DRM_XE_EUDEBUG_EVENT_STATE_CHANGE BIT(2) +#define DRM_XE_EUDEBUG_EVENT_CREATE (1 << 0) +#define DRM_XE_EUDEBUG_EVENT_DESTROY (1 << 1) +#define DRM_XE_EUDEBUG_EVENT_STATE_CHANGE (1 << 2) +#define DRM_XE_EUDEBUG_EVENT_NEED_ACK (1 << 3) __u64 seqno; __u64 reserved; @@ -88,18 +92,19 @@ struct drm_xe_eudebug_event_vm_bind { __u64 vm_handle; __u64 va_start; __u64 va_length; + + __u64 metadata_handle; + __u64 metadata_cookie; } __attribute__((packed)); struct drm_xe_eudebug_event_metadata { struct drm_xe_eudebug_event base; __u64 client_handle; - + __u64 metadata_handle; /* XXX: Refer to xe_drm.h for fields */ __u64 type; - __u64 user_addr; __u64 len; - __u64 id; } __attribute__((packed)); struct drm_xe_eudebug_event_vm_set_metadata { @@ -165,6 +170,15 @@ struct drm_xe_eudebug_vm_open { __u64 timeout_ns; }; +struct drm_xe_eudebug_read_metadata { + __u64 client_handle; + __u64 metadata_handle; + __u32 flags; + + __u64 ptr; + __u64 size; +}; + #if defined(__cplusplus) } #endif